@4da/mcp-server 6.0.2 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +433 -0
- package/README.md +26 -18
- package/dist/db.d.ts +73 -8
- package/dist/db.js +180 -43
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +15 -9
- package/dist/embeddings.js +19 -7
- package/dist/gitignore.js +9 -3
- package/dist/index.js +43 -4
- package/dist/live/cache.d.ts +31 -2
- package/dist/live/cache.js +113 -1
- package/dist/live/changelog-classify.d.ts +9 -3
- package/dist/live/changelog-classify.js +125 -12
- package/dist/live/changelog.d.ts +22 -3
- package/dist/live/changelog.js +132 -38
- package/dist/live/dev-scope.d.ts +2 -2
- package/dist/live/fix-paths.d.ts +118 -0
- package/dist/live/fix-paths.js +498 -0
- package/dist/live/github-releases.d.ts +69 -0
- package/dist/live/github-releases.js +252 -0
- package/dist/live/go-registry.d.ts +5 -0
- package/dist/live/go-registry.js +1 -1
- package/dist/live/go-releases.d.ts +48 -0
- package/dist/live/go-releases.js +196 -0
- package/dist/live/index.d.ts +34 -8
- package/dist/live/index.js +66 -7
- package/dist/live/lockfile-parents.d.ts +61 -0
- package/dist/live/lockfile-parents.js +360 -0
- package/dist/live/lockfile-parsers-pygo.d.ts +31 -0
- package/dist/live/lockfile-parsers-pygo.js +178 -28
- package/dist/live/lockfile-parsers.d.ts +4 -1
- package/dist/live/lockfile-parsers.js +57 -24
- package/dist/live/lockfile-types.d.ts +12 -3
- package/dist/live/lockfile-types.js +2 -1
- package/dist/live/osv-ranges.d.ts +85 -0
- package/dist/live/osv-ranges.js +136 -0
- package/dist/live/osv-scanner.d.ts +1 -0
- package/dist/live/osv-scanner.js +85 -78
- package/dist/live/package-archive.d.ts +2 -0
- package/dist/live/package-archive.js +7 -4
- package/dist/live/pypi-releases.d.ts +44 -0
- package/dist/live/pypi-releases.js +103 -0
- package/dist/live/range-satisfies.d.ts +21 -0
- package/dist/live/range-satisfies.js +190 -0
- package/dist/live/rate-limiter.d.ts +6 -0
- package/dist/live/rate-limiter.js +5 -0
- package/dist/live/registry-health.js +2 -0
- package/dist/live/release-metadata.d.ts +17 -8
- package/dist/live/release-metadata.js +6 -3
- package/dist/live/resolution.d.ts +11 -1
- package/dist/live/resolution.js +16 -6
- package/dist/live/semver-utils.d.ts +8 -0
- package/dist/live/semver-utils.js +10 -2
- package/dist/live/types.d.ts +17 -1
- package/dist/live/upgrade-go.d.ts +62 -0
- package/dist/live/upgrade-go.js +130 -0
- package/dist/live/upgrade-pypi.d.ts +42 -0
- package/dist/live/upgrade-pypi.js +85 -0
- package/dist/live/upgrade-sources.d.ts +30 -11
- package/dist/live/upgrade-sources.js +93 -21
- package/dist/live/zip-archive.d.ts +22 -0
- package/dist/live/zip-archive.js +98 -0
- package/dist/memory-store.d.ts +19 -0
- package/dist/memory-store.js +8 -0
- package/dist/native-bindings.d.ts +7 -2
- package/dist/native-bindings.js +4 -0
- package/dist/project-tree.d.ts +50 -8
- package/dist/project-tree.js +121 -29
- package/dist/schema-registry.js +5 -5
- package/dist/server-instructions.d.ts +6 -0
- package/dist/server-instructions.js +20 -15
- package/dist/sqlite-driver.d.ts +134 -0
- package/dist/sqlite-driver.js +359 -0
- package/dist/tool-dispatch.d.ts +8 -1
- package/dist/tool-dispatch.js +17 -2
- package/dist/tools/agent-memory.js +10 -2
- package/dist/tools/app-plan.d.ts +11 -1
- package/dist/tools/app-plan.js +32 -6
- package/dist/tools/briefing-task-scope.d.ts +21 -2
- package/dist/tools/briefing-task-scope.js +97 -4
- package/dist/tools/decision-memory.js +9 -3
- package/dist/tools/dependency-check-signals.d.ts +23 -5
- package/dist/tools/dependency-check-signals.js +62 -19
- package/dist/tools/dependency-check-versions.d.ts +94 -0
- package/dist/tools/dependency-check-versions.js +213 -0
- package/dist/tools/dependency-check.d.ts +27 -11
- package/dist/tools/dependency-check.js +321 -74
- package/dist/tools/dependency-health.d.ts +1 -1
- package/dist/tools/dependency-health.js +5 -0
- package/dist/tools/knowledge-gap-ranges.d.ts +1 -1
- package/dist/tools/knowledge-gap-ranges.js +1 -1
- package/dist/tools/knowledge-gap-scope.d.ts +2 -2
- package/dist/tools/project-scope.d.ts +45 -0
- package/dist/tools/project-scope.js +119 -0
- package/dist/tools/record-feedback.js +1 -1
- package/dist/tools/upgrade-impact-callsites.d.ts +1 -0
- package/dist/tools/upgrade-impact-callsites.js +23 -5
- package/dist/tools/upgrade-impact-changelog.d.ts +41 -0
- package/dist/tools/upgrade-impact-changelog.js +79 -0
- package/dist/tools/upgrade-impact-imports.d.ts +42 -0
- package/dist/tools/upgrade-impact-imports.js +260 -0
- package/dist/tools/upgrade-impact-literals.js +1 -1
- package/dist/tools/upgrade-impact-report.d.ts +23 -0
- package/dist/tools/upgrade-impact-report.js +45 -6
- package/dist/tools/upgrade-impact.d.ts +13 -4
- package/dist/tools/upgrade-impact.js +236 -85
- package/dist/tools/upgrade-planner-fixpaths.d.ts +40 -0
- package/dist/tools/upgrade-planner-fixpaths.js +0 -0
- package/dist/tools/upgrade-planner.d.ts +31 -3
- package/dist/tools/upgrade-planner.js +74 -19
- package/dist/tools/vulnerability-scan-concise.d.ts +13 -1
- package/dist/tools/vulnerability-scan-concise.js +15 -3
- package/dist/tools/vulnerability-scan-format.d.ts +58 -1
- package/dist/tools/vulnerability-scan-format.js +107 -11
- package/dist/tools/vulnerability-scan.d.ts +104 -13
- package/dist/tools/vulnerability-scan.js +27 -3
- package/dist/tools/what-should-i-know.d.ts +22 -2
- package/dist/tools/what-should-i-know.js +122 -12
- package/package.json +10 -32
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Per-release registry metadata, normalised across npm
|
|
2
|
+
* Per-release registry metadata, normalised across npm, crates.io, PyPI and Go.
|
|
3
3
|
*
|
|
4
4
|
* `dependency_check` judges ONE proposed version against the one installed
|
|
5
5
|
* (or, for a new dependency, against the previous release on the same line).
|
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
* raw responses into this shape so the signal logic never sees a registry's
|
|
10
10
|
* wire format.
|
|
11
11
|
*
|
|
12
|
-
* Privacy: every request behind this shape carries
|
|
13
|
-
*
|
|
12
|
+
* Privacy: every npm, crates.io and PyPI request behind this shape carries
|
|
13
|
+
* the package NAME only; versions are selected locally from the full release
|
|
14
|
+
* list. The Go module proxy lists versions without dates, so a Go release's
|
|
15
|
+
* date is read from its own `/@v/<version>.info` (the proxy `go get` itself
|
|
16
|
+
* downloads from), for the versions being judged only.
|
|
14
17
|
*/
|
|
15
18
|
/**
|
|
16
19
|
* Publish trust of one release.
|
|
@@ -27,17 +30,23 @@ export interface ReleaseRecord {
|
|
|
27
30
|
trust: TrustLevel;
|
|
28
31
|
/** Human label for the trust evidence, e.g. "trusted publisher (github)". */
|
|
29
32
|
trustEvidence: string;
|
|
30
|
-
/** npm: install-time lifecycle scripts declared (preinstall/install/postinstall, or a gypfile). Null
|
|
33
|
+
/** npm: install-time lifecycle scripts declared (preinstall/install/postinstall, or a gypfile). Null elsewhere. */
|
|
31
34
|
installScripts: string[] | null;
|
|
32
35
|
/** npm deprecation message, or null. */
|
|
33
36
|
deprecated: string | null;
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
/**
|
|
38
|
+
* crates.io / PyPI: yanked (PyPI: every file of the release). npm: unpublished
|
|
39
|
+
* (listed in `time` but gone from `versions`). Go: retracted by a `retract`
|
|
40
|
+
* directive in the module's latest go.mod.
|
|
41
|
+
*/
|
|
42
|
+
withdrawn: "yanked" | "unpublished" | "retracted" | null;
|
|
43
|
+
/** Runtime dependency NAMES (npm dependencies + optionalDependencies; crates normal + build). Null when unknown or not read (PyPI, Go). */
|
|
37
44
|
dependencies: string[] | null;
|
|
38
45
|
}
|
|
46
|
+
/** The registries dependency_check reads. */
|
|
47
|
+
export type CheckEcosystem = "npm" | "crates.io" | "PyPI" | "Go";
|
|
39
48
|
export interface PackageReleaseIndex {
|
|
40
|
-
ecosystem:
|
|
49
|
+
ecosystem: CheckEcosystem;
|
|
41
50
|
name: string;
|
|
42
51
|
/** When the package itself was first published, or null. */
|
|
43
52
|
createdAt: string | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
/**
|
|
3
|
-
* Per-release registry metadata, normalised across npm
|
|
3
|
+
* Per-release registry metadata, normalised across npm, crates.io, PyPI and Go.
|
|
4
4
|
*
|
|
5
5
|
* `dependency_check` judges ONE proposed version against the one installed
|
|
6
6
|
* (or, for a new dependency, against the previous release on the same line).
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
* raw responses into this shape so the signal logic never sees a registry's
|
|
11
11
|
* wire format.
|
|
12
12
|
*
|
|
13
|
-
* Privacy: every request behind this shape carries
|
|
14
|
-
*
|
|
13
|
+
* Privacy: every npm, crates.io and PyPI request behind this shape carries
|
|
14
|
+
* the package NAME only; versions are selected locally from the full release
|
|
15
|
+
* list. The Go module proxy lists versions without dates, so a Go release's
|
|
16
|
+
* date is read from its own `/@v/<version>.info` (the proxy `go get` itself
|
|
17
|
+
* downloads from), for the versions being judged only.
|
|
15
18
|
*/
|
|
16
19
|
export {};
|
|
@@ -33,8 +33,18 @@ export interface GroupResolution {
|
|
|
33
33
|
/** The full lockfile set plus drift entries (vulnerability scanning). */
|
|
34
34
|
audit: ResolvedDependency[];
|
|
35
35
|
drift: InstallDriftRecord[];
|
|
36
|
-
/** The file the versions were read from, or null when the group resolved from nothing. */
|
|
36
|
+
/** The (first) file the versions were read from, or null when the group resolved from nothing. */
|
|
37
37
|
source: ResolutionSourceRecord | null;
|
|
38
|
+
/** Every file read, `source` first (Python merges its lockfiles and requirement files). */
|
|
39
|
+
sources: ResolutionSourceRecord[];
|
|
40
|
+
/**
|
|
41
|
+
* Set when no lockfile pins this group's versions: the manifest and how many
|
|
42
|
+
* of its dependencies are declared ranges, which are not sent to OSV.
|
|
43
|
+
*/
|
|
44
|
+
unresolved: {
|
|
45
|
+
manifest: string;
|
|
46
|
+
ranges: number;
|
|
47
|
+
} | null;
|
|
38
48
|
signatures: Map<string, FileSignature>;
|
|
39
49
|
resolvedAt: string;
|
|
40
50
|
}
|
package/dist/live/resolution.js
CHANGED
|
@@ -30,11 +30,20 @@ export function resolveGroup(group) {
|
|
|
30
30
|
...(manifest ? [manifest] : []),
|
|
31
31
|
]);
|
|
32
32
|
const read = resolveVersionSource(group.dir, ecosystem);
|
|
33
|
+
const readFiles = read.source ? [read.source, ...(read.extraSources ?? [])] : [];
|
|
33
34
|
// The manifest only matters when the resolver fell back to it; editing a
|
|
34
35
|
// script in package.json must not throw away a scan of an unchanged lockfile.
|
|
35
|
-
if (manifest &&
|
|
36
|
+
if (manifest && !readFiles.includes(manifest))
|
|
36
37
|
signatures.delete(manifest);
|
|
37
38
|
let resolved = resolveVersionsFrom(group.dir, group.deps, group.devDeps, ecosystem, read.versions, group.targets);
|
|
39
|
+
// No lockfile: a ranged dependency's version is the range's floor, a label.
|
|
40
|
+
const ranges = read.kind === "declared_ranges" ? read.ranges ?? new Map() : null;
|
|
41
|
+
if (ranges && ranges.size > 0) {
|
|
42
|
+
resolved = resolved.map((dep) => {
|
|
43
|
+
const declaredRange = ranges.get(dep.name);
|
|
44
|
+
return declaredRange ? { ...dep, declaredRange } : dep;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
38
47
|
let drift = [];
|
|
39
48
|
let driftAudit = [];
|
|
40
49
|
// Only a lockfile pins exact versions; comparing node_modules against a
|
|
@@ -50,11 +59,12 @@ export function resolveGroup(group) {
|
|
|
50
59
|
const audit = [
|
|
51
60
|
...resolveAuditVersionsFrom(group.dir, group.deps, group.devDeps, ecosystem, read.versions, group.targets, resolved, read.instances),
|
|
52
61
|
...driftAudit,
|
|
53
|
-
];
|
|
54
|
-
const
|
|
55
|
-
? { path:
|
|
56
|
-
:
|
|
57
|
-
|
|
62
|
+
].filter((dep) => !dep.declaredRange);
|
|
63
|
+
const sources = read.kind
|
|
64
|
+
? readFiles.map((file) => ({ path: file, kind: read.kind, mtimeMs: signatureMtime(signatures.get(file)) }))
|
|
65
|
+
: [];
|
|
66
|
+
const unresolved = ranges && ranges.size > 0 && read.source ? { manifest: read.source, ranges: ranges.size } : null;
|
|
67
|
+
return { group, resolved, audit, drift, source: sources[0] ?? null, sources, unresolved, signatures, resolvedAt };
|
|
58
68
|
}
|
|
59
69
|
/** True when any file the resolution depended on changed, appeared, or vanished. */
|
|
60
70
|
export function groupIsStale(result) {
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { SemverDistance } from "./types.js";
|
|
2
2
|
export declare function parseSemver(version: string): [number, number, number] | null;
|
|
3
|
+
/**
|
|
4
|
+
* How far `current` is behind `latest`. `label` is the kind of bump, by the
|
|
5
|
+
* caret rules npm and Cargo apply: below 1.0 a minor bump is breaking (^0.8.6
|
|
6
|
+
* does not admit 0.9.0) and below 0.1 a patch bump is too (^0.0.3 is =0.0.3),
|
|
7
|
+
* so both are labelled "major", the same as upgrade_impact and dependency_check.
|
|
8
|
+
* Until 6.0.2 sqlx 0.8 -> 0.9 and reqwest 0.12 -> 0.13 were labelled "minor".
|
|
9
|
+
* The numeric fields stay literal (0.8.6 -> 0.9.0 is major 0, minor 1).
|
|
10
|
+
*/
|
|
3
11
|
export declare function computeSemverDistance(current: string, latest: string): SemverDistance | null;
|
|
4
12
|
export declare function isPreRelease(version: string): boolean;
|
|
5
13
|
/**
|
|
@@ -5,6 +5,14 @@ export function parseSemver(version) {
|
|
|
5
5
|
return null;
|
|
6
6
|
return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])];
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* How far `current` is behind `latest`. `label` is the kind of bump, by the
|
|
10
|
+
* caret rules npm and Cargo apply: below 1.0 a minor bump is breaking (^0.8.6
|
|
11
|
+
* does not admit 0.9.0) and below 0.1 a patch bump is too (^0.0.3 is =0.0.3),
|
|
12
|
+
* so both are labelled "major", the same as upgrade_impact and dependency_check.
|
|
13
|
+
* Until 6.0.2 sqlx 0.8 -> 0.9 and reqwest 0.12 -> 0.13 were labelled "minor".
|
|
14
|
+
* The numeric fields stay literal (0.8.6 -> 0.9.0 is major 0, minor 1).
|
|
15
|
+
*/
|
|
8
16
|
export function computeSemverDistance(current, latest) {
|
|
9
17
|
const c = parseSemver(current);
|
|
10
18
|
const l = parseSemver(latest);
|
|
@@ -17,9 +25,9 @@ export function computeSemverDistance(current, latest) {
|
|
|
17
25
|
if (major > 0)
|
|
18
26
|
label = "major";
|
|
19
27
|
else if (minor > 0)
|
|
20
|
-
label = "minor";
|
|
28
|
+
label = c[0] === 0 ? "major" : "minor";
|
|
21
29
|
else if (patch > 0)
|
|
22
|
-
label = "patch";
|
|
30
|
+
label = c[0] === 0 && c[1] === 0 ? "major" : "patch";
|
|
23
31
|
return { major: Math.max(0, major), minor: Math.max(0, minor), patch: Math.max(0, patch), label };
|
|
24
32
|
}
|
|
25
33
|
export function isPreRelease(version) {
|
package/dist/live/types.d.ts
CHANGED
|
@@ -37,6 +37,12 @@ export interface ResolvedDependency {
|
|
|
37
37
|
installDriftOf?: string;
|
|
38
38
|
/** On drift entries: the command that reinstalls from the lockfile. */
|
|
39
39
|
installFix?: InstallFixCommand;
|
|
40
|
+
/**
|
|
41
|
+
* Set when no lockfile pins this dependency: the requirement the manifest
|
|
42
|
+
* declares ("^1.2.0"). `version` is then that range's floor, NOT an
|
|
43
|
+
* installed version, and the dependency is never sent to OSV as if it were.
|
|
44
|
+
*/
|
|
45
|
+
declaredRange?: string;
|
|
40
46
|
}
|
|
41
47
|
/**
|
|
42
48
|
* One npm direct dependency whose installed copy differs from its lockfile.
|
|
@@ -54,7 +60,8 @@ export interface InstallDriftRecord {
|
|
|
54
60
|
export interface ResolutionSourceRecord {
|
|
55
61
|
/** Absolute path. */
|
|
56
62
|
path: string;
|
|
57
|
-
|
|
63
|
+
/** lockfile = exact versions; manifest = exact requirement pins; declared_ranges = no lockfile, range floors only. */
|
|
64
|
+
kind: "lockfile" | "manifest" | "declared_ranges";
|
|
58
65
|
/** Modification time when it was read, or null if it vanished since. */
|
|
59
66
|
mtimeMs: number | null;
|
|
60
67
|
}
|
|
@@ -88,8 +95,12 @@ export interface OsvVulnerability {
|
|
|
88
95
|
events: Array<{
|
|
89
96
|
introduced?: string;
|
|
90
97
|
fixed?: string;
|
|
98
|
+
last_affected?: string;
|
|
99
|
+
limit?: string;
|
|
91
100
|
}>;
|
|
92
101
|
}>;
|
|
102
|
+
/** Versions listed explicitly, in addition to (or instead of) the ranges. */
|
|
103
|
+
versions?: string[];
|
|
93
104
|
}>;
|
|
94
105
|
references?: Array<{
|
|
95
106
|
type: string;
|
|
@@ -191,6 +202,11 @@ export interface RegistryPackageInfo {
|
|
|
191
202
|
weeklyDownloads: number | null;
|
|
192
203
|
isDev: boolean;
|
|
193
204
|
fetchError: string | null;
|
|
205
|
+
/**
|
|
206
|
+
* No lockfile: the declared requirement ("^1.2.0"); `currentVersion` is then
|
|
207
|
+
* its floor, not an installed version.
|
|
208
|
+
*/
|
|
209
|
+
declaredRange?: string;
|
|
194
210
|
/**
|
|
195
211
|
* The version node_modules actually holds, present only when it differs
|
|
196
212
|
* from `currentVersion` (the lockfile's). Several differing installs across
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Go modules for `upgrade_impact`, from the module proxy (proxy.golang.org):
|
|
3
|
+
* `/@v/list` for the tagged versions, `/@v/<version>.info` for a release's
|
|
4
|
+
* time and VCS origin, and `/@v/<version>.zip` as the archive a changelog is
|
|
5
|
+
* read from (the zip holds the module's files under `<module>@<version>/`).
|
|
6
|
+
*
|
|
7
|
+
* Major versions 2 and up are DIFFERENT modules: `github.com/jackc/pgx/v5`
|
|
8
|
+
* is not a newer release of `github.com/jackc/pgx/v4`, and the proxy lists
|
|
9
|
+
* each path's own versions only. `goMajorPath` gives the path a major lives
|
|
10
|
+
* at, so an upgrade across majors is read from the target module and the
|
|
11
|
+
* report can say that every import path changes.
|
|
12
|
+
*
|
|
13
|
+
* `.info` carries `Origin` (VCS URL, the tag's ref, the module's subdirectory
|
|
14
|
+
* in a multi-module repository) for versions the proxy fetched recently.
|
|
15
|
+
* That, not a guess from the import path, names the GitHub repository and tag
|
|
16
|
+
* for the opt-in release-notes fallback: `go.uber.org/zap` lives at
|
|
17
|
+
* github.com/uber-go/zap, and aws-sdk-go-v2's `service/s3` tags its releases
|
|
18
|
+
* `service/s3/v1.50.0`.
|
|
19
|
+
*/
|
|
20
|
+
import type { PublishedVersion, RegistryIndex, UpgradeNet } from "./upgrade-sources.js";
|
|
21
|
+
/** A module path: a dotted host, then path elements (case kept; the proxy escapes capitals). */
|
|
22
|
+
export declare const GO_MODULE: RegExp;
|
|
23
|
+
/** Proxy escaping: each capital letter becomes `!` + lowercase. */
|
|
24
|
+
export declare function escapeGoPath(path: string): string;
|
|
25
|
+
export interface GoInfo {
|
|
26
|
+
Version?: string;
|
|
27
|
+
Time?: string;
|
|
28
|
+
Origin?: {
|
|
29
|
+
VCS?: string;
|
|
30
|
+
URL?: string;
|
|
31
|
+
Subdir?: string;
|
|
32
|
+
Ref?: string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** The major version a module path carries: `/v5` -> 5, `gopkg.in/yaml.v3` -> 3, none -> null (0 or 1). */
|
|
36
|
+
export declare function goPathMajor(path: string): number | null;
|
|
37
|
+
/** The module path without its major suffix. */
|
|
38
|
+
export declare function goBasePath(path: string): string;
|
|
39
|
+
/** The path major `major` lives at: none for 0 and 1, `/vN` (or gopkg.in's `.vN`) from 2. */
|
|
40
|
+
export declare function goMajorPath(path: string, major: number): string;
|
|
41
|
+
export declare function goVersionMajor(version: string): number | null;
|
|
42
|
+
/** GitHub repository from a module path: `github.com/<owner>/<repo>/...`. */
|
|
43
|
+
export declare function githubRepoOfGoPath(path: string): string | null;
|
|
44
|
+
export declare function goInfo(net: UpgradeNet, module: string, version: string): Promise<GoInfo | null>;
|
|
45
|
+
export declare function loadGoIndex(net: UpgradeNet, module: string): Promise<RegistryIndex | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Publish times for the versions an upgrade crosses (newest first, at most
|
|
48
|
+
* MAX_INFO_READS), and the target's VCS origin. Missing `.info` leaves a null date.
|
|
49
|
+
*/
|
|
50
|
+
export declare function enrichGoVersions(net: UpgradeNet, module: string, versions: PublishedVersion[], target: PublishedVersion | null, budget?: number): Promise<{
|
|
51
|
+
origin: GoInfo["Origin"] | null;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* The tag prefix a module's releases carry: `service/s3` for
|
|
55
|
+
* `refs/tags/service/s3/v1.50.0`, "" for a module at the repository root.
|
|
56
|
+
* From the proxy's recorded ref when there is one; otherwise from a
|
|
57
|
+
* github.com path (the elements after owner/repo, minus a `/vN` suffix,
|
|
58
|
+
* which a major-version subdirectory does not put in its tags).
|
|
59
|
+
*/
|
|
60
|
+
export declare function goTagPrefix(module: string, version: string, origin: GoInfo["Origin"] | null): string;
|
|
61
|
+
/** The proxy's zip of one module version. */
|
|
62
|
+
export declare function goZipUrl(module: string, version: string): string;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Go modules for `upgrade_impact`, from the module proxy (proxy.golang.org):
|
|
4
|
+
* `/@v/list` for the tagged versions, `/@v/<version>.info` for a release's
|
|
5
|
+
* time and VCS origin, and `/@v/<version>.zip` as the archive a changelog is
|
|
6
|
+
* read from (the zip holds the module's files under `<module>@<version>/`).
|
|
7
|
+
*
|
|
8
|
+
* Major versions 2 and up are DIFFERENT modules: `github.com/jackc/pgx/v5`
|
|
9
|
+
* is not a newer release of `github.com/jackc/pgx/v4`, and the proxy lists
|
|
10
|
+
* each path's own versions only. `goMajorPath` gives the path a major lives
|
|
11
|
+
* at, so an upgrade across majors is read from the target module and the
|
|
12
|
+
* report can say that every import path changes.
|
|
13
|
+
*
|
|
14
|
+
* `.info` carries `Origin` (VCS URL, the tag's ref, the module's subdirectory
|
|
15
|
+
* in a multi-module repository) for versions the proxy fetched recently.
|
|
16
|
+
* That, not a guess from the import path, names the GitHub repository and tag
|
|
17
|
+
* for the opt-in release-notes fallback: `go.uber.org/zap` lives at
|
|
18
|
+
* github.com/uber-go/zap, and aws-sdk-go-v2's `service/s3` tags its releases
|
|
19
|
+
* `service/s3/v1.50.0`.
|
|
20
|
+
*/
|
|
21
|
+
const GO_PROXY = "https://proxy.golang.org";
|
|
22
|
+
const GO_TIMEOUT_MS = 15_000;
|
|
23
|
+
/** `.info` reads per call: one per release crossed, newest first, then dates are left null. */
|
|
24
|
+
const MAX_INFO_READS = 40;
|
|
25
|
+
/** A module path: a dotted host, then path elements (case kept; the proxy escapes capitals). */
|
|
26
|
+
export const GO_MODULE = /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}(?:\/[A-Za-z0-9._~+-]+)+$|^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/;
|
|
27
|
+
/** Proxy escaping: each capital letter becomes `!` + lowercase. */
|
|
28
|
+
export function escapeGoPath(path) {
|
|
29
|
+
return path.replace(/[A-Z]/g, (c) => `!${c.toLowerCase()}`);
|
|
30
|
+
}
|
|
31
|
+
/** The major version a module path carries: `/v5` -> 5, `gopkg.in/yaml.v3` -> 3, none -> null (0 or 1). */
|
|
32
|
+
export function goPathMajor(path) {
|
|
33
|
+
const m = /\/v(\d+)$/.exec(path) ?? (/^gopkg\.in\//.test(path) ? /\.v(\d+)$/.exec(path) : null);
|
|
34
|
+
return m ? Number(m[1]) : null;
|
|
35
|
+
}
|
|
36
|
+
/** The module path without its major suffix. */
|
|
37
|
+
export function goBasePath(path) {
|
|
38
|
+
if (/^gopkg\.in\//.test(path))
|
|
39
|
+
return path.replace(/\.v\d+$/, "");
|
|
40
|
+
return path.replace(/\/v\d+$/, "");
|
|
41
|
+
}
|
|
42
|
+
/** The path major `major` lives at: none for 0 and 1, `/vN` (or gopkg.in's `.vN`) from 2. */
|
|
43
|
+
export function goMajorPath(path, major) {
|
|
44
|
+
const base = goBasePath(path);
|
|
45
|
+
if (/^gopkg\.in\//.test(path))
|
|
46
|
+
return `${base}.v${major}`;
|
|
47
|
+
return major >= 2 ? `${base}/v${major}` : base;
|
|
48
|
+
}
|
|
49
|
+
export function goVersionMajor(version) {
|
|
50
|
+
const m = /^v?(\d+)\./.exec(version.trim());
|
|
51
|
+
return m ? Number(m[1]) : null;
|
|
52
|
+
}
|
|
53
|
+
/** GitHub repository from a module path: `github.com/<owner>/<repo>/...`. */
|
|
54
|
+
export function githubRepoOfGoPath(path) {
|
|
55
|
+
const m = /^github\.com\/([\w.-]+)\/([\w.-]+)/.exec(path);
|
|
56
|
+
return m ? `https://github.com/${m[1]}/${m[2]}` : null;
|
|
57
|
+
}
|
|
58
|
+
export async function goInfo(net, module, version) {
|
|
59
|
+
const res = await net.fetch(`${GO_PROXY}/${escapeGoPath(module)}/@v/${escapeGoPath(version)}.info`, {}, GO_TIMEOUT_MS);
|
|
60
|
+
if (!res.ok)
|
|
61
|
+
return null;
|
|
62
|
+
try {
|
|
63
|
+
return (await res.json());
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export async function loadGoIndex(net, module) {
|
|
70
|
+
const base = `${GO_PROXY}/${escapeGoPath(module)}/@v/list`;
|
|
71
|
+
const res = await net.fetch(base, {}, GO_TIMEOUT_MS);
|
|
72
|
+
// The proxy answers 404 and 410 for a path it cannot resolve.
|
|
73
|
+
if (res.status === 404 || res.status === 410)
|
|
74
|
+
return null;
|
|
75
|
+
if (!res.ok)
|
|
76
|
+
throw new Error(`Go module proxy HTTP ${res.status} for ${module}`);
|
|
77
|
+
const listed = (await res.text())
|
|
78
|
+
.split("\n")
|
|
79
|
+
.map((v) => v.trim())
|
|
80
|
+
.filter((v) => /^v\d+\.\d+\.\d+/.test(v));
|
|
81
|
+
let versions = listed.map((version) => ({ version, published: null }));
|
|
82
|
+
if (versions.length === 0) {
|
|
83
|
+
// An untagged module: the proxy's @latest is a pseudo-version.
|
|
84
|
+
const latest = await net.fetch(`${GO_PROXY}/${escapeGoPath(module)}/@latest`, {}, GO_TIMEOUT_MS);
|
|
85
|
+
if (!latest.ok)
|
|
86
|
+
return null;
|
|
87
|
+
const info = (await latest.json());
|
|
88
|
+
if (!info.Version)
|
|
89
|
+
return null;
|
|
90
|
+
versions = [{ version: info.Version, published: info.Time ?? null }];
|
|
91
|
+
}
|
|
92
|
+
return { name: module, ecosystem: "Go", versions, repository: githubRepoOfGoPath(module) };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Publish times for the versions an upgrade crosses (newest first, at most
|
|
96
|
+
* MAX_INFO_READS), and the target's VCS origin. Missing `.info` leaves a null date.
|
|
97
|
+
*/
|
|
98
|
+
export async function enrichGoVersions(net, module, versions, target, budget = MAX_INFO_READS) {
|
|
99
|
+
const rest = [...versions].reverse().filter((v) => v !== target && !v.published);
|
|
100
|
+
const wanted = (target ? [target, ...rest] : rest).slice(0, budget);
|
|
101
|
+
let origin = null;
|
|
102
|
+
await Promise.all(wanted.map(async (v) => {
|
|
103
|
+
const info = await goInfo(net, module, v.version).catch(() => null);
|
|
104
|
+
if (info?.Time && !v.published)
|
|
105
|
+
v.published = info.Time;
|
|
106
|
+
if (v === target && info?.Origin)
|
|
107
|
+
origin = info.Origin;
|
|
108
|
+
}));
|
|
109
|
+
return { origin };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The tag prefix a module's releases carry: `service/s3` for
|
|
113
|
+
* `refs/tags/service/s3/v1.50.0`, "" for a module at the repository root.
|
|
114
|
+
* From the proxy's recorded ref when there is one; otherwise from a
|
|
115
|
+
* github.com path (the elements after owner/repo, minus a `/vN` suffix,
|
|
116
|
+
* which a major-version subdirectory does not put in its tags).
|
|
117
|
+
*/
|
|
118
|
+
export function goTagPrefix(module, version, origin) {
|
|
119
|
+
const ref = origin?.Ref?.replace(/^refs\/tags\//, "");
|
|
120
|
+
if (ref && ref.endsWith(version))
|
|
121
|
+
return ref.slice(0, ref.length - version.length).replace(/\/$/, "");
|
|
122
|
+
if (origin?.Subdir !== undefined)
|
|
123
|
+
return origin.Subdir.replace(/(?:^|\/)v\d+$/, "");
|
|
124
|
+
const m = /^github\.com\/[\w.-]+\/[\w.-]+\/(.+)$/.exec(goBasePath(module));
|
|
125
|
+
return m ? m[1] : "";
|
|
126
|
+
}
|
|
127
|
+
/** The proxy's zip of one module version. */
|
|
128
|
+
export function goZipUrl(module, version) {
|
|
129
|
+
return `${GO_PROXY}/${escapeGoPath(module)}/@v/${escapeGoPath(version)}.zip`;
|
|
130
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PyPI for `upgrade_impact`: the version index from the PyPI JSON API
|
|
3
|
+
* (`https://pypi.org/pypi/<name>/json`), each release's sdist as the archive
|
|
4
|
+
* its changelog is read from (files.pythonhosted.org, PyPI's own file host),
|
|
5
|
+
* and the links the project itself publishes in `project_urls`.
|
|
6
|
+
*
|
|
7
|
+
* Versions are PEP 440, not semver (`2.0`, `3.0.0rc1`, `1.0.post1`), so they
|
|
8
|
+
* are ordered with version-compare.ts's PEP 440 comparator. A release's
|
|
9
|
+
* publish time is its earliest file upload; it is yanked when every file is.
|
|
10
|
+
*
|
|
11
|
+
* `project_urls` is metadata, not a changelog: a "Changelog" link usually
|
|
12
|
+
* points at an HTML docs page this server does not parse (it is reported as
|
|
13
|
+
* the release-notes URL). Only a link to a raw changelog FILE on GitHub can be
|
|
14
|
+
* read, and only when the GitHub fallback is enabled (github-releases.ts).
|
|
15
|
+
*/
|
|
16
|
+
import type { RegistryIndex, UpgradeNet } from "./upgrade-sources.js";
|
|
17
|
+
interface PyPIFile {
|
|
18
|
+
url?: string;
|
|
19
|
+
packagetype?: string;
|
|
20
|
+
filename?: string;
|
|
21
|
+
upload_time_iso_8601?: string;
|
|
22
|
+
yanked?: boolean;
|
|
23
|
+
yanked_reason?: string | null;
|
|
24
|
+
}
|
|
25
|
+
interface PyPIProject {
|
|
26
|
+
info?: {
|
|
27
|
+
name?: string;
|
|
28
|
+
home_page?: string | null;
|
|
29
|
+
project_urls?: Record<string, string> | null;
|
|
30
|
+
};
|
|
31
|
+
releases?: Record<string, PyPIFile[]>;
|
|
32
|
+
}
|
|
33
|
+
/** PEP 503 normalised name: lowercase, runs of `-`, `_`, `.` read as one `-`. */
|
|
34
|
+
export declare function normalizePypiName(name: string): string;
|
|
35
|
+
/** PEP 508 distribution name. */
|
|
36
|
+
export declare const PYPI_NAME: RegExp;
|
|
37
|
+
/** The project's source repository from `project_urls` / `home_page`: a GitHub URL first, then any https one. */
|
|
38
|
+
export declare function pypiRepository(info: PyPIProject["info"]): string | null;
|
|
39
|
+
/** Links the project labels as its changelog / release notes, in `project_urls` order. */
|
|
40
|
+
export declare function pypiChangelogLinks(info: PyPIProject["info"]): string[];
|
|
41
|
+
export declare function loadPypiIndex(net: UpgradeNet, name: string): Promise<RegistryIndex | null>;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* PyPI for `upgrade_impact`: the version index from the PyPI JSON API
|
|
4
|
+
* (`https://pypi.org/pypi/<name>/json`), each release's sdist as the archive
|
|
5
|
+
* its changelog is read from (files.pythonhosted.org, PyPI's own file host),
|
|
6
|
+
* and the links the project itself publishes in `project_urls`.
|
|
7
|
+
*
|
|
8
|
+
* Versions are PEP 440, not semver (`2.0`, `3.0.0rc1`, `1.0.post1`), so they
|
|
9
|
+
* are ordered with version-compare.ts's PEP 440 comparator. A release's
|
|
10
|
+
* publish time is its earliest file upload; it is yanked when every file is.
|
|
11
|
+
*
|
|
12
|
+
* `project_urls` is metadata, not a changelog: a "Changelog" link usually
|
|
13
|
+
* points at an HTML docs page this server does not parse (it is reported as
|
|
14
|
+
* the release-notes URL). Only a link to a raw changelog FILE on GitHub can be
|
|
15
|
+
* read, and only when the GitHub fallback is enabled (github-releases.ts).
|
|
16
|
+
*/
|
|
17
|
+
import { parsePep440 } from "./version-compare.js";
|
|
18
|
+
const PYPI_TIMEOUT_MS = 30_000;
|
|
19
|
+
/** PEP 503 normalised name: lowercase, runs of `-`, `_`, `.` read as one `-`. */
|
|
20
|
+
export function normalizePypiName(name) {
|
|
21
|
+
return name.toLowerCase().replace(/[-_.]+/g, "-");
|
|
22
|
+
}
|
|
23
|
+
/** PEP 508 distribution name. */
|
|
24
|
+
export const PYPI_NAME = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,198}[A-Za-z0-9])?$/;
|
|
25
|
+
/** The sdist to read a changelog from: a `.tar.gz` first, then a `.zip`. */
|
|
26
|
+
function pickSdist(files) {
|
|
27
|
+
const sdists = files.filter((f) => f.packagetype === "sdist" && typeof f.url === "string");
|
|
28
|
+
return (sdists.find((f) => /\.tar\.gz$/i.test(f.url)) ?? sdists.find((f) => /\.zip$/i.test(f.url)))?.url;
|
|
29
|
+
}
|
|
30
|
+
const REPO_KEY = /^(?:source(?: code)?|repository|repo|code|github|git|homepage|home|project)$/i;
|
|
31
|
+
const CHANGELOG_KEY = /change ?log|changes|release ?notes|releases|history|news|what'?s ?new/i;
|
|
32
|
+
/** The project's source repository from `project_urls` / `home_page`: a GitHub URL first, then any https one. */
|
|
33
|
+
export function pypiRepository(info) {
|
|
34
|
+
const urls = Object.entries(info?.project_urls ?? {}).filter(([, v]) => typeof v === "string");
|
|
35
|
+
const github = (v) => /^https?:\/\/(?:www\.)?github\.com\/[\w.-]+\/[\w.-]+/i.test(v);
|
|
36
|
+
const keyed = urls.filter(([k]) => REPO_KEY.test(k.trim()));
|
|
37
|
+
const pick = keyed.find(([, v]) => github(v))?.[1] ??
|
|
38
|
+
urls.find(([, v]) => github(v))?.[1] ??
|
|
39
|
+
(info?.home_page && github(info.home_page) ? info.home_page : null) ??
|
|
40
|
+
keyed.find(([, v]) => /^https:\/\//i.test(v))?.[1] ??
|
|
41
|
+
null;
|
|
42
|
+
return pick;
|
|
43
|
+
}
|
|
44
|
+
/** Links the project labels as its changelog / release notes, in `project_urls` order. */
|
|
45
|
+
export function pypiChangelogLinks(info) {
|
|
46
|
+
return Object.entries(info?.project_urls ?? {})
|
|
47
|
+
.filter(([k, v]) => typeof v === "string" && CHANGELOG_KEY.test(k) && /^https:\/\//i.test(v))
|
|
48
|
+
.map(([, v]) => v);
|
|
49
|
+
}
|
|
50
|
+
export async function loadPypiIndex(net, name) {
|
|
51
|
+
const url = `https://pypi.org/pypi/${encodeURIComponent(normalizePypiName(name))}/json`;
|
|
52
|
+
const res = await net.fetch(url, { headers: { Accept: "application/json" } }, PYPI_TIMEOUT_MS);
|
|
53
|
+
if (res.status === 404)
|
|
54
|
+
return null;
|
|
55
|
+
if (!res.ok)
|
|
56
|
+
throw new Error(`PyPI HTTP ${res.status} for ${name}`);
|
|
57
|
+
const doc = (await res.json());
|
|
58
|
+
if (!doc.releases || typeof doc.releases !== "object")
|
|
59
|
+
throw new Error(`PyPI returned no release list for ${name}`);
|
|
60
|
+
const versions = [];
|
|
61
|
+
for (const [version, files] of Object.entries(doc.releases)) {
|
|
62
|
+
if (!parsePep440(version))
|
|
63
|
+
continue;
|
|
64
|
+
const list = Array.isArray(files) ? files : [];
|
|
65
|
+
const times = list.map((f) => f.upload_time_iso_8601).filter((t) => typeof t === "string").sort();
|
|
66
|
+
const entry = { version, published: times[0] ?? null };
|
|
67
|
+
if (list.length > 0 && list.every((f) => f.yanked)) {
|
|
68
|
+
entry.yanked = true;
|
|
69
|
+
const reason = list.find((f) => f.yanked_reason)?.yanked_reason;
|
|
70
|
+
if (reason)
|
|
71
|
+
entry.deprecated = `yanked: ${reason}`;
|
|
72
|
+
}
|
|
73
|
+
const sdist = pickSdist(list);
|
|
74
|
+
if (sdist)
|
|
75
|
+
entry.tarball = sdist;
|
|
76
|
+
versions.push(entry);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
name: doc.info?.name ?? name,
|
|
80
|
+
ecosystem: "PyPI",
|
|
81
|
+
versions,
|
|
82
|
+
repository: pypiRepository(doc.info),
|
|
83
|
+
changelogLinks: pypiChangelogLinks(doc.info),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
* Network sources for `upgrade_impact`: registry version index, the target
|
|
3
3
|
* version's changelog (read from its registry archive) and OSV advisories.
|
|
4
4
|
*
|
|
5
|
-
* Privacy rule (Decision 1, option A): the only hosts contacted
|
|
6
|
-
* package's own registry (registry.npmjs.org; crates.io +
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
5
|
+
* Privacy rule (Decision 1, option A): by default the only hosts contacted
|
|
6
|
+
* are the package's own registry (registry.npmjs.org; crates.io +
|
|
7
|
+
* static.crates.io; pypi.org + files.pythonhosted.org; proxy.golang.org) and
|
|
8
|
+
* api.osv.dev. A changelog is read from GitHub only when the user opts in
|
|
9
|
+
* (Decision 1, option B: FOURDA_GITHUB_CHANGELOG, github-releases.ts) and the
|
|
10
|
+
* archive has none for the range; otherwise the report hands the agent a
|
|
11
|
+
* `release_notes_url` built from registry metadata and says how to enable the
|
|
12
|
+
* fallback. Every request goes through `UpgradeNet`, which records the host
|
|
13
|
+
* so `_meta.sources` is a fact, not a claim, and which tests replace with a
|
|
14
|
+
* fake fetch.
|
|
13
15
|
*
|
|
14
16
|
* Request budget per call: index (1) + archive (1) + OSV for from/to (2) = 4,
|
|
15
17
|
* each cached (index 6 h, parsed changelog 7 d, OSV 1 h). The npm packument
|
|
@@ -17,8 +19,17 @@
|
|
|
17
19
|
* is cached — never the raw document.
|
|
18
20
|
*/
|
|
19
21
|
import { type FetchFn } from "./package-archive.js";
|
|
20
|
-
import { type ChangelogSection } from "./changelog.js";
|
|
21
|
-
export type UpgradeEcosystem = "npm" | "crates.io";
|
|
22
|
+
import { type ChangelogSection, type VersionOrder } from "./changelog.js";
|
|
23
|
+
export type UpgradeEcosystem = "npm" | "crates.io" | "PyPI" | "Go";
|
|
24
|
+
export declare const UPGRADE_ECOSYSTEMS: readonly UpgradeEcosystem[];
|
|
25
|
+
/** Version order for an ecosystem: semver precedence (npm, crates.io, Go without its `v`), PEP 440 (PyPI). Null = unreadable. */
|
|
26
|
+
export declare function versionOrder(ecosystem: UpgradeEcosystem): VersionOrder;
|
|
27
|
+
/** Readable as a version in this ecosystem. */
|
|
28
|
+
export declare function isReadableVersion(version: string, ecosystem: UpgradeEcosystem): boolean;
|
|
29
|
+
/** A prerelease in this ecosystem's own terms (PEP 440 `a`/`b`/`rc`/`.dev`; semver `-x`; a Go pseudo-version). */
|
|
30
|
+
export declare function isPrerelease(version: string, ecosystem: UpgradeEcosystem): boolean;
|
|
31
|
+
/** Package-name identity: crates.io folds `-`/`_`, PyPI normalises per PEP 503, npm and Go are exact. */
|
|
32
|
+
export declare function sameUpgradeName(a: string, b: string, ecosystem: string): boolean;
|
|
22
33
|
/** crates.io refuses API requests without a descriptive User-Agent. */
|
|
23
34
|
export declare const CRATES_USER_AGENT = "4da-mcp-server (https://4da.ai; upgrade_impact)";
|
|
24
35
|
export interface PublishedVersion {
|
|
@@ -26,7 +37,7 @@ export interface PublishedVersion {
|
|
|
26
37
|
published: string | null;
|
|
27
38
|
deprecated?: string;
|
|
28
39
|
yanked?: boolean;
|
|
29
|
-
/** Archive URL (npm `dist.tarball
|
|
40
|
+
/** Archive URL (npm `dist.tarball`, PyPI sdist); crates and Go archives are derived from name + version. */
|
|
30
41
|
tarball?: string;
|
|
31
42
|
}
|
|
32
43
|
export interface RegistryIndex {
|
|
@@ -35,12 +46,18 @@ export interface RegistryIndex {
|
|
|
35
46
|
ecosystem: UpgradeEcosystem;
|
|
36
47
|
versions: PublishedVersion[];
|
|
37
48
|
repository: string | null;
|
|
49
|
+
/** Links the project itself labels as its changelog (PyPI `project_urls`). */
|
|
50
|
+
changelogLinks?: string[];
|
|
38
51
|
}
|
|
39
52
|
export interface ChangelogResult {
|
|
40
53
|
found: boolean;
|
|
41
54
|
file?: string;
|
|
42
55
|
sections?: ChangelogSection[];
|
|
43
56
|
reason?: string;
|
|
57
|
+
/** Where the sections came from: the registry archive (default), or the opt-in GitHub fallback. */
|
|
58
|
+
source?: "archive" | "github-releases" | "github-file";
|
|
59
|
+
/** The page or API the GitHub fallback read. */
|
|
60
|
+
url?: string;
|
|
44
61
|
}
|
|
45
62
|
/** The cache surface used here; `LiveCache` satisfies it. */
|
|
46
63
|
export interface UpgradeCache {
|
|
@@ -59,6 +76,8 @@ export declare function isValidPackageName(name: string, ecosystem: UpgradeEcosy
|
|
|
59
76
|
export declare function getRegistryIndex(net: UpgradeNet, ecosystem: UpgradeEcosystem, name: string): Promise<RegistryIndex | null>;
|
|
60
77
|
/** The registry archive URL for one version, or null when it is not on the package's own registry. */
|
|
61
78
|
export declare function archiveUrl(index: RegistryIndex, version: PublishedVersion): string | null;
|
|
79
|
+
/** Cache key of one version's parsed changelog; it carries CHANGELOG_PARSER_VERSION. */
|
|
80
|
+
export declare function changelogCacheKey(ecosystem: string, name: string, version: string): string;
|
|
62
81
|
/** Parsed changelog shipped inside one version's archive (cached 7 days per version). */
|
|
63
82
|
export declare function getChangelog(net: UpgradeNet, index: RegistryIndex, version: PublishedVersion): Promise<ChangelogResult>;
|
|
64
83
|
/** OSV advisory ids affecting one version, or null when OSV could not be reached. */
|