From acf812654a7a8445a367df9798f5c10db4bf63c9 Mon Sep 17 00:00:00 2001 From: Christian Andersson Date: Mon, 26 Jan 2026 12:57:48 +0100 Subject: [PATCH] Apply deno fmt formatting, fix lint errors, and simplify README - Apply consistent formatting across the codebase using deno fmt - Fix require-await lint errors by removing unnecessary async keywords - Fix verbatim-module-syntax by using import type for type-only imports - Fix no-unused-vars by removing unused RepositoryAuth import - Add @std/assert to deno.json imports and use bare specifiers in tests - Simplify project structure section in README for human readability Co-Authored-By: Claude Opus 4.5 --- .mcp.json | 2 +- README.md | 55 ++--------------- deno.json | 3 +- deno.lock | 7 +++ src/config/loader.ts | 16 +++-- src/parsers/cargo.ts | 2 +- src/parsers/deno.ts | 6 +- src/parsers/docker.ts | 14 ++++- src/parsers/gradle-groovy.ts | 10 ++-- src/parsers/gradle-kotlin.ts | 2 +- src/parsers/index.ts | 11 +++- src/parsers/maven.ts | 3 +- src/parsers/npm.ts | 4 +- src/parsers/nuget.ts | 9 ++- src/parsers/parsers.test.ts | 87 +++++++++++++++++++++------ src/parsers/pypi.ts | 4 +- src/registries/cargo.ts | 35 ++++++----- src/registries/go.ts | 66 +++++++++++++-------- src/registries/jsr.ts | 63 +++++++++++++------- src/registries/maven.ts | 95 ++++++++++++++++++++---------- src/registries/npm.ts | 35 ++++++----- src/registries/nuget.ts | 73 +++++++++++++++-------- src/registries/pypi.ts | 37 +++++++----- src/tools/analyze-dependencies.ts | 35 +++++++---- src/tools/check-vulnerabilities.ts | 21 +++++-- src/tools/get-package-docs.ts | 51 +++++++++++----- src/tools/list-versions.ts | 2 +- src/tools/lookup-version.ts | 2 +- src/tools/types.ts | 2 +- src/utils/cache.ts | 2 +- src/utils/github.ts | 23 +++++--- src/utils/http.ts | 7 ++- src/utils/version.test.ts | 12 ++-- src/utils/version.ts | 6 +- src/utils/vulnerability.ts | 2 +- 35 files changed, 505 insertions(+), 299 deletions(-) diff --git a/.mcp.json b/.mcp.json index f9a3e0c..0defd66 100644 --- a/.mcp.json +++ b/.mcp.json @@ -12,4 +12,4 @@ "cwd": "." } } -} \ No newline at end of file +} diff --git a/README.md b/README.md index 177ca04..8cb66e6 100644 --- a/README.md +++ b/README.md @@ -562,55 +562,12 @@ deno task fmt ### Project Structure ``` -mcp-dependency-version/ -├── main.ts # MCP server entry point -├── deno.json # Deno configuration -├── Dockerfile # Docker container build -├── .mcp.json # MCP server registration -├── README.md # Documentation -├── src/ -│ ├── config/ # Configuration management -│ │ ├── types.ts # Config types and defaults -│ │ ├── loader.ts # Config file loading -│ │ └── index.ts # Module exports -│ ├── registries/ # Package registry clients -│ │ ├── types.ts # Shared types -│ │ ├── index.ts # Client factory -│ │ ├── npm.ts # npm registry -│ │ ├── maven.ts # Maven Central -│ │ ├── pypi.ts # PyPI -│ │ ├── cargo.ts # crates.io -│ │ ├── go.ts # Go proxy -│ │ ├── jsr.ts # JSR (jsr.io) -│ │ ├── nuget.ts # NuGet -│ │ └── docker.ts # Docker Hub -│ ├── parsers/ # Dependency file parsers -│ │ ├── types.ts # Parser types -│ │ ├── index.ts # Parser factory -│ │ ├── npm.ts # package.json -│ │ ├── pypi.ts # requirements.txt -│ │ ├── cargo.ts # Cargo.toml -│ │ ├── go.ts # go.mod -│ │ ├── maven.ts # pom.xml -│ │ ├── gradle-groovy.ts # build.gradle -│ │ ├── gradle-kotlin.ts # build.gradle.kts -│ │ ├── deno.ts # deno.json -│ │ ├── nuget.ts # *.csproj -│ │ └── docker.ts # Dockerfile, docker-compose.yml -│ ├── tools/ # MCP tool implementations -│ │ ├── types.ts # Tool input/output types -│ │ ├── lookup-version.ts # lookup_version tool -│ │ ├── list-versions.ts # list_versions tool -│ │ ├── check-vulnerabilities.ts # check_vulnerabilities tool -│ │ ├── analyze-dependencies.ts # analyze_dependencies tool -│ │ └── get-package-docs.ts # get_package_docs tool -│ └── utils/ -│ ├── version.ts # Semver utilities -│ ├── version.test.ts # Version tests -│ ├── http.ts # HTTP utilities with auth -│ ├── github.ts # GitHub API utilities -│ ├── cache.ts # TTL cache -│ └── vulnerability.ts # OSV vulnerability checking +src/ +├── config/ # Configuration loading +├── registries/ # Registry client implementations (npm, maven, pypi, etc.) +├── parsers/ # Dependency file parsers (package.json, pom.xml, etc.) +├── tools/ # MCP tool implementations +└── utils/ # Shared utilities (version parsing, caching, HTTP) ``` ## API Reference diff --git a/deno.json b/deno.json index 1869687..451d361 100644 --- a/deno.json +++ b/deno.json @@ -12,7 +12,8 @@ }, "imports": { "@modelcontextprotocol/sdk": "npm:@modelcontextprotocol/sdk@1.25.3", - "zod": "npm:zod@3.25.76" + "zod": "npm:zod@3.25.76", + "@std/assert": "jsr:@std/assert@1.0.17" }, "compilerOptions": { "strict": true, diff --git a/deno.lock b/deno.lock index dac06d2..fa8e4af 100644 --- a/deno.lock +++ b/deno.lock @@ -13,6 +13,12 @@ "jsr:@std/internal" ] }, + "@std/assert@1.0.17": { + "integrity": "df5ebfffe77c03b3fa1401e11c762cc8f603d51021c56c4d15a8c7ab45e90dbe", + "dependencies": [ + "jsr:@std/internal" + ] + }, "@std/internal@1.0.12": { "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" } @@ -527,6 +533,7 @@ }, "workspace": { "dependencies": [ + "jsr:@std/assert@1.0.17", "npm:@modelcontextprotocol/sdk@1.25.3", "npm:zod@3.25.76" ] diff --git a/src/config/loader.ts b/src/config/loader.ts index b8b2410..11f9c65 100644 --- a/src/config/loader.ts +++ b/src/config/loader.ts @@ -28,7 +28,7 @@ function expandPath(path: string): string { */ function getConfigPath(): string { return expandPath( - Deno.env.get("MCP_DEPENDENCY_VERSION_CONFIG") || DEFAULT_CONFIG_PATH + Deno.env.get("MCP_DEPENDENCY_VERSION_CONFIG") || DEFAULT_CONFIG_PATH, ); } @@ -68,7 +68,10 @@ export async function loadConfig(): Promise { userConfig = JSON.parse(content) as Partial; } catch (error) { if (!(error instanceof Deno.errors.NotFound)) { - console.error(`Warning: Failed to load config from ${configPath}:`, error); + console.error( + `Warning: Failed to load config from ${configPath}:`, + error, + ); } // Use defaults if no config file exists } @@ -94,7 +97,10 @@ export function loadConfigSync(): Config { userConfig = JSON.parse(content) as Partial; } catch (error) { if (!(error instanceof Deno.errors.NotFound)) { - console.error(`Warning: Failed to load config from ${configPath}:`, error); + console.error( + `Warning: Failed to load config from ${configPath}:`, + error, + ); } } @@ -109,7 +115,7 @@ export function loadConfigSync(): Config { */ export function getRepositoryConfig( registry: Registry, - repository?: string + repository?: string, ): RepositoryConfig { const config = loadConfigSync(); const repos = config.repositories[registry]; @@ -123,7 +129,7 @@ export function getRepositoryConfig( if (!repo) { const available = Object.keys(repos).join(", "); throw new Error( - `Repository '${repository}' not found for ${registry}. Available: ${available}` + `Repository '${repository}' not found for ${registry}. Available: ${available}`, ); } return repo; diff --git a/src/parsers/cargo.ts b/src/parsers/cargo.ts index 69635a8..cc6cdba 100644 --- a/src/parsers/cargo.ts +++ b/src/parsers/cargo.ts @@ -25,7 +25,7 @@ function parse(content: string): ParsedDependency[] { } const complexMatch = line.match( - /^([a-zA-Z0-9_-]+)\s*=\s*\{.*version\s*=\s*"([^"]+)"/ + /^([a-zA-Z0-9_-]+)\s*=\s*\{.*version\s*=\s*"([^"]+)"/, ); if (complexMatch) { deps.push({ name: complexMatch[1], version: complexMatch[2] }); diff --git a/src/parsers/deno.ts b/src/parsers/deno.ts index ee327ba..e1895e4 100644 --- a/src/parsers/deno.ts +++ b/src/parsers/deno.ts @@ -72,7 +72,7 @@ function stripJsonComments(content: string): string { * jsr:@oak/oak@17 -> { registry: "jsr", name: "@oak/oak", version: "17" } */ function parseImportSpecifier( - specifier: string + specifier: string, ): { registry: "jsr" | "npm"; name: string; version: string } | null { // Skip URL imports if (specifier.startsWith("http://") || specifier.startsWith("https://")) { @@ -129,7 +129,9 @@ function parse(content: string): ParsedDependency[] { // For npm: packages, prefix the name to indicate registry routing // For jsr: packages, use the name as-is - const name = parsed.registry === "npm" ? `npm:${parsed.name}` : parsed.name; + const name = parsed.registry === "npm" + ? `npm:${parsed.name}` + : parsed.name; deps.push({ name, diff --git a/src/parsers/docker.ts b/src/parsers/docker.ts index 2b3cdd3..857f75b 100644 --- a/src/parsers/docker.ts +++ b/src/parsers/docker.ts @@ -76,7 +76,7 @@ function parseDockerfile(content: string): ParsedDependency[] { // Match FROM statements // FROM [--platform=...] image[:tag|@digest] [AS name] const fromMatch = trimmed.match( - /^FROM\s+(?:--platform=[^\s]+\s+)?([^\s]+)(?:\s+AS\s+\w+)?$/i + /^FROM\s+(?:--platform=[^\s]+\s+)?([^\s]+)(?:\s+AS\s+\w+)?$/i, ); if (fromMatch) { @@ -84,7 +84,11 @@ function parseDockerfile(content: string): ParsedDependency[] { const parsed = parseImageReference(imageRef); if (parsed) { // Avoid duplicates - if (!deps.some((d) => d.name === parsed.name && d.version === parsed.version)) { + if ( + !deps.some((d) => + d.name === parsed.name && d.version === parsed.version + ) + ) { deps.push(parsed); } } @@ -119,7 +123,11 @@ function parseDockerCompose(content: string): ParsedDependency[] { const parsed = parseImageReference(imageRef); if (parsed) { // Avoid duplicates - if (!deps.some((d) => d.name === parsed.name && d.version === parsed.version)) { + if ( + !deps.some((d) => + d.name === parsed.name && d.version === parsed.version + ) + ) { deps.push(parsed); } } diff --git a/src/parsers/gradle-groovy.ts b/src/parsers/gradle-groovy.ts index c55d0b6..6c1ff5f 100644 --- a/src/parsers/gradle-groovy.ts +++ b/src/parsers/gradle-groovy.ts @@ -16,9 +16,9 @@ const configurations = [ "annotationProcessor", "kapt", "ksp", - "compile", // deprecated but still used - "testCompile", // deprecated but still used - "runtime", // deprecated but still used + "compile", // deprecated but still used + "testCompile", // deprecated but still used + "runtime", // deprecated but still used ]; /** @@ -32,7 +32,7 @@ function parse(content: string): ParsedDependency[] { // Handles both single and double quotes const stringNotationRegex = new RegExp( `(?:${configPattern})\\s*[("']([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]+):([^'"\\s:]+)[)'"]`, - "g" + "g", ); let match; @@ -47,7 +47,7 @@ function parse(content: string): ParsedDependency[] { // Pattern 2: Map notation - implementation group: 'com.example', name: 'lib', version: '1.0' const mapNotationRegex = new RegExp( `(?:${configPattern})\\s+group:\\s*['"]([^'"]+)['"]\\s*,\\s*name:\\s*['"]([^'"]+)['"]\\s*,\\s*version:\\s*['"]([^'"]+)['"]`, - "g" + "g", ); while ((match = mapNotationRegex.exec(content)) !== null) { diff --git a/src/parsers/gradle-kotlin.ts b/src/parsers/gradle-kotlin.ts index ae6a0f3..c465bbd 100644 --- a/src/parsers/gradle-kotlin.ts +++ b/src/parsers/gradle-kotlin.ts @@ -28,7 +28,7 @@ function parse(content: string): ParsedDependency[] { // Pattern: implementation("group:artifact:version") const funcNotationRegex = new RegExp( `(?:${configPattern})\\s*\\(\\s*["']([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]+):([^"'\\s:]+)["']\\s*\\)`, - "g" + "g", ); let match; diff --git a/src/parsers/index.ts b/src/parsers/index.ts index c276e4f..5d54ebd 100644 --- a/src/parsers/index.ts +++ b/src/parsers/index.ts @@ -69,8 +69,10 @@ function parseMavenDependencies(content: string): ParsedDependency[] { // Check for Groovy DSL (build.gradle patterns) // Groovy can use: implementation 'group:artifact:version' or implementation "..." - if (/(?:implementation|api|testImplementation|compile)\s+['"]/.test(content) || - /(?:implementation|api|testImplementation)\s+group:/.test(content)) { + if ( + /(?:implementation|api|testImplementation|compile)\s+['"]/.test(content) || + /(?:implementation|api|testImplementation)\s+group:/.test(content) + ) { return gradleGroovyParser.parse(content); } @@ -82,7 +84,10 @@ function parseMavenDependencies(content: string): ParsedDependency[] { * Parse dependencies from file content based on registry * For maven registry, auto-detects between pom.xml, build.gradle, and build.gradle.kts */ -export function parseDependencies(content: string, registry: Registry): ParsedDependency[] { +export function parseDependencies( + content: string, + registry: Registry, +): ParsedDependency[] { switch (registry) { case "npm": return npmParser.parse(content); diff --git a/src/parsers/maven.ts b/src/parsers/maven.ts index fdeeedb..edbef6d 100644 --- a/src/parsers/maven.ts +++ b/src/parsers/maven.ts @@ -11,7 +11,8 @@ function parse(content: string): ParsedDependency[] { const deps: ParsedDependency[] = []; // Match dependency blocks - handle various whitespace patterns - const depRegex = /[\s\S]*?([^<]+)<\/groupId>[\s\S]*?([^<]+)<\/artifactId>[\s\S]*?([^<]+)<\/version>[\s\S]*?<\/dependency>/g; + const depRegex = + /[\s\S]*?([^<]+)<\/groupId>[\s\S]*?([^<]+)<\/artifactId>[\s\S]*?([^<]+)<\/version>[\s\S]*?<\/dependency>/g; let match; while ((match = depRegex.exec(content)) !== null) { diff --git a/src/parsers/npm.ts b/src/parsers/npm.ts index 308ae54..e7e33d2 100644 --- a/src/parsers/npm.ts +++ b/src/parsers/npm.ts @@ -12,7 +12,9 @@ function parse(content: string): ParsedDependency[] { const pkg = JSON.parse(content); const deps: ParsedDependency[] = []; - for (const depType of ["dependencies", "devDependencies", "peerDependencies"]) { + for ( + const depType of ["dependencies", "devDependencies", "peerDependencies"] + ) { const section = pkg[depType]; if (section && typeof section === "object") { for (const [name, version] of Object.entries(section)) { diff --git a/src/parsers/nuget.ts b/src/parsers/nuget.ts index 048b9b9..bdf9261 100644 --- a/src/parsers/nuget.ts +++ b/src/parsers/nuget.ts @@ -20,16 +20,19 @@ function parse(content: string): ParsedDependency[] { // Match PackageReference with Version as attribute // - const attrRegex = /]*Include\s*=\s*["']([^"']+)["'][^>]*Version\s*=\s*["']([^"']+)["'][^>]*\/?>/gi; + const attrRegex = + /]*Include\s*=\s*["']([^"']+)["'][^>]*Version\s*=\s*["']([^"']+)["'][^>]*\/?>/gi; // Also match when Version comes before Include - const attrRegex2 = /]*Version\s*=\s*["']([^"']+)["'][^>]*Include\s*=\s*["']([^"']+)["'][^>]*\/?>/gi; + const attrRegex2 = + /]*Version\s*=\s*["']([^"']+)["'][^>]*Include\s*=\s*["']([^"']+)["'][^>]*\/?>/gi; // Match PackageReference with Version as child element // // 1.0.0 // - const elementRegex = /]*Include\s*=\s*["']([^"']+)["'][^>]*>[\s\S]*?([^<]+)<\/Version>[\s\S]*?<\/PackageReference>/gi; + const elementRegex = + /]*Include\s*=\s*["']([^"']+)["'][^>]*>[\s\S]*?([^<]+)<\/Version>[\s\S]*?<\/PackageReference>/gi; let match: RegExpExecArray | null; diff --git a/src/parsers/parsers.test.ts b/src/parsers/parsers.test.ts index 7a7fd18..468fbb8 100644 --- a/src/parsers/parsers.test.ts +++ b/src/parsers/parsers.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "jsr:@std/assert"; +import { assertEquals } from "@std/assert"; import { npmParser } from "./npm.ts"; import { pypiParser } from "./pypi.ts"; import { cargoParser } from "./cargo.ts"; @@ -134,7 +134,10 @@ require golang.org/x/text v0.14.0 `; const deps = goParser.parse(content); assertEquals(deps.length, 2); - assertEquals(deps[0], { name: "github.com/gin-gonic/gin", version: "v1.9.1" }); + assertEquals(deps[0], { + name: "github.com/gin-gonic/gin", + version: "v1.9.1", + }); assertEquals(deps[1], { name: "golang.org/x/text", version: "v0.14.0" }); }); @@ -151,7 +154,10 @@ require ( `; const deps = goParser.parse(content); assertEquals(deps.length, 2); - assertEquals(deps[0], { name: "github.com/gin-gonic/gin", version: "v1.9.1" }); + assertEquals(deps[0], { + name: "github.com/gin-gonic/gin", + version: "v1.9.1", + }); assertEquals(deps[1], { name: "golang.org/x/text", version: "v0.14.0" }); }); @@ -175,8 +181,14 @@ Deno.test("mavenParser - parses pom.xml dependencies", () => { `; const deps = mavenParser.parse(content); assertEquals(deps.length, 2); - assertEquals(deps[0], { name: "org.springframework:spring-core", version: "6.0.0" }); - assertEquals(deps[1], { name: "com.google.guava:guava", version: "31.1-jre" }); + assertEquals(deps[0], { + name: "org.springframework:spring-core", + version: "6.0.0", + }); + assertEquals(deps[1], { + name: "com.google.guava:guava", + version: "31.1-jre", + }); }); Deno.test("mavenParser - skips property references", () => { @@ -198,7 +210,10 @@ Deno.test("mavenParser - skips property references", () => { `; const deps = mavenParser.parse(content); assertEquals(deps.length, 1); - assertEquals(deps[0], { name: "com.google.guava:guava", version: "31.1-jre" }); + assertEquals(deps[0], { + name: "com.google.guava:guava", + version: "31.1-jre", + }); }); // Gradle Groovy Parser Tests @@ -211,7 +226,10 @@ dependencies { `; const deps = gradleGroovyParser.parse(content); assertEquals(deps.length, 2); - assertEquals(deps[0], { name: "org.springframework:spring-core", version: "6.0.0" }); + assertEquals(deps[0], { + name: "org.springframework:spring-core", + version: "6.0.0", + }); assertEquals(deps[1], { name: "junit:junit", version: "4.13.2" }); }); @@ -224,8 +242,14 @@ dependencies { `; const deps = gradleGroovyParser.parse(content); assertEquals(deps.length, 2); - assertEquals(deps[0], { name: "org.springframework:spring-core", version: "6.0.0" }); - assertEquals(deps[1], { name: "com.google.guava:guava", version: "31.1-jre" }); + assertEquals(deps[0], { + name: "org.springframework:spring-core", + version: "6.0.0", + }); + assertEquals(deps[1], { + name: "com.google.guava:guava", + version: "31.1-jre", + }); }); Deno.test("gradleGroovyParser - parses map notation", () => { @@ -236,7 +260,10 @@ dependencies { `; const deps = gradleGroovyParser.parse(content); assertEquals(deps.length, 1); - assertEquals(deps[0], { name: "org.springframework:spring-core", version: "6.0.0" }); + assertEquals(deps[0], { + name: "org.springframework:spring-core", + version: "6.0.0", + }); }); Deno.test("gradleGroovyParser - skips variable references", () => { @@ -248,7 +275,10 @@ dependencies { `; const deps = gradleGroovyParser.parse(content); assertEquals(deps.length, 1); - assertEquals(deps[0], { name: "com.google.guava:guava", version: "31.1-jre" }); + assertEquals(deps[0], { + name: "com.google.guava:guava", + version: "31.1-jre", + }); }); // Gradle Kotlin Parser Tests @@ -261,7 +291,10 @@ dependencies { `; const deps = gradleKotlinParser.parse(content); assertEquals(deps.length, 2); - assertEquals(deps[0], { name: "org.springframework:spring-core", version: "6.0.0" }); + assertEquals(deps[0], { + name: "org.springframework:spring-core", + version: "6.0.0", + }); assertEquals(deps[1], { name: "junit:junit", version: "4.13.2" }); }); @@ -274,7 +307,10 @@ dependencies { `; const deps = gradleKotlinParser.parse(content); assertEquals(deps.length, 1); - assertEquals(deps[0], { name: "com.google.guava:guava", version: "31.1-jre" }); + assertEquals(deps[0], { + name: "com.google.guava:guava", + version: "31.1-jre", + }); }); // parseDependencies factory function tests @@ -292,7 +328,10 @@ Deno.test("parseDependencies - auto-detects pom.xml", () => { `; const deps = parseDependencies(content, "maven"); assertEquals(deps.length, 1); - assertEquals(deps[0], { name: "org.springframework:spring-core", version: "6.0.0" }); + assertEquals(deps[0], { + name: "org.springframework:spring-core", + version: "6.0.0", + }); }); Deno.test("parseDependencies - auto-detects Gradle Groovy", () => { @@ -303,7 +342,10 @@ dependencies { `; const deps = parseDependencies(content, "maven"); assertEquals(deps.length, 1); - assertEquals(deps[0], { name: "org.springframework:spring-core", version: "6.0.0" }); + assertEquals(deps[0], { + name: "org.springframework:spring-core", + version: "6.0.0", + }); }); Deno.test("parseDependencies - auto-detects Gradle Kotlin", () => { @@ -314,7 +356,10 @@ dependencies { `; const deps = parseDependencies(content, "maven"); assertEquals(deps.length, 1); - assertEquals(deps[0], { name: "org.springframework:spring-core", version: "6.0.0" }); + assertEquals(deps[0], { + name: "org.springframework:spring-core", + version: "6.0.0", + }); }); Deno.test("parseDependencies - uses correct parser for npm", () => { @@ -351,7 +396,10 @@ require github.com/gin-gonic/gin v1.9.1 `; const deps = parseDependencies(content, "go"); assertEquals(deps.length, 1); - assertEquals(deps[0], { name: "github.com/gin-gonic/gin", version: "v1.9.1" }); + assertEquals(deps[0], { + name: "github.com/gin-gonic/gin", + version: "v1.9.1", + }); }); // Parser metadata tests @@ -554,7 +602,10 @@ Deno.test("nugetParser - parses PackageReference with Version attribute", () => const deps = nugetParser.parse(content); assertEquals(deps.length, 2); assertEquals(deps[0], { name: "Newtonsoft.Json", version: "13.0.1" }); - assertEquals(deps[1], { name: "Microsoft.Extensions.Logging", version: "7.0.0" }); + assertEquals(deps[1], { + name: "Microsoft.Extensions.Logging", + version: "7.0.0", + }); }); Deno.test("nugetParser - parses PackageReference with Version as child element", () => { diff --git a/src/parsers/pypi.ts b/src/parsers/pypi.ts index 7bfde19..14f92f6 100644 --- a/src/parsers/pypi.ts +++ b/src/parsers/pypi.ts @@ -19,7 +19,9 @@ function parse(content: string): ParsedDependency[] { } // Match: package==version, package>=version, package~=version, etc. - const match = trimmed.match(/^([a-zA-Z0-9_-]+)\s*[=~><]+\s*([0-9][^\s,;#]*)/); + const match = trimmed.match( + /^([a-zA-Z0-9_-]+)\s*[=~><]+\s*([0-9][^\s,;#]*)/, + ); if (match) { deps.push({ name: match[1], version: match[2] }); } diff --git a/src/registries/cargo.ts b/src/registries/cargo.ts index f810e2c..cddd132 100644 --- a/src/registries/cargo.ts +++ b/src/registries/cargo.ts @@ -5,19 +5,19 @@ */ import type { + LookupOptions, + PackageMetadata, Registry, RegistryClient, - VersionInfo, VersionDetail, - PackageMetadata, - LookupOptions, + VersionInfo, } from "./types.ts"; import { + filterByPrefix, + findLatestPrerelease, + findLatestStable, isPrerelease, sortVersionsDescending, - findLatestStable, - findLatestPrerelease, - filterByPrefix, } from "../utils/version.ts"; import { versionCache } from "../utils/cache.ts"; import { fetchWithHeaders } from "../utils/http.ts"; @@ -55,7 +55,7 @@ export class CargoClient implements RegistryClient { private async fetchCrate( crateName: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("cargo", repositoryName); const cacheKey = `cargo:${repoConfig.url}:${crateName}`; @@ -72,7 +72,7 @@ export class CargoClient implements RegistryClient { throw new Error(`Crate '${crateName}' not found on ${repoConfig.name}`); } throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -83,7 +83,7 @@ export class CargoClient implements RegistryClient { async lookupVersion( packageName: string, - options?: LookupOptions & { repository?: string } + options?: LookupOptions & { repository?: string }, ): Promise { const data = await this.fetchCrate(packageName, options?.repository); @@ -97,7 +97,7 @@ export class CargoClient implements RegistryClient { versions = filterByPrefix(versions, options.versionPrefix); if (versions.length === 0) { throw new Error( - `No versions found for '${packageName}' with prefix '${options.versionPrefix}'` + `No versions found for '${packageName}' with prefix '${options.versionPrefix}'`, ); } } @@ -107,13 +107,16 @@ export class CargoClient implements RegistryClient { // Fall back to max_stable_version if available if (!latestStable && !options?.versionPrefix) { - latestStable = - data.crate.max_stable_version || data.crate.max_version; + latestStable = data.crate.max_stable_version || data.crate.max_version; } if (!latestStable) { throw new Error( - `No stable version found for '${packageName}'${options?.versionPrefix ? ` with prefix '${options.versionPrefix}'` : ""}` + `No stable version found for '${packageName}'${ + options?.versionPrefix + ? ` with prefix '${options.versionPrefix}'` + : "" + }`, ); } @@ -143,7 +146,7 @@ export class CargoClient implements RegistryClient { async listVersions( packageName: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { const data = await this.fetchCrate(packageName, options?.repository); @@ -159,14 +162,14 @@ export class CargoClient implements RegistryClient { isDeprecated: false, yanked: versionData?.yanked, }; - } + }, ); } async getMetadata( packageName: string, version?: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { const data = await this.fetchCrate(packageName, options?.repository); diff --git a/src/registries/go.ts b/src/registries/go.ts index 407993a..d89e57f 100644 --- a/src/registries/go.ts +++ b/src/registries/go.ts @@ -5,19 +5,19 @@ */ import type { + LookupOptions, + PackageMetadata, Registry, RegistryClient, - VersionInfo, VersionDetail, - PackageMetadata, - LookupOptions, + VersionInfo, } from "./types.ts"; import { + filterByPrefix, + findLatestPrerelease, + findLatestStable, isPrerelease, sortVersionsDescending, - findLatestStable, - findLatestPrerelease, - filterByPrefix, } from "../utils/version.ts"; import { versionCache } from "../utils/cache.ts"; import { fetchWithHeaders } from "../utils/http.ts"; @@ -41,7 +41,7 @@ export class GoClient implements RegistryClient { private async fetchVersionList( modulePath: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("go", repositoryName); const cacheKey = `go:${repoConfig.url}:versions:${modulePath}`; @@ -56,10 +56,12 @@ export class GoClient implements RegistryClient { if (!response.ok) { if (response.status === 404 || response.status === 410) { - throw new Error(`Module '${modulePath}' not found on ${repoConfig.name}`); + throw new Error( + `Module '${modulePath}' not found on ${repoConfig.name}`, + ); } throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -76,7 +78,7 @@ export class GoClient implements RegistryClient { private async fetchVersionInfo( modulePath: string, version: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("go", repositoryName); const cacheKey = `go:${repoConfig.url}:info:${modulePath}:${version}`; @@ -91,7 +93,7 @@ export class GoClient implements RegistryClient { if (!response.ok) { throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -102,16 +104,19 @@ export class GoClient implements RegistryClient { async lookupVersion( packageName: string, - options?: LookupOptions & { repository?: string } + options?: LookupOptions & { repository?: string }, ): Promise { - let versions = await this.fetchVersionList(packageName, options?.repository); + let versions = await this.fetchVersionList( + packageName, + options?.repository, + ); // Apply version prefix filter if specified if (options?.versionPrefix) { versions = filterByPrefix(versions, options.versionPrefix); if (versions.length === 0) { throw new Error( - `No versions found for '${packageName}' with prefix '${options.versionPrefix}'` + `No versions found for '${packageName}' with prefix '${options.versionPrefix}'`, ); } } @@ -121,14 +126,22 @@ export class GoClient implements RegistryClient { if (!latestStable) { throw new Error( - `No stable version found for '${packageName}'${options?.versionPrefix ? ` with prefix '${options.versionPrefix}'` : ""}` + `No stable version found for '${packageName}'${ + options?.versionPrefix + ? ` with prefix '${options.versionPrefix}'` + : "" + }`, ); } // Fetch version info to get timestamp let publishedAt: Date | undefined; try { - const info = await this.fetchVersionInfo(packageName, latestStable, options?.repository); + const info = await this.fetchVersionInfo( + packageName, + latestStable, + options?.repository, + ); publishedAt = new Date(info.Time); } catch { // Ignore errors fetching version info @@ -157,15 +170,22 @@ export class GoClient implements RegistryClient { async listVersions( packageName: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { - const versions = await this.fetchVersionList(packageName, options?.repository); + const versions = await this.fetchVersionList( + packageName, + options?.repository, + ); const details: VersionDetail[] = []; for (const version of sortVersionsDescending(versions)) { let publishedAt: Date | undefined; try { - const info = await this.fetchVersionInfo(packageName, version, options?.repository); + const info = await this.fetchVersionInfo( + packageName, + version, + options?.repository, + ); publishedAt = new Date(info.Time); } catch { // Ignore errors fetching version info @@ -182,21 +202,21 @@ export class GoClient implements RegistryClient { return details; } - async getMetadata( + getMetadata( packageName: string, _version?: string, - _options?: { repository?: string } + _options?: { repository?: string }, ): Promise { // Go proxy doesn't provide metadata - just return basic info // Full metadata would require fetching from pkg.go.dev or parsing go.mod - return { + return Promise.resolve({ name: packageName, registry: "go", homepage: `https://pkg.go.dev/${packageName}`, repository: packageName.startsWith("github.com") ? `https://${packageName}` : undefined, - }; + }); } } diff --git a/src/registries/jsr.ts b/src/registries/jsr.ts index 6d851f3..b7a8a23 100644 --- a/src/registries/jsr.ts +++ b/src/registries/jsr.ts @@ -9,19 +9,19 @@ */ import type { + LookupOptions, + PackageMetadata, Registry, RegistryClient, - VersionInfo, VersionDetail, - PackageMetadata, - LookupOptions, + VersionInfo, } from "./types.ts"; import { + filterByPrefix, + findLatestPrerelease, + findLatestStable, isPrerelease, sortVersionsDescending, - findLatestStable, - findLatestPrerelease, - filterByPrefix, } from "../utils/version.ts"; import { versionCache } from "../utils/cache.ts"; import { fetchWithHeaders } from "../utils/http.ts"; @@ -57,11 +57,13 @@ interface JsrVersionResponse { * Parse a JSR package name into scope and name * @param packageName Format: "@scope/name" (e.g., "@std/path") */ -function parseJsrPackageName(packageName: string): { scope: string; name: string } { +function parseJsrPackageName( + packageName: string, +): { scope: string; name: string } { const match = packageName.match(/^@([^/]+)\/(.+)$/); if (!match) { throw new Error( - `Invalid JSR package name: '${packageName}'. Expected format: @scope/name` + `Invalid JSR package name: '${packageName}'. Expected format: @scope/name`, ); } return { scope: match[1], name: match[2] }; @@ -72,7 +74,7 @@ export class JsrClient implements RegistryClient { private async fetchPackage( packageName: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("jsr", repositoryName); const cacheKey = `jsr:${repoConfig.url}:${packageName}`; @@ -87,10 +89,12 @@ export class JsrClient implements RegistryClient { if (!response.ok) { if (response.status === 404) { - throw new Error(`Package '${packageName}' not found on ${repoConfig.name}`); + throw new Error( + `Package '${packageName}' not found on ${repoConfig.name}`, + ); } throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -101,7 +105,7 @@ export class JsrClient implements RegistryClient { private async fetchVersions( packageName: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("jsr", repositoryName); const cacheKey = `jsr:${repoConfig.url}:versions:${packageName}`; @@ -116,10 +120,12 @@ export class JsrClient implements RegistryClient { if (!response.ok) { if (response.status === 404) { - throw new Error(`Package '${packageName}' not found on ${repoConfig.name}`); + throw new Error( + `Package '${packageName}' not found on ${repoConfig.name}`, + ); } throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -130,7 +136,7 @@ export class JsrClient implements RegistryClient { async lookupVersion( packageName: string, - options?: LookupOptions & { repository?: string } + options?: LookupOptions & { repository?: string }, ): Promise { const [packageData, versionsData] = await Promise.all([ this.fetchPackage(packageName, options?.repository), @@ -156,12 +162,18 @@ export class JsrClient implements RegistryClient { if (!latestStable) { throw new Error( - `No stable version found for '${packageName}'${options?.versionPrefix ? ` with prefix '${options.versionPrefix}'` : ""}` + `No stable version found for '${packageName}'${ + options?.versionPrefix + ? ` with prefix '${options.versionPrefix}'` + : "" + }`, ); } // Find publish date for the latest version - const latestVersionData = versionsData.find((v) => v.version === latestStable); + const latestVersionData = versionsData.find((v) => + v.version === latestStable + ); const result: VersionInfo = { packageName, @@ -189,9 +201,12 @@ export class JsrClient implements RegistryClient { async listVersions( packageName: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { - const versionsData = await this.fetchVersions(packageName, options?.repository); + const versionsData = await this.fetchVersions( + packageName, + options?.repository, + ); const versions = versionsData.map((v) => v.version); return sortVersionsDescending(versions).map((version) => { @@ -212,14 +227,18 @@ export class JsrClient implements RegistryClient { async getMetadata( packageName: string, _version?: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { - const packageData = await this.fetchPackage(packageName, options?.repository); + const packageData = await this.fetchPackage( + packageName, + options?.repository, + ); // Build GitHub repository URL if available let repository: string | undefined; if (packageData.githubRepository) { - repository = `https://github.com/${packageData.githubRepository.owner}/${packageData.githubRepository.name}`; + repository = + `https://github.com/${packageData.githubRepository.owner}/${packageData.githubRepository.name}`; } return { diff --git a/src/registries/maven.ts b/src/registries/maven.ts index 5b1e243..581cad5 100644 --- a/src/registries/maven.ts +++ b/src/registries/maven.ts @@ -5,24 +5,23 @@ */ import type { + LookupOptions, + PackageMetadata, Registry, RegistryClient, - VersionInfo, VersionDetail, - PackageMetadata, - LookupOptions, + VersionInfo, } from "./types.ts"; import { + filterByPrefix, + findLatestPrerelease, + findLatestStable, isPrerelease, sortVersionsDescending, - findLatestStable, - findLatestPrerelease, - filterByPrefix, } from "../utils/version.ts"; import { versionCache } from "../utils/cache.ts"; import { fetchWithHeaders } from "../utils/http.ts"; import { getRepositoryConfig } from "../config/loader.ts"; -import type { RepositoryAuth } from "../config/types.ts"; /** * Parsed maven-metadata.xml content @@ -71,11 +70,13 @@ function parseMavenMetadata(xml: string): MavenMetadata { export class MavenClient implements RegistryClient { readonly registry = "maven" as const satisfies Registry; - private parsePackageName(name: string): { groupId: string; artifactId: string } { + private parsePackageName( + name: string, + ): { groupId: string; artifactId: string } { const parts = name.split(":"); if (parts.length !== 2) { throw new Error( - `Invalid Maven package name '${name}'. Expected format: groupId:artifactId` + `Invalid Maven package name '${name}'. Expected format: groupId:artifactId`, ); } return { groupId: parts[0], artifactId: parts[1] }; @@ -92,27 +93,29 @@ export class MavenClient implements RegistryClient { private async fetchMetadata( groupId: string, artifactId: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("maven", repositoryName); - const cacheKey = `maven:${repoConfig.url}:metadata:${groupId}:${artifactId}`; + const cacheKey = + `maven:${repoConfig.url}:metadata:${groupId}:${artifactId}`; const cached = versionCache.get(cacheKey); if (cached) { return cached as MavenMetadata; } const groupPath = this.groupIdToPath(groupId); - const url = `${repoConfig.url}/${groupPath}/${artifactId}/maven-metadata.xml`; + const url = + `${repoConfig.url}/${groupPath}/${artifactId}/maven-metadata.xml`; const response = await fetchWithHeaders(url, { auth: repoConfig.auth }); if (!response.ok) { if (response.status === 404) { throw new Error( - `Package '${groupId}:${artifactId}' not found on ${repoConfig.name}` + `Package '${groupId}:${artifactId}' not found on ${repoConfig.name}`, ); } throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -125,14 +128,18 @@ export class MavenClient implements RegistryClient { async lookupVersion( packageName: string, - options?: LookupOptions & { repository?: string } + options?: LookupOptions & { repository?: string }, ): Promise { const { groupId, artifactId } = this.parsePackageName(packageName); - const metadata = await this.fetchMetadata(groupId, artifactId, options?.repository); + const metadata = await this.fetchMetadata( + groupId, + artifactId, + options?.repository, + ); if (metadata.versions.length === 0) { throw new Error( - `Package '${packageName}' not found on Maven Central` + `Package '${packageName}' not found on Maven Central`, ); } @@ -143,7 +150,7 @@ export class MavenClient implements RegistryClient { versionStrings = filterByPrefix(versionStrings, options.versionPrefix); if (versionStrings.length === 0) { throw new Error( - `No versions found for '${packageName}' with prefix '${options.versionPrefix}'` + `No versions found for '${packageName}' with prefix '${options.versionPrefix}'`, ); } } @@ -152,7 +159,11 @@ export class MavenClient implements RegistryClient { if (!latestStable) { throw new Error( - `No stable version found for '${packageName}'${options?.versionPrefix ? ` with prefix '${options.versionPrefix}'` : ""}` + `No stable version found for '${packageName}'${ + options?.versionPrefix + ? ` with prefix '${options.versionPrefix}'` + : "" + }`, ); } @@ -178,10 +189,14 @@ export class MavenClient implements RegistryClient { async listVersions( packageName: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { const { groupId, artifactId } = this.parsePackageName(packageName); - const metadata = await this.fetchMetadata(groupId, artifactId, options?.repository); + const metadata = await this.fetchMetadata( + groupId, + artifactId, + options?.repository, + ); return sortVersionsDescending(metadata.versions).map((version) => ({ version, @@ -197,14 +212,17 @@ export class MavenClient implements RegistryClient { groupId: string, artifactId: string, version: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("maven", repositoryName); const groupPath = this.groupIdToPath(groupId); - const pomUrl = `${repoConfig.url}/${groupPath}/${artifactId}/${version}/${artifactId}-${version}.pom`; + const pomUrl = + `${repoConfig.url}/${groupPath}/${artifactId}/${version}/${artifactId}-${version}.pom`; try { - const response = await fetchWithHeaders(pomUrl, { auth: repoConfig.auth }); + const response = await fetchWithHeaders(pomUrl, { + auth: repoConfig.auth, + }); if (!response.ok) { return undefined; } @@ -213,12 +231,16 @@ export class MavenClient implements RegistryClient { // Extract SCM URL from POM // Try first, then - const scmUrlMatch = pom.match(/[\s\S]*?([^<]+)<\/url>[\s\S]*?<\/scm>/); + const scmUrlMatch = pom.match( + /[\s\S]*?([^<]+)<\/url>[\s\S]*?<\/scm>/, + ); if (scmUrlMatch) { return scmUrlMatch[1].trim(); } - const scmConnMatch = pom.match(/[\s\S]*?([^<]+)<\/connection>[\s\S]*?<\/scm>/); + const scmConnMatch = pom.match( + /[\s\S]*?([^<]+)<\/connection>[\s\S]*?<\/scm>/, + ); if (scmConnMatch) { // Convert scm:git:... format to URL const conn = scmConnMatch[1].trim(); @@ -243,18 +265,28 @@ export class MavenClient implements RegistryClient { async getMetadata( packageName: string, version?: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { const { groupId, artifactId } = this.parsePackageName(packageName); - const metadata = await this.fetchMetadata(groupId, artifactId, options?.repository); + const metadata = await this.fetchMetadata( + groupId, + artifactId, + options?.repository, + ); // Get version to fetch POM from - const targetVersion = version || metadata.releaseVersion || metadata.latestVersion; + const targetVersion = version || metadata.releaseVersion || + metadata.latestVersion; // Try to get source repository from POM let sourceRepo: string | undefined; if (targetVersion) { - sourceRepo = await this.fetchScmUrl(groupId, artifactId, targetVersion, options?.repository); + sourceRepo = await this.fetchScmUrl( + groupId, + artifactId, + targetVersion, + options?.repository, + ); } return { @@ -262,7 +294,8 @@ export class MavenClient implements RegistryClient { registry: "maven", repository: sourceRepo, // homepage links to Maven Central for browsing - homepage: `https://central.sonatype.com/artifact/${groupId}/${artifactId}`, + homepage: + `https://central.sonatype.com/artifact/${groupId}/${artifactId}`, }; } } diff --git a/src/registries/npm.ts b/src/registries/npm.ts index 14323d7..f9fa323 100644 --- a/src/registries/npm.ts +++ b/src/registries/npm.ts @@ -5,19 +5,19 @@ */ import type { + LookupOptions, + PackageMetadata, Registry, RegistryClient, - VersionInfo, VersionDetail, - PackageMetadata, - LookupOptions, + VersionInfo, } from "./types.ts"; import { + filterByPrefix, + findLatestPrerelease, + findLatestStable, isPrerelease, sortVersionsDescending, - findLatestStable, - findLatestPrerelease, - filterByPrefix, } from "../utils/version.ts"; import { versionCache } from "../utils/cache.ts"; import { fetchWithHeaders } from "../utils/http.ts"; @@ -57,7 +57,7 @@ export class NpmClient implements RegistryClient { private async fetchPackage( packageName: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("npm", repositoryName); const cacheKey = `npm:${repoConfig.url}:${packageName}`; @@ -72,10 +72,12 @@ export class NpmClient implements RegistryClient { if (!response.ok) { if (response.status === 404) { - throw new Error(`Package '${packageName}' not found on ${repoConfig.name}`); + throw new Error( + `Package '${packageName}' not found on ${repoConfig.name}`, + ); } throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -86,7 +88,7 @@ export class NpmClient implements RegistryClient { async lookupVersion( packageName: string, - options?: LookupOptions & { repository?: string } + options?: LookupOptions & { repository?: string }, ): Promise { const data = await this.fetchPackage(packageName, options?.repository); let versions = Object.keys(data.versions); @@ -106,7 +108,11 @@ export class NpmClient implements RegistryClient { if (!latestStable) { throw new Error( - `No stable version found for '${packageName}'${options?.versionPrefix ? ` with prefix '${options.versionPrefix}'` : ""}` + `No stable version found for '${packageName}'${ + options?.versionPrefix + ? ` with prefix '${options.versionPrefix}'` + : "" + }`, ); } @@ -138,7 +144,7 @@ export class NpmClient implements RegistryClient { async listVersions( packageName: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { const data = await this.fetchPackage(packageName, options?.repository); const versions = Object.keys(data.versions); @@ -159,7 +165,7 @@ export class NpmClient implements RegistryClient { async getMetadata( packageName: string, version?: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { const data = await this.fetchPackage(packageName, options?.repository); @@ -175,8 +181,7 @@ export class NpmClient implements RegistryClient { } const license = versionData?.license ?? data.license; - const licenseStr = - typeof license === "string" ? license : license?.type; + const licenseStr = typeof license === "string" ? license : license?.type; return { name: packageName, diff --git a/src/registries/nuget.ts b/src/registries/nuget.ts index 7740a74..0f4e0dd 100644 --- a/src/registries/nuget.ts +++ b/src/registries/nuget.ts @@ -9,19 +9,19 @@ */ import type { + LookupOptions, + PackageMetadata, Registry, RegistryClient, - VersionInfo, VersionDetail, - PackageMetadata, - LookupOptions, + VersionInfo, } from "./types.ts"; import { + filterByPrefix, + findLatestPrerelease, + findLatestStable, isPrerelease, sortVersionsDescending, - findLatestStable, - findLatestPrerelease, - filterByPrefix, } from "../utils/version.ts"; import { versionCache } from "../utils/cache.ts"; import { fetchWithHeaders } from "../utils/http.ts"; @@ -62,24 +62,28 @@ export class NuGetClient implements RegistryClient { private async fetchVersions( packageName: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("nuget", repositoryName); - const cacheKey = `nuget:${repoConfig.url}:versions:${packageName.toLowerCase()}`; + const cacheKey = + `nuget:${repoConfig.url}:versions:${packageName.toLowerCase()}`; const cached = versionCache.get(cacheKey); if (cached) { return cached as string[]; } - const url = `${repoConfig.url}-flatcontainer/${packageName.toLowerCase()}/index.json`; + const url = + `${repoConfig.url}-flatcontainer/${packageName.toLowerCase()}/index.json`; const response = await fetchWithHeaders(url, { auth: repoConfig.auth }); if (!response.ok) { if (response.status === 404) { - throw new Error(`Package '${packageName}' not found on ${repoConfig.name}`); + throw new Error( + `Package '${packageName}' not found on ${repoConfig.name}`, + ); } throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -90,7 +94,7 @@ export class NuGetClient implements RegistryClient { private async fetchRegistration( packageName: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("nuget", repositoryName); const cacheKey = `nuget:${repoConfig.url}:reg:${packageName.toLowerCase()}`; @@ -99,7 +103,8 @@ export class NuGetClient implements RegistryClient { return cached as NuGetRegistrationResponse; } - const url = `${repoConfig.url}/registration5-gz-semver2/${packageName.toLowerCase()}/index.json`; + const url = + `${repoConfig.url}/registration5-gz-semver2/${packageName.toLowerCase()}/index.json`; const response = await fetchWithHeaders(url, { auth: repoConfig.auth, headers: { @@ -109,10 +114,12 @@ export class NuGetClient implements RegistryClient { if (!response.ok) { if (response.status === 404) { - throw new Error(`Package '${packageName}' not found on ${repoConfig.name}`); + throw new Error( + `Package '${packageName}' not found on ${repoConfig.name}`, + ); } throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -121,7 +128,9 @@ export class NuGetClient implements RegistryClient { return data; } - private getCatalogEntries(registration: NuGetRegistrationResponse): Map { + private getCatalogEntries( + registration: NuGetRegistrationResponse, + ): Map { const entries = new Map(); for (const page of registration.items) { if (page.items) { @@ -135,7 +144,7 @@ export class NuGetClient implements RegistryClient { async lookupVersion( packageName: string, - options?: LookupOptions & { repository?: string } + options?: LookupOptions & { repository?: string }, ): Promise { let versions = await this.fetchVersions(packageName, options?.repository); @@ -149,7 +158,11 @@ export class NuGetClient implements RegistryClient { if (!latestStable) { throw new Error( - `No stable version found for '${packageName}'${options?.versionPrefix ? ` with prefix '${options.versionPrefix}'` : ""}` + `No stable version found for '${packageName}'${ + options?.versionPrefix + ? ` with prefix '${options.versionPrefix}'` + : "" + }`, ); } @@ -159,7 +172,10 @@ export class NuGetClient implements RegistryClient { let deprecationMessage: string | undefined; try { - const registration = await this.fetchRegistration(packageName, options?.repository); + const registration = await this.fetchRegistration( + packageName, + options?.repository, + ); const entries = this.getCatalogEntries(registration); const entry = entries.get(latestStable); if (entry) { @@ -199,14 +215,17 @@ export class NuGetClient implements RegistryClient { async listVersions( packageName: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { const versions = await this.fetchVersions(packageName, options?.repository); // Try to get metadata from registration let entries = new Map(); try { - const registration = await this.fetchRegistration(packageName, options?.repository); + const registration = await this.fetchRegistration( + packageName, + options?.repository, + ); entries = this.getCatalogEntries(registration); } catch { // Continue without detailed metadata @@ -226,9 +245,12 @@ export class NuGetClient implements RegistryClient { async getMetadata( packageName: string, version?: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { - const registration = await this.fetchRegistration(packageName, options?.repository); + const registration = await this.fetchRegistration( + packageName, + options?.repository, + ); const entries = this.getCatalogEntries(registration); // Get specific version or latest @@ -236,7 +258,10 @@ export class NuGetClient implements RegistryClient { if (version) { entry = entries.get(version); } else { - const versions = await this.fetchVersions(packageName, options?.repository); + const versions = await this.fetchVersions( + packageName, + options?.repository, + ); const latestStable = findLatestStable(versions); if (latestStable) { entry = entries.get(latestStable); diff --git a/src/registries/pypi.ts b/src/registries/pypi.ts index eafe006..925e745 100644 --- a/src/registries/pypi.ts +++ b/src/registries/pypi.ts @@ -5,19 +5,19 @@ */ import type { + LookupOptions, + PackageMetadata, Registry, RegistryClient, - VersionInfo, VersionDetail, - PackageMetadata, - LookupOptions, + VersionInfo, } from "./types.ts"; import { + filterByPrefix, + findLatestPrerelease, + findLatestStable, isPrerelease, sortVersionsDescending, - findLatestStable, - findLatestPrerelease, - filterByPrefix, } from "../utils/version.ts"; import { versionCache } from "../utils/cache.ts"; import { fetchWithHeaders } from "../utils/http.ts"; @@ -58,7 +58,7 @@ export class PyPIClient implements RegistryClient { private async fetchPackage( packageName: string, - repositoryName?: string + repositoryName?: string, ): Promise { const repoConfig = getRepositoryConfig("pypi", repositoryName); const normalized = this.normalizePackageName(packageName); @@ -73,10 +73,12 @@ export class PyPIClient implements RegistryClient { if (!response.ok) { if (response.status === 404) { - throw new Error(`Package '${packageName}' not found on ${repoConfig.name}`); + throw new Error( + `Package '${packageName}' not found on ${repoConfig.name}`, + ); } throw new Error( - `${repoConfig.name} error: ${response.status} ${response.statusText}` + `${repoConfig.name} error: ${response.status} ${response.statusText}`, ); } @@ -103,7 +105,7 @@ export class PyPIClient implements RegistryClient { async lookupVersion( packageName: string, - options?: LookupOptions & { repository?: string } + options?: LookupOptions & { repository?: string }, ): Promise { const data = await this.fetchPackage(packageName, options?.repository); let versions = Object.keys(data.releases).filter((v) => { @@ -119,7 +121,7 @@ export class PyPIClient implements RegistryClient { // Find latest stable version const stableVersions = versions.filter( - (v) => !this.isPythonPrerelease(v) + (v) => !this.isPythonPrerelease(v), ); let latestStable = findLatestStable(stableVersions); @@ -130,7 +132,11 @@ export class PyPIClient implements RegistryClient { if (!latestStable) { throw new Error( - `No stable version found for '${packageName}'${options?.versionPrefix ? ` with prefix '${options.versionPrefix}'` : ""}` + `No stable version found for '${packageName}'${ + options?.versionPrefix + ? ` with prefix '${options.versionPrefix}'` + : "" + }`, ); } @@ -163,7 +169,7 @@ export class PyPIClient implements RegistryClient { async listVersions( packageName: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { const data = await this.fetchPackage(packageName, options?.repository); const versions = Object.keys(data.releases); @@ -186,15 +192,14 @@ export class PyPIClient implements RegistryClient { async getMetadata( packageName: string, _version?: string, - options?: { repository?: string } + options?: { repository?: string }, ): Promise { const data = await this.fetchPackage(packageName, options?.repository); // Try to find repository URL from project_urls let repository: string | undefined; if (data.info.project_urls) { - repository = - data.info.project_urls["Source"] || + repository = data.info.project_urls["Source"] || data.info.project_urls["Repository"] || data.info.project_urls["GitHub"] || data.info.project_urls["Code"]; diff --git a/src/tools/analyze-dependencies.ts b/src/tools/analyze-dependencies.ts index af619b0..263781d 100644 --- a/src/tools/analyze-dependencies.ts +++ b/src/tools/analyze-dependencies.ts @@ -3,7 +3,7 @@ * Analyze a dependency file and check for updates/vulnerabilities */ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { getClient, getJsrPackageClient } from "../registries/index.ts"; import type { Registry } from "../registries/types.ts"; @@ -13,13 +13,22 @@ import { checkVulnerabilities } from "../utils/vulnerability.ts"; const inputSchema = z.object({ content: z.string().describe( - "File content (package.json, pom.xml, build.gradle, build.gradle.kts, requirements.txt, Cargo.toml, go.mod, deno.json, *.csproj, Dockerfile, docker-compose.yml)" + "File content (package.json, pom.xml, build.gradle, build.gradle.kts, requirements.txt, Cargo.toml, go.mod, deno.json, *.csproj, Dockerfile, docker-compose.yml)", ), - registry: z.enum(["npm", "maven", "pypi", "cargo", "go", "jsr", "nuget", "docker"]).describe( - "Package registry to use. For Gradle files (build.gradle, build.gradle.kts), use 'maven'" + registry: z.enum([ + "npm", + "maven", + "pypi", + "cargo", + "go", + "jsr", + "nuget", + "docker", + ]).describe( + "Package registry to use. For Gradle files (build.gradle, build.gradle.kts), use 'maven'", ), checkVulnerabilities: z.boolean().optional().default(false).describe( - "Also scan for vulnerabilities (slower)" + "Also scan for vulnerabilities (slower)", ), }); @@ -74,7 +83,7 @@ SECURITY: Always use exact versions (e.g., "1.2.3") instead of ranges (e.g., "^1 }, }, null, - 2 + 2, ), }, ], @@ -89,7 +98,11 @@ SECURITY: Always use exact versions (e.g., "1.2.3") instead of ranges (e.g., "^1 updateAvailable: boolean; updateType: string; deprecated?: boolean; - vulnerabilities?: { id: string; summary?: string; severity?: string }[]; + vulnerabilities?: { + id: string; + summary?: string; + severity?: string; + }[]; }[] = []; const summary = { @@ -127,7 +140,7 @@ SECURITY: Always use exact versions (e.g., "1.2.3") instead of ranges (e.g., "^1 const versionInfo = await client.lookupVersion(lookupName); const updateType = getUpdateType( dep.version, - versionInfo.latestStable + versionInfo.latestStable, ); const updateAvailable = updateType !== "none"; @@ -144,7 +157,7 @@ SECURITY: Always use exact versions (e.g., "1.2.3") instead of ranges (e.g., "^1 const vulns = await checkVulnerabilities( lookupName, dep.version, - vulnRegistry + vulnRegistry, ); result.vulnerabilities = vulns.map((v) => ({ id: v.id, @@ -163,7 +176,7 @@ SECURITY: Always use exact versions (e.g., "1.2.3") instead of ranges (e.g., "^1 updateType: "unknown", }; } - }) + }), ); results.push(...batchResults); @@ -209,6 +222,6 @@ SECURITY: Always use exact versions (e.g., "1.2.3") instead of ranges (e.g., "^1 isError: true, }; } - } + }, ); } diff --git a/src/tools/check-vulnerabilities.ts b/src/tools/check-vulnerabilities.ts index ea36188..81e9ccc 100644 --- a/src/tools/check-vulnerabilities.ts +++ b/src/tools/check-vulnerabilities.ts @@ -3,7 +3,7 @@ * Check a package version for known vulnerabilities using OSV database */ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import type { Registry, Severity } from "../registries/types.ts"; import { @@ -12,11 +12,20 @@ import { } from "../utils/vulnerability.ts"; const inputSchema = z.object({ - registry: z.enum(["npm", "maven", "pypi", "cargo", "go", "jsr", "nuget", "docker"]).describe( - "Package registry (npm, maven, pypi, cargo, go, jsr, nuget, docker)" + registry: z.enum([ + "npm", + "maven", + "pypi", + "cargo", + "go", + "jsr", + "nuget", + "docker", + ]).describe( + "Package registry (npm, maven, pypi, cargo, go, jsr, nuget, docker)", ), package: z.string().describe( - "Package name. Maven uses groupId:artifactId format, Go uses full module path, JSR uses @scope/name, Docker uses image name (nginx, user/repo)" + "Package name. Maven uses groupId:artifactId format, Go uses full module path, JSR uses @scope/name, Docker uses image name (nginx, user/repo)", ), version: z.string().describe("Version to check for vulnerabilities"), severityThreshold: z @@ -46,7 +55,7 @@ Returns CVE IDs, severity ratings, affected version ranges, and available fixes. packageName, version, registry as Registry, - { severityThreshold: severityThreshold as Severity | undefined } + { severityThreshold: severityThreshold as Severity | undefined }, ); const summary = getVulnerabilitySummary(vulns); @@ -90,6 +99,6 @@ Returns CVE IDs, severity ratings, affected version ranges, and available fixes. isError: true, }; } - } + }, ); } diff --git a/src/tools/get-package-docs.ts b/src/tools/get-package-docs.ts index 9f479df..65edcb1 100644 --- a/src/tools/get-package-docs.ts +++ b/src/tools/get-package-docs.ts @@ -3,7 +3,7 @@ * Fetches README/documentation for packages from various registries */ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import type { Registry } from "../registries/types.ts"; import { getClient, supportedRegistries } from "../registries/index.ts"; @@ -34,17 +34,21 @@ export interface GetPackageDocsResult { function getDocumentationUrls( registry: Registry, packageName: string, - version?: string + version?: string, ): { docs?: string; repository?: string } { switch (registry) { case "npm": return { - docs: `https://www.npmjs.com/package/${packageName}${version ? `/v/${version}` : ""}`, + docs: `https://www.npmjs.com/package/${packageName}${ + version ? `/v/${version}` : "" + }`, }; case "maven": { const [groupId, artifactId] = packageName.split(":"); return { - docs: `https://javadoc.io/doc/${groupId}/${artifactId}${version ? `/${version}` : "/latest"}`, + docs: `https://javadoc.io/doc/${groupId}/${artifactId}${ + version ? `/${version}` : "/latest" + }`, }; } case "pypi": @@ -57,7 +61,9 @@ function getDocumentationUrls( }; case "go": return { - docs: `https://pkg.go.dev/${packageName}${version ? `@${version}` : ""}`, + docs: `https://pkg.go.dev/${packageName}${ + version ? `@${version}` : "" + }`, }; case "jsr": { const [scope, name] = packageName.replace(/^@/, "").split("/"); @@ -99,7 +105,7 @@ async function fetchNpmReadme(packageName: string): Promise { try { const response = await fetchWithHeaders( `${repoConfig.url}/${encodedName}`, - { auth: repoConfig.auth } + { auth: repoConfig.auth }, ); if (response.ok) { const data = await response.json(); @@ -114,14 +120,16 @@ async function fetchNpmReadme(packageName: string): Promise { /** * Fetch description from PyPI (often contains README content) */ -async function fetchPypiDescription(packageName: string): Promise { +async function fetchPypiDescription( + packageName: string, +): Promise { const repoConfig = getRepositoryConfig("pypi"); const normalized = packageName.toLowerCase().replace(/[-_.]+/g, "-"); try { const response = await fetchWithHeaders( `${repoConfig.url}/${normalized}/json`, - { auth: repoConfig.auth } + { auth: repoConfig.auth }, ); if (response.ok) { const data = await response.json(); @@ -143,7 +151,7 @@ async function fetchCargoReadme(packageName: string): Promise { try { const response = await fetchWithHeaders( `${repoConfig.url}/${packageName}`, - { auth: repoConfig.auth } + { auth: repoConfig.auth }, ); if (response.ok) { const data = await response.json(); @@ -200,7 +208,7 @@ export function formatDocsResultAsText(result: GetPackageDocsResult): string { * Get package documentation (README) from a registry */ export async function getPackageDocs( - input: GetPackageDocsInput + input: GetPackageDocsInput, ): Promise { const { registry, package: packageName, version } = input; const urls = getDocumentationUrls(registry, packageName, version); @@ -264,7 +272,9 @@ export async function getPackageDocs( result.content = content || ""; return result; } catch (error) { - const message = error instanceof Error ? error.message : "Unknown error occurred"; + const message = error instanceof Error + ? error.message + : "Unknown error occurred"; return { ...result, error: message, @@ -273,14 +283,23 @@ export async function getPackageDocs( } const inputSchema = z.object({ - registry: z.enum(["npm", "maven", "pypi", "cargo", "go", "jsr", "nuget", "docker"]).describe( - "Package registry (npm, maven, pypi, cargo, go, jsr, nuget, docker)" + registry: z.enum([ + "npm", + "maven", + "pypi", + "cargo", + "go", + "jsr", + "nuget", + "docker", + ]).describe( + "Package registry (npm, maven, pypi, cargo, go, jsr, nuget, docker)", ), package: z.string().describe( - "Package name. Maven uses groupId:artifactId format, Go uses full module path, JSR uses @scope/name, Docker uses image name (nginx, user/repo)" + "Package name. Maven uses groupId:artifactId format, Go uses full module path, JSR uses @scope/name, Docker uses image name (nginx, user/repo)", ), version: z.string().optional().describe( - "Specific version to get documentation for (optional, defaults to latest)" + "Specific version to get documentation for (optional, defaults to latest)", ), }); @@ -326,6 +345,6 @@ Examples: ], isError: !!result.error, }; - } + }, ); } diff --git a/src/tools/list-versions.ts b/src/tools/list-versions.ts index 80d1343..0b5df5e 100644 --- a/src/tools/list-versions.ts +++ b/src/tools/list-versions.ts @@ -3,7 +3,7 @@ * List all available versions of a package */ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { getClient, supportedRegistries } from "../registries/index.ts"; import type { Registry } from "../registries/types.ts"; diff --git a/src/tools/lookup-version.ts b/src/tools/lookup-version.ts index e8b883f..761dd6a 100644 --- a/src/tools/lookup-version.ts +++ b/src/tools/lookup-version.ts @@ -3,7 +3,7 @@ * Look up the latest version of a package */ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { getClient, supportedRegistries } from "../registries/index.ts"; import type { Registry } from "../registries/types.ts"; diff --git a/src/tools/types.ts b/src/tools/types.ts index fecd6fe..0271b47 100644 --- a/src/tools/types.ts +++ b/src/tools/types.ts @@ -3,10 +3,10 @@ */ import type { + DependencyInfo, Registry, Severity, Vulnerability, - DependencyInfo, } from "../registries/types.ts"; // === lookup_version === diff --git a/src/utils/cache.ts b/src/utils/cache.ts index bc478db..5d6bb24 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -102,7 +102,7 @@ export class Cache { async getOrSet( key: string, compute: () => Promise, - ttlMs?: number + ttlMs?: number, ): Promise { const existing = this.get(key); if (existing !== undefined) { diff --git a/src/utils/github.ts b/src/utils/github.ts index 2884a1c..a558aca 100644 --- a/src/utils/github.ts +++ b/src/utils/github.ts @@ -14,7 +14,9 @@ const GITHUB_API_BASE = "https://api.github.com"; * - git+https://github.com/owner/repo.git * - github:owner/repo */ -export function parseGitHubUrl(url: string): { owner: string; repo: string } | null { +export function parseGitHubUrl( + url: string, +): { owner: string; repo: string } | null { // Handle github: shorthand if (url.startsWith("github:")) { const parts = url.slice(7).split("/"); @@ -48,14 +50,21 @@ export function parseGitHubUrl(url: string): { owner: string; repo: string } | n export async function fetchGitHubReadme( owner: string, repo: string, - ref?: string + ref?: string, ): Promise { // Try common README filenames - const readmeFiles = ["README.md", "readme.md", "README", "readme.txt", "README.rst"]; + const readmeFiles = [ + "README.md", + "readme.md", + "README", + "readme.txt", + "README.rst", + ]; for (const filename of readmeFiles) { try { - let url = `${GITHUB_API_BASE}/repos/${owner}/${repo}/contents/${filename}`; + let url = + `${GITHUB_API_BASE}/repos/${owner}/${repo}/contents/${filename}`; if (ref) { url += `?ref=${encodeURIComponent(ref)}`; } @@ -82,13 +91,13 @@ export async function fetchGitHubReadme( * @param repositoryUrl - Full repository URL (GitHub, GitLab, etc.) * @param ref - Optional branch/tag/commit */ -export async function fetchReadmeFromRepository( +export function fetchReadmeFromRepository( repositoryUrl: string, - ref?: string + ref?: string, ): Promise { const parsed = parseGitHubUrl(repositoryUrl); if (!parsed) { - return null; // Not a GitHub URL or unsupported format + return Promise.resolve(null); // Not a GitHub URL or unsupported format } return fetchGitHubReadme(parsed.owner, parsed.repo, ref); diff --git a/src/utils/http.ts b/src/utils/http.ts index f29fb7d..bd55791 100644 --- a/src/utils/http.ts +++ b/src/utils/http.ts @@ -12,7 +12,8 @@ const VERSION = "1.0.0"; * User-Agent header for all registry requests * Format follows RFC 7231 conventions */ -export const USER_AGENT = `mcp-dependency-version/${VERSION} (https://github.com/anthropics/mcp-dependency-version)`; +export const USER_AGENT = + `mcp-dependency-version/${VERSION} (https://github.com/anthropics/mcp-dependency-version)`; /** * Default headers for all registry requests @@ -48,9 +49,9 @@ function buildAuthHeader(auth: RepositoryAuth): string | null { * Fetch with default headers (User-Agent, Accept) and optional authentication * Use this instead of raw fetch() for all registry requests */ -export async function fetchWithHeaders( +export function fetchWithHeaders( url: string, - options?: FetchOptions + options?: FetchOptions, ): Promise { const headers = new Headers(options?.headers); diff --git a/src/utils/version.test.ts b/src/utils/version.test.ts index c828bfa..ad5e84f 100644 --- a/src/utils/version.test.ts +++ b/src/utils/version.test.ts @@ -1,14 +1,14 @@ -import { assertEquals, assert } from "jsr:@std/assert"; +import { assert, assertEquals } from "@std/assert"; import { - parseVersion, - isPrerelease, compareVersions, - sortVersionsDescending, - findLatestStable, - findLatestPrerelease, filterByPrefix, + findLatestPrerelease, + findLatestStable, getUpdateType, + isPrerelease, + parseVersion, satisfiesConstraint, + sortVersionsDescending, } from "./version.ts"; Deno.test("parseVersion - parses basic semver", () => { diff --git a/src/utils/version.ts b/src/utils/version.ts index dbacb10..e4e0462 100644 --- a/src/utils/version.ts +++ b/src/utils/version.ts @@ -20,7 +20,7 @@ export function parseVersion(version: string): ParsedVersion | null { // Match semver pattern: major.minor.patch[-prerelease][+build] const match = normalized.match( - /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([a-zA-Z0-9.-]+))?(?:\+([a-zA-Z0-9.-]+))?$/ + /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([a-zA-Z0-9.-]+))?(?:\+([a-zA-Z0-9.-]+))?$/, ); if (!match) { @@ -175,7 +175,7 @@ export function filterByPrefix(versions: string[], prefix: string): string[] { */ export function getUpdateType( currentVersion: string, - newVersion: string + newVersion: string, ): "major" | "minor" | "patch" | "prerelease" | "none" { const current = parseVersion(currentVersion); const next = parseVersion(newVersion); @@ -210,7 +210,7 @@ export function getUpdateType( */ export function satisfiesConstraint( version: string, - constraint: string + constraint: string, ): boolean { const parsed = parseVersion(version); if (!parsed) return false; diff --git a/src/utils/vulnerability.ts b/src/utils/vulnerability.ts index 632a2c3..d059071 100644 --- a/src/utils/vulnerability.ts +++ b/src/utils/vulnerability.ts @@ -162,7 +162,7 @@ export async function checkVulnerabilities( packageName: string, version: string, registry: Registry, - options?: CheckVulnerabilitiesOptions + options?: CheckVulnerabilitiesOptions, ): Promise { const cacheKey = `osv:${registry}:${packageName}:${version}`; let vulns = vulnerabilityCache.get(cacheKey) as Vulnerability[] | undefined;