@4da/mcp-server 6.2.2 → 6.2.3
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
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.2.3 — 2026-10-11
|
|
4
|
+
|
|
5
|
+
### Yarn 1 refresh instruction covers alias entries
|
|
6
|
+
|
|
7
|
+
The yarn 1 refresh step said "delete the `<name>@` entries from yarn.lock". A
|
|
8
|
+
yarn 1 block is keyed by every spec that resolved to it, and an alias block's
|
|
9
|
+
first key names the alias (`"string-width-cjs@npm:string-width@^4.2.0"`), so
|
|
10
|
+
following the step left those blocks and kept the vulnerable version. It now
|
|
11
|
+
names both forms: keys `"<name>@…"` and alias keys `"…@npm:<name>@…"`.
|
|
12
|
+
|
|
13
|
+
### Fix-path parents name the real package
|
|
14
|
+
|
|
15
|
+
The parent reader behind the fix-path rule ("a lockfile refresh is enough"
|
|
16
|
+
vs "the parent must move") parsed lockfiles with its own code and still had
|
|
17
|
+
the two naming defects 6.2.2 fixed in the package readers. It now names
|
|
18
|
+
packages with the readers' helpers.
|
|
19
|
+
|
|
20
|
+
- **yarn (v1 and berry): npm aliases.** A parent installed under an alias was
|
|
21
|
+
reported by the alias: on hono's benchmarks/routers lockfile, string-width
|
|
22
|
+
4.2.3 and wrap-ansi 7.0.0 were named `string-width-cjs` and `wrap-ansi-cjs`.
|
|
23
|
+
An aliased dependency edge (`string-width-cjs: "npm:string-width@^4.2.0"`)
|
|
24
|
+
was never matched, so @isaacs/cliui was missing from string-width 4.2.3's
|
|
25
|
+
parents, and its range is now the real package's `^4.2.0`.
|
|
26
|
+
- **pnpm 5: entries keyed by a tarball URL.** Such an entry was dropped as a
|
|
27
|
+
parent and as a dependency: trpc's tarball-keyed @prisma/client 4.7.1 was
|
|
28
|
+
missing from prisma 4.7.1's parents, and the four workspace projects that
|
|
29
|
+
depend on it were missing from @prisma/client's. A dependency value that
|
|
30
|
+
points at another entry (that tarball key, or a pnpm alias's
|
|
31
|
+
`/real/1.2.3`) is now an edge to the package it installs.
|
|
32
|
+
|
|
3
33
|
## 6.2.2 — 2026-10-11
|
|
4
34
|
|
|
5
35
|
### Lockfile readers agree with the 4DA conformance corpus
|
package/dist/live/fix-paths.js
CHANGED
|
@@ -312,7 +312,10 @@ export function refreshCommand(format, name, installed, target) {
|
|
|
312
312
|
case "yarn-berry":
|
|
313
313
|
return `yarn up -R ${name}`;
|
|
314
314
|
case "yarn":
|
|
315
|
-
|
|
315
|
+
// Yarn 1 has no command that refreshes a transitive copy. Its blocks are keyed
|
|
316
|
+
// by every spec that resolved to them, and an alias block's first key names
|
|
317
|
+
// the alias ("string-width-cjs@npm:string-width@^4.2.0"), so name both forms.
|
|
318
|
+
return `delete every yarn.lock entry that resolves to ${name} (keys "${name}@…" and alias keys "…@npm:${name}@…"), then run yarn install`;
|
|
316
319
|
case "bun":
|
|
317
320
|
return `delete the ${name} entries from bun.lock, then run bun install`;
|
|
318
321
|
case "cargo":
|
|
@@ -17,12 +17,30 @@
|
|
|
17
17
|
* file, link) is dropped instead of being queried as a package name.
|
|
18
18
|
*/
|
|
19
19
|
import type { InstanceSet } from "./lockfile-types.js";
|
|
20
|
+
/** An exact version with pnpm's peer suffixes removed: v6/v9 `1.2.3(react@18)`, v5 `1.2.3_react@18`. */
|
|
21
|
+
export declare function exactVersion(raw: string): string | null;
|
|
20
22
|
/**
|
|
21
23
|
* Splits one `packages:` or `snapshots:` key into [name, version]:
|
|
22
24
|
* v9 `'@scope/pkg@1.2.3(peer@4.5.6)'`, v6 `/pkg@1.2.3`, v5 `/@scope/pkg/1.2.3_peer@4.5.6`.
|
|
23
25
|
* Returns null for a key that does not pin a registry version.
|
|
24
26
|
*/
|
|
25
27
|
export declare function parsePnpmKey(rawKey: string): [string, string] | null;
|
|
28
|
+
/** A `packages:`/`snapshots:` key, or a dependency value that points at one, in one spelling: unquoted, no leading `/`. */
|
|
29
|
+
export declare function pnpmKeyRef(raw: string): string;
|
|
30
|
+
export interface PnpmEntryReader {
|
|
31
|
+
/** One line inside a `packages:` or `snapshots:` section (indent > 0). */
|
|
32
|
+
line(indent: number, text: string): void;
|
|
33
|
+
/** Ends the entry being read: call at every section change and at the end of the file. */
|
|
34
|
+
finish(): void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reads `packages:`/`snapshots:` entries line by line and reports the
|
|
38
|
+
* registry (name, version) each installs (see pnpmEntryIdentity); entries
|
|
39
|
+
* that install no registry release are not reported.
|
|
40
|
+
*/
|
|
41
|
+
export declare function pnpmEntryReader(onEntry: (key: string, id: [string, string], dev: boolean | undefined) => void): PnpmEntryReader;
|
|
42
|
+
/** Every `packages:`/`snapshots:` entry's registry (name, version), by pnpmKeyRef of its key. */
|
|
43
|
+
export declare function pnpmEntryIdentities(content: string): Map<string, [string, string]>;
|
|
26
44
|
/**
|
|
27
45
|
* Reads a pnpm lockfile (v5.x, v6 or v9) into `versions`; the first version seen wins.
|
|
28
46
|
*
|
|
@@ -37,6 +55,15 @@ export declare function parsePnpmKey(rawKey: string): [string, string] | null;
|
|
|
37
55
|
* package key. pnpm 9 dropped that field, so its copies have unknown scope.
|
|
38
56
|
*/
|
|
39
57
|
export declare function readPnpmLock(content: string, versions: Map<string, string>, instances?: InstanceSet): void;
|
|
58
|
+
/**
|
|
59
|
+
* The registry package a yarn block installs. An npm alias
|
|
60
|
+
* (`"string-width-cjs": "npm:string-width@^4.2.0"`) keys the block by the
|
|
61
|
+
* alias, which is not a package: OSV knows `string-width`, so the alias was
|
|
62
|
+
* queried for nothing and the real package went unscanned (hono's
|
|
63
|
+
* benchmarks/routers yarn.lock, 4DA conformance corpus, 2026-10-12). berry
|
|
64
|
+
* names the real package in `resolution:`; v1 only in the alias spec.
|
|
65
|
+
*/
|
|
66
|
+
export declare function yarnPackageName(block: string, firstLine: string, headerName: string): string;
|
|
40
67
|
/**
|
|
41
68
|
* Reads a yarn lockfile (v1 or berry) into `versions`.
|
|
42
69
|
* v1: `"@scope/pkg@^1.0.0", "@scope/pkg@^1.1.0":` then ` version "1.2.3"`.
|
|
@@ -24,7 +24,7 @@ function unquote(s) {
|
|
|
24
24
|
return s.length >= 2 && (s[0] === "'" || s[0] === '"') && s[s.length - 1] === s[0] ? s.slice(1, -1) : s;
|
|
25
25
|
}
|
|
26
26
|
/** An exact version with pnpm's peer suffixes removed: v6/v9 `1.2.3(react@18)`, v5 `1.2.3_react@18`. */
|
|
27
|
-
function exactVersion(raw) {
|
|
27
|
+
export function exactVersion(raw) {
|
|
28
28
|
const version = unquote(raw.trim()).split("(")[0].split("_")[0].trim();
|
|
29
29
|
return /^\d/.test(version) ? version : null;
|
|
30
30
|
}
|
|
@@ -116,6 +116,60 @@ function pnpmEntryIdentity(entry) {
|
|
|
116
116
|
}
|
|
117
117
|
return parsed;
|
|
118
118
|
}
|
|
119
|
+
/** A `packages:`/`snapshots:` key, or a dependency value that points at one, in one spelling: unquoted, no leading `/`. */
|
|
120
|
+
export function pnpmKeyRef(raw) {
|
|
121
|
+
return unquote(raw.trim()).replace(/^\//, "");
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Reads `packages:`/`snapshots:` entries line by line and reports the
|
|
125
|
+
* registry (name, version) each installs (see pnpmEntryIdentity); entries
|
|
126
|
+
* that install no registry release are not reported.
|
|
127
|
+
*/
|
|
128
|
+
export function pnpmEntryReader(onEntry) {
|
|
129
|
+
let entry = null;
|
|
130
|
+
const finish = () => {
|
|
131
|
+
const id = entry ? pnpmEntryIdentity(entry) : null;
|
|
132
|
+
if (entry && id)
|
|
133
|
+
onEntry(entry.key, id, entry.dev);
|
|
134
|
+
entry = null;
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
line(indent, text) {
|
|
138
|
+
if (indent === 2 && text.endsWith(":")) {
|
|
139
|
+
finish();
|
|
140
|
+
entry = { key: text.slice(0, -1), inResolution: false, local: false };
|
|
141
|
+
}
|
|
142
|
+
else if (entry && indent === 4) {
|
|
143
|
+
pnpmEntryField(entry, text);
|
|
144
|
+
}
|
|
145
|
+
else if (entry?.inResolution && indent > 4) {
|
|
146
|
+
pnpmResolutionText(entry, text);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
finish,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** Every `packages:`/`snapshots:` entry's registry (name, version), by pnpmKeyRef of its key. */
|
|
153
|
+
export function pnpmEntryIdentities(content) {
|
|
154
|
+
const ids = new Map();
|
|
155
|
+
const reader = pnpmEntryReader((key, id) => ids.set(pnpmKeyRef(key), id));
|
|
156
|
+
let inEntries = false;
|
|
157
|
+
for (const line of content.split(/\r?\n/)) {
|
|
158
|
+
const text = line.trim();
|
|
159
|
+
if (text === "" || text.startsWith("#"))
|
|
160
|
+
continue;
|
|
161
|
+
const indent = line.length - line.trimStart().length;
|
|
162
|
+
if (indent === 0) {
|
|
163
|
+
reader.finish();
|
|
164
|
+
inEntries = text === "packages:" || text === "snapshots:";
|
|
165
|
+
}
|
|
166
|
+
else if (inEntries) {
|
|
167
|
+
reader.line(indent, text);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
reader.finish();
|
|
171
|
+
return ids;
|
|
172
|
+
}
|
|
119
173
|
/**
|
|
120
174
|
* Reads a pnpm lockfile (v5.x, v6 or v9) into `versions`; the first version seen wins.
|
|
121
175
|
*
|
|
@@ -139,39 +193,25 @@ export function readPnpmLock(content, versions, instances) {
|
|
|
139
193
|
let section = "";
|
|
140
194
|
let blockIndent = -1; // column of the dependency block being read, or -1
|
|
141
195
|
let pending = null; // v6/v9: a name whose `version:` line follows
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (entry.dev !== undefined)
|
|
148
|
-
devByKey.set(`${id[0]}\0${id[1]}`, entry.dev);
|
|
149
|
-
}
|
|
150
|
-
entry = null;
|
|
151
|
-
};
|
|
196
|
+
const entries = pnpmEntryReader((_key, id, dev) => {
|
|
197
|
+
transitive.push(id);
|
|
198
|
+
if (dev !== undefined)
|
|
199
|
+
devByKey.set(`${id[0]}\0${id[1]}`, dev);
|
|
200
|
+
});
|
|
152
201
|
for (const line of content.split(/\r?\n/)) {
|
|
153
202
|
const text = line.trim();
|
|
154
203
|
if (text === "" || text.startsWith("#"))
|
|
155
204
|
continue;
|
|
156
205
|
const indent = line.length - line.trimStart().length;
|
|
157
206
|
if (indent === 0) {
|
|
158
|
-
finish();
|
|
207
|
+
entries.finish();
|
|
159
208
|
section = text.endsWith(":") ? text.slice(0, -1) : "";
|
|
160
209
|
blockIndent = DEPENDENCY_BLOCKS.has(text) ? 0 : -1;
|
|
161
210
|
pending = null;
|
|
162
211
|
continue;
|
|
163
212
|
}
|
|
164
213
|
if (section === "packages" || section === "snapshots") {
|
|
165
|
-
|
|
166
|
-
finish();
|
|
167
|
-
entry = { key: text.slice(0, -1), inResolution: false, local: false };
|
|
168
|
-
}
|
|
169
|
-
else if (entry && indent === 4) {
|
|
170
|
-
pnpmEntryField(entry, text);
|
|
171
|
-
}
|
|
172
|
-
else if (entry?.inResolution && indent > 4) {
|
|
173
|
-
pnpmResolutionText(entry, text);
|
|
174
|
-
}
|
|
214
|
+
entries.line(indent, text);
|
|
175
215
|
continue;
|
|
176
216
|
}
|
|
177
217
|
if (section === "importers" && indent <= 4) {
|
|
@@ -204,7 +244,7 @@ export function readPnpmLock(content, versions, instances) {
|
|
|
204
244
|
pending = null;
|
|
205
245
|
}
|
|
206
246
|
}
|
|
207
|
-
finish();
|
|
247
|
+
entries.finish();
|
|
208
248
|
for (const [name, version] of transitive)
|
|
209
249
|
setFirst(name, version);
|
|
210
250
|
if (!instances)
|
|
@@ -224,7 +264,7 @@ export function readPnpmLock(content, versions, instances) {
|
|
|
224
264
|
* benchmarks/routers yarn.lock, 4DA conformance corpus, 2026-10-12). berry
|
|
225
265
|
* names the real package in `resolution:`; v1 only in the alias spec.
|
|
226
266
|
*/
|
|
227
|
-
function yarnPackageName(block, firstLine, headerName) {
|
|
267
|
+
export function yarnPackageName(block, firstLine, headerName) {
|
|
228
268
|
const resolution = /^\s+resolution:\s+"?((?:@[^\s/@"]+\/)?[^\s/@"]+)@npm:/m.exec(block);
|
|
229
269
|
if (resolution)
|
|
230
270
|
return resolution[1];
|
|
@@ -45,8 +45,20 @@ export declare function packageLockParents(lock: {
|
|
|
45
45
|
* dependency blocks resolve `name` to `version`. pnpm records resolved
|
|
46
46
|
* versions only, so `range` is null. Importers (the project's own manifests)
|
|
47
47
|
* become local edges with their `specifier` when the lockfile records one.
|
|
48
|
+
*
|
|
49
|
+
* Entries and dependency values are named the way the package reader names
|
|
50
|
+
* them (pnpmEntryIdentities): a pnpm 5 entry keyed by its tarball URL is the
|
|
51
|
+
* package its `name:`/`version:` fields say, and a dependency value that is a
|
|
52
|
+
* key rather than a version (that tarball key, or an alias's
|
|
53
|
+
* `/real/1.2.3`, `/real@1.2.3`, `real@1.2.3`) is an edge to the package it points at.
|
|
48
54
|
*/
|
|
49
55
|
export declare function pnpmParents(content: string, name: string, version: string): ParentEdge[];
|
|
56
|
+
/**
|
|
57
|
+
* Blocks whose dependencies resolve `name` to `version`. An npm alias edge
|
|
58
|
+
* (`string-width-cjs: "npm:string-width@^4.2.0"`) is an edge to the real
|
|
59
|
+
* package, with the real package's range; it resolves through the block whose
|
|
60
|
+
* spec is `string-width-cjs@npm:string-width@^4.2.0`.
|
|
61
|
+
*/
|
|
50
62
|
export declare function yarnParents(content: string, name: string, version: string): ParentEdge[];
|
|
51
63
|
export declare function bunParents(lock: {
|
|
52
64
|
packages?: Record<string, unknown>;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import * as fs from "node:fs";
|
|
12
12
|
import * as path from "node:path";
|
|
13
13
|
import { stripTrailingCommas } from "./bun-lockfile.js";
|
|
14
|
-
import { parsePnpmKey } from "./js-lockfile-readers.js";
|
|
14
|
+
import { exactVersion, parsePnpmKey, pnpmEntryIdentities, pnpmKeyRef, yarnPackageName } from "./js-lockfile-readers.js";
|
|
15
15
|
import { packageNameFromNodeModulesPath, parseCargoLockPackages } from "./lockfile-parsers.js";
|
|
16
16
|
const DEP_FIELDS = ["dependencies", "optionalDependencies", "peerDependencies"];
|
|
17
17
|
/** The lockfile format of a path, or null for a file this module does not read. */
|
|
@@ -123,18 +123,32 @@ export function packageLockParents(lock, name, version) {
|
|
|
123
123
|
// ---------------------------------------------------------------------------
|
|
124
124
|
// pnpm-lock.yaml (v5, v6, v9)
|
|
125
125
|
// ---------------------------------------------------------------------------
|
|
126
|
-
function exactVersion(raw) {
|
|
127
|
-
const unquoted = raw.trim().replace(/^['"]|['"]$/g, "");
|
|
128
|
-
const v = unquoted.split("(")[0].split("_")[0].trim();
|
|
129
|
-
return /^\d/.test(v) ? v : null;
|
|
130
|
-
}
|
|
131
126
|
/**
|
|
132
127
|
* Parents from `packages:` (v5/v6) or `snapshots:` (v9) entries whose
|
|
133
128
|
* dependency blocks resolve `name` to `version`. pnpm records resolved
|
|
134
129
|
* versions only, so `range` is null. Importers (the project's own manifests)
|
|
135
130
|
* become local edges with their `specifier` when the lockfile records one.
|
|
131
|
+
*
|
|
132
|
+
* Entries and dependency values are named the way the package reader names
|
|
133
|
+
* them (pnpmEntryIdentities): a pnpm 5 entry keyed by its tarball URL is the
|
|
134
|
+
* package its `name:`/`version:` fields say, and a dependency value that is a
|
|
135
|
+
* key rather than a version (that tarball key, or an alias's
|
|
136
|
+
* `/real/1.2.3`, `/real@1.2.3`, `real@1.2.3`) is an edge to the package it points at.
|
|
136
137
|
*/
|
|
137
138
|
export function pnpmParents(content, name, version) {
|
|
139
|
+
const ids = pnpmEntryIdentities(content);
|
|
140
|
+
/** The (name, version) a dependency line installs: `dep: 1.2.3`, or a value that points at an entry. */
|
|
141
|
+
const installs = (dep, value) => {
|
|
142
|
+
const exact = exactVersion(value);
|
|
143
|
+
if (exact)
|
|
144
|
+
return [dep, exact];
|
|
145
|
+
const ref = pnpmKeyRef(value);
|
|
146
|
+
return ids.get(ref) ?? parsePnpmKey(ref);
|
|
147
|
+
};
|
|
148
|
+
const isTarget = (dep, value) => {
|
|
149
|
+
const id = installs(dep, value);
|
|
150
|
+
return id !== null && id[0] === name && id[1] === version;
|
|
151
|
+
};
|
|
138
152
|
const edges = [];
|
|
139
153
|
const seen = new Set();
|
|
140
154
|
let section = "";
|
|
@@ -142,7 +156,7 @@ export function pnpmParents(content, name, version) {
|
|
|
142
156
|
let importer = null;
|
|
143
157
|
let inDeps = false;
|
|
144
158
|
let depIndent = -1;
|
|
145
|
-
let
|
|
159
|
+
let pendingDep = null;
|
|
146
160
|
let importerSpecifier = null;
|
|
147
161
|
const add = (edge) => {
|
|
148
162
|
const key = `${edge.parent}\0${edge.parentVersion}`;
|
|
@@ -166,7 +180,7 @@ export function pnpmParents(content, name, version) {
|
|
|
166
180
|
}
|
|
167
181
|
if (section === "packages" || section === "snapshots") {
|
|
168
182
|
if (indent === 2 && text.endsWith(":")) {
|
|
169
|
-
parent =
|
|
183
|
+
parent = ids.get(pnpmKeyRef(text.slice(0, -1))) ?? null;
|
|
170
184
|
inDeps = false;
|
|
171
185
|
}
|
|
172
186
|
else if (indent === 4 && parent) {
|
|
@@ -174,7 +188,7 @@ export function pnpmParents(content, name, version) {
|
|
|
174
188
|
}
|
|
175
189
|
else if (indent === 6 && parent && inDeps) {
|
|
176
190
|
const m = /^(?:'([^']+)'|"([^"]+)"|([^\s:'"]+)):\s*(.+)$/.exec(text);
|
|
177
|
-
if (m && (m[1] ?? m[2] ?? m[3]
|
|
191
|
+
if (m && isTarget(m[1] ?? m[2] ?? m[3], m[4])) {
|
|
178
192
|
add({ parent: parent[0], parentVersion: parent[1], range: null, local: false });
|
|
179
193
|
}
|
|
180
194
|
}
|
|
@@ -197,21 +211,23 @@ export function pnpmParents(content, name, version) {
|
|
|
197
211
|
// A dependency of the project itself: v5 `name: 1.2.3`, v6/v9 `name:` + `specifier:` + `version:`.
|
|
198
212
|
if (indent === depIndent + 2) {
|
|
199
213
|
const m = /^(?:'([^']+)'|"([^"]+)"|([^\s:'"]+)):(?:\s+(.*))?$/.exec(text);
|
|
200
|
-
|
|
214
|
+
pendingDep = m ? m[1] ?? m[2] ?? m[3] : null;
|
|
201
215
|
importerSpecifier = null;
|
|
202
|
-
if (
|
|
203
|
-
|
|
204
|
-
|
|
216
|
+
if (pendingDep && m?.[4]) {
|
|
217
|
+
if (isTarget(pendingDep, m[4]))
|
|
218
|
+
add({ parent: importer, parentVersion: null, range: null, local: true });
|
|
219
|
+
pendingDep = null;
|
|
205
220
|
}
|
|
206
221
|
}
|
|
207
|
-
else if (indent === depIndent + 4 &&
|
|
222
|
+
else if (indent === depIndent + 4 && pendingDep) {
|
|
208
223
|
const spec = /^specifier:\s*(.+)$/.exec(text);
|
|
209
224
|
if (spec)
|
|
210
225
|
importerSpecifier = spec[1].replace(/^['"]|['"]$/g, "");
|
|
211
226
|
const ver = /^version:\s*(.+)$/.exec(text);
|
|
212
|
-
if (ver
|
|
213
|
-
|
|
214
|
-
|
|
227
|
+
if (ver) {
|
|
228
|
+
if (isTarget(pendingDep, ver[1]))
|
|
229
|
+
add({ parent: importer, parentVersion: null, range: importerSpecifier, local: true });
|
|
230
|
+
pendingDep = null;
|
|
215
231
|
}
|
|
216
232
|
}
|
|
217
233
|
}
|
|
@@ -226,9 +242,11 @@ function parseYarnBlocks(content) {
|
|
|
226
242
|
for (const block of content.split(/\r?\n(?=\S)/)) {
|
|
227
243
|
const lines = block.split(/\r?\n/);
|
|
228
244
|
const header = lines[0];
|
|
229
|
-
const
|
|
230
|
-
if (!
|
|
245
|
+
const headerName = /^"?(@?[^@\s"]+)@/.exec(header)?.[1];
|
|
246
|
+
if (!headerName || headerName.startsWith("__"))
|
|
231
247
|
continue;
|
|
248
|
+
// An npm alias block (`string-width-cjs@npm:string-width@^4.2.0`) installs the real package.
|
|
249
|
+
const name = yarnPackageName(block, header, headerName);
|
|
232
250
|
const version = /^\s+version:?\s+"?([^"\s]+)"?\s*$/m.exec(block)?.[1];
|
|
233
251
|
if (!version)
|
|
234
252
|
continue;
|
|
@@ -260,21 +278,27 @@ function parseYarnBlocks(content) {
|
|
|
260
278
|
}
|
|
261
279
|
return blocks;
|
|
262
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Blocks whose dependencies resolve `name` to `version`. An npm alias edge
|
|
283
|
+
* (`string-width-cjs: "npm:string-width@^4.2.0"`) is an edge to the real
|
|
284
|
+
* package, with the real package's range; it resolves through the block whose
|
|
285
|
+
* spec is `string-width-cjs@npm:string-width@^4.2.0`.
|
|
286
|
+
*/
|
|
263
287
|
export function yarnParents(content, name, version) {
|
|
264
288
|
const blocks = parseYarnBlocks(content);
|
|
265
289
|
const resolved = new Map();
|
|
266
290
|
for (const b of blocks)
|
|
267
291
|
for (const spec of b.specs)
|
|
268
|
-
resolved.set(spec, b
|
|
292
|
+
resolved.set(spec, b);
|
|
269
293
|
const edges = [];
|
|
270
294
|
for (const b of blocks) {
|
|
271
295
|
for (const [dep, rawRange] of b.deps) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (resolved.get(`${name}@${range}`) !== version)
|
|
296
|
+
const spec = rawRange.replace(/^npm:/, "");
|
|
297
|
+
const target = resolved.get(`${dep}@${spec}`);
|
|
298
|
+
if (!target || target.name !== name || target.version !== version)
|
|
276
299
|
continue;
|
|
277
|
-
|
|
300
|
+
const alias = /^npm:((?:@[^\s/@]+\/)?[^\s/@]+)@(.+)$/.exec(rawRange);
|
|
301
|
+
edges.push({ parent: b.name, parentVersion: b.version, range: alias ? alias[2] : spec, local: false });
|
|
278
302
|
}
|
|
279
303
|
}
|
|
280
304
|
return edges;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@4da/mcp-server",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.3",
|
|
4
4
|
"mcpName": "io.github.4DA-Systems/4da-mcp-server",
|
|
5
5
|
"description": "Upgrade intelligence for AI coding agents: what changes between your installed and target version (changelog, breaking changes, the files you import it from), a pre-install verdict on each bump, vulnerability scanning at osv-scanner parity across npm/pnpm/yarn, Cargo, Python and Go lockfiles, ranked upgrade plans, task-scoped briefings, decision memory. Local, no account; works standalone, and adds the 4DA desktop app's signals, knowledge gaps and stack-judged news when it is installed.",
|
|
6
6
|
"type": "module",
|