flatlock 1.3.0 → 1.4.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/bin/flatcover.js +191 -42
- package/bin/flatlock-cmp.js +2 -2
- package/dist/compare.d.ts +85 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/detect.d.ts +33 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/parsers/index.d.ts +5 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/npm.d.ts +154 -0
- package/dist/parsers/npm.d.ts.map +1 -0
- package/dist/parsers/pnpm/detect.d.ts +136 -0
- package/dist/parsers/pnpm/detect.d.ts.map +1 -0
- package/dist/parsers/pnpm/index.d.ts +154 -0
- package/dist/parsers/pnpm/index.d.ts.map +1 -0
- package/dist/parsers/pnpm/internal.d.ts +5 -0
- package/dist/parsers/pnpm/internal.d.ts.map +1 -0
- package/dist/parsers/pnpm/shrinkwrap.d.ts +129 -0
- package/dist/parsers/pnpm/shrinkwrap.d.ts.map +1 -0
- package/dist/parsers/pnpm/v5.d.ts +139 -0
- package/dist/parsers/pnpm/v5.d.ts.map +1 -0
- package/dist/parsers/pnpm/v6plus.d.ts +212 -0
- package/dist/parsers/pnpm/v6plus.d.ts.map +1 -0
- package/dist/parsers/pnpm.d.ts +2 -0
- package/dist/parsers/pnpm.d.ts.map +1 -0
- package/dist/parsers/types.d.ts +23 -0
- package/dist/parsers/types.d.ts.map +1 -0
- package/dist/parsers/yarn-berry.d.ts +197 -0
- package/dist/parsers/yarn-berry.d.ts.map +1 -0
- package/dist/parsers/yarn-classic.d.ts +110 -0
- package/dist/parsers/yarn-classic.d.ts.map +1 -0
- package/dist/result.d.ts +12 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/set.d.ts +238 -0
- package/dist/set.d.ts.map +1 -0
- package/package.json +9 -4
- package/src/compare.js +5 -7
package/bin/flatcover.js
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
import { parseArgs } from 'node:util';
|
|
16
16
|
import { readFileSync } from 'node:fs';
|
|
17
|
+
import { createReadStream } from 'node:fs';
|
|
18
|
+
import { createInterface } from 'node:readline';
|
|
17
19
|
import { dirname, join } from 'node:path';
|
|
18
20
|
import { Pool, RetryAgent } from 'undici';
|
|
19
21
|
import { FlatlockSet } from '../src/set.js';
|
|
@@ -21,6 +23,7 @@ import { FlatlockSet } from '../src/set.js';
|
|
|
21
23
|
const { values, positionals } = parseArgs({
|
|
22
24
|
options: {
|
|
23
25
|
workspace: { type: 'string', short: 'w' },
|
|
26
|
+
list: { type: 'string', short: 'l' },
|
|
24
27
|
dev: { type: 'boolean', default: false },
|
|
25
28
|
peer: { type: 'boolean', default: true },
|
|
26
29
|
specs: { type: 'boolean', short: 's', default: false },
|
|
@@ -39,16 +42,28 @@ const { values, positionals } = parseArgs({
|
|
|
39
42
|
allowPositionals: true
|
|
40
43
|
});
|
|
41
44
|
|
|
42
|
-
if
|
|
45
|
+
// Check if stdin input is requested via '-' positional argument (Unix convention)
|
|
46
|
+
const useStdin = positionals[0] === '-';
|
|
47
|
+
|
|
48
|
+
// Determine if we have a valid input source
|
|
49
|
+
const hasInputSource = positionals.length > 0 || values.list;
|
|
50
|
+
|
|
51
|
+
if (values.help || !hasInputSource) {
|
|
43
52
|
console.log(`flatcover - Check lockfile package coverage against a registry
|
|
44
53
|
|
|
45
54
|
Usage:
|
|
46
55
|
flatcover <lockfile> --cover
|
|
47
|
-
flatcover
|
|
56
|
+
flatcover --list packages.json --cover
|
|
57
|
+
cat packages.ndjson | flatcover - --cover
|
|
48
58
|
flatcover <lockfile> --cover --registry <url> --auth user:pass
|
|
49
59
|
|
|
60
|
+
Input sources (mutually exclusive):
|
|
61
|
+
<lockfile> Parse lockfile (package-lock.json, pnpm-lock.yaml, yarn.lock)
|
|
62
|
+
-l, --list <file> Read JSON array of {name, version} objects from file
|
|
63
|
+
- Read NDJSON {name, version} objects from stdin (one per line)
|
|
64
|
+
|
|
50
65
|
Options:
|
|
51
|
-
-w, --workspace <path> Workspace path within monorepo
|
|
66
|
+
-w, --workspace <path> Workspace path within monorepo (lockfile mode only)
|
|
52
67
|
-s, --specs Include version (name@version or {name,version})
|
|
53
68
|
--json Output as JSON array
|
|
54
69
|
--ndjson Output as newline-delimited JSON (streaming)
|
|
@@ -68,14 +83,27 @@ Coverage options:
|
|
|
68
83
|
|
|
69
84
|
Output formats (with --cover):
|
|
70
85
|
(default) CSV: package,version,present
|
|
86
|
+
--full CSV: package,version,present,integrity,resolved
|
|
71
87
|
--json [{"name":"...","version":"...","present":true}, ...]
|
|
88
|
+
--full --json Adds "integrity" and "resolved" fields to JSON
|
|
72
89
|
--ndjson {"name":"...","version":"...","present":true} per line
|
|
73
90
|
|
|
74
91
|
Examples:
|
|
92
|
+
# From lockfile
|
|
75
93
|
flatcover package-lock.json --cover
|
|
94
|
+
flatcover package-lock.json --cover --full --json
|
|
95
|
+
|
|
96
|
+
# From JSON list file
|
|
97
|
+
flatcover --list packages.json --cover --summary
|
|
98
|
+
echo '[{"name":"lodash","version":"4.17.21"}]' > pkgs.json && flatcover -l pkgs.json --cover
|
|
99
|
+
|
|
100
|
+
# From stdin (NDJSON) - use '-' to read from stdin
|
|
101
|
+
echo '{"name":"lodash","version":"4.17.21"}' | flatcover - --cover
|
|
102
|
+
cat packages.ndjson | flatcover - --cover --json
|
|
103
|
+
|
|
104
|
+
# With custom registry
|
|
76
105
|
flatcover package-lock.json --cover --registry https://npm.pkg.github.com --token ghp_xxx
|
|
77
|
-
flatcover pnpm-lock.yaml --cover --auth admin:secret --ndjson
|
|
78
|
-
flatcover pnpm-lock.yaml -w packages/core --cover --summary`);
|
|
106
|
+
flatcover pnpm-lock.yaml --cover --auth admin:secret --ndjson`);
|
|
79
107
|
process.exit(values.help ? 0 : 1);
|
|
80
108
|
}
|
|
81
109
|
|
|
@@ -89,6 +117,19 @@ if (values.auth && values.token) {
|
|
|
89
117
|
process.exit(1);
|
|
90
118
|
}
|
|
91
119
|
|
|
120
|
+
// Validate mutually exclusive input sources
|
|
121
|
+
// Note: useStdin means positionals[0] === '-', so it's already counted in positionals.length
|
|
122
|
+
if (positionals.length > 0 && values.list) {
|
|
123
|
+
console.error('Error: Cannot use both lockfile/stdin and --list');
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// --workspace only works with lockfile input (not stdin or --list)
|
|
128
|
+
if (values.workspace && (useStdin || values.list || !positionals.length)) {
|
|
129
|
+
console.error('Error: --workspace can only be used with lockfile input');
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
|
|
92
133
|
// --full implies --specs
|
|
93
134
|
if (values.full) {
|
|
94
135
|
values.specs = true;
|
|
@@ -102,6 +143,70 @@ if (values.cover) {
|
|
|
102
143
|
const lockfilePath = positionals[0];
|
|
103
144
|
const concurrency = Math.max(1, Math.min(50, Number.parseInt(values.concurrency, 10) || 20));
|
|
104
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Read packages from a JSON list file
|
|
148
|
+
* @param {string} filePath - Path to JSON file containing [{name, version}, ...]
|
|
149
|
+
* @returns {Array<{ name: string, version: string }>}
|
|
150
|
+
*/
|
|
151
|
+
function readJsonList(filePath) {
|
|
152
|
+
const content = readFileSync(filePath, 'utf8');
|
|
153
|
+
const data = JSON.parse(content);
|
|
154
|
+
|
|
155
|
+
if (!Array.isArray(data)) {
|
|
156
|
+
throw new Error('--list file must contain a JSON array');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const packages = [];
|
|
160
|
+
for (const item of data) {
|
|
161
|
+
if (!item.name || !item.version) {
|
|
162
|
+
throw new Error('Each item in --list must have "name" and "version" fields');
|
|
163
|
+
}
|
|
164
|
+
packages.push({
|
|
165
|
+
name: item.name,
|
|
166
|
+
version: item.version,
|
|
167
|
+
integrity: item.integrity,
|
|
168
|
+
resolved: item.resolved
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return packages;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Read packages from stdin as NDJSON
|
|
177
|
+
* @returns {Promise<Array<{ name: string, version: string }>>}
|
|
178
|
+
*/
|
|
179
|
+
async function readStdinNdjson() {
|
|
180
|
+
const packages = [];
|
|
181
|
+
|
|
182
|
+
const rl = createInterface({
|
|
183
|
+
input: process.stdin,
|
|
184
|
+
crlfDelay: Infinity
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
for await (const line of rl) {
|
|
188
|
+
const trimmed = line.trim();
|
|
189
|
+
if (!trimmed) continue;
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const item = JSON.parse(trimmed);
|
|
193
|
+
if (!item.name || !item.version) {
|
|
194
|
+
throw new Error('Each line must have "name" and "version" fields');
|
|
195
|
+
}
|
|
196
|
+
packages.push({
|
|
197
|
+
name: item.name,
|
|
198
|
+
version: item.version,
|
|
199
|
+
integrity: item.integrity,
|
|
200
|
+
resolved: item.resolved
|
|
201
|
+
});
|
|
202
|
+
} catch (err) {
|
|
203
|
+
throw new Error(`Invalid JSON on stdin: ${err.message}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return packages;
|
|
208
|
+
}
|
|
209
|
+
|
|
105
210
|
/**
|
|
106
211
|
* Encode package name for URL (handle scoped packages)
|
|
107
212
|
* @param {string} name - Package name like @babel/core
|
|
@@ -161,21 +266,22 @@ function createClient(registryUrl, { auth, token }) {
|
|
|
161
266
|
|
|
162
267
|
/**
|
|
163
268
|
* Check coverage for all dependencies
|
|
164
|
-
* @param {Array<{ name: string, version: string }>} deps
|
|
269
|
+
* @param {Array<{ name: string, version: string, integrity?: string, resolved?: string }>} deps
|
|
165
270
|
* @param {{ registry: string, auth?: string, token?: string, progress: boolean }} options
|
|
166
|
-
* @returns {AsyncGenerator<{ name: string, version: string, present: boolean, error?: string }>}
|
|
271
|
+
* @returns {AsyncGenerator<{ name: string, version: string, present: boolean, integrity?: string, resolved?: string, error?: string }>}
|
|
167
272
|
*/
|
|
168
273
|
async function* checkCoverage(deps, { registry, auth, token, progress }) {
|
|
169
274
|
const { client, headers, baseUrl } = createClient(registry, { auth, token });
|
|
170
275
|
|
|
171
276
|
// Group by package name to avoid duplicate requests
|
|
172
|
-
|
|
277
|
+
// Store full dep info (including integrity/resolved) keyed by version
|
|
278
|
+
/** @type {Map<string, Map<string, { name: string, version: string, integrity?: string, resolved?: string }>>} */
|
|
173
279
|
const byPackage = new Map();
|
|
174
280
|
for (const dep of deps) {
|
|
175
281
|
if (!byPackage.has(dep.name)) {
|
|
176
|
-
byPackage.set(dep.name, new
|
|
282
|
+
byPackage.set(dep.name, new Map());
|
|
177
283
|
}
|
|
178
|
-
byPackage.get(dep.name).
|
|
284
|
+
byPackage.get(dep.name).set(dep.version, dep);
|
|
179
285
|
}
|
|
180
286
|
|
|
181
287
|
const packages = [...byPackage.entries()];
|
|
@@ -187,7 +293,7 @@ async function* checkCoverage(deps, { registry, auth, token, progress }) {
|
|
|
187
293
|
const batch = packages.slice(i, i + concurrency);
|
|
188
294
|
|
|
189
295
|
const results = await Promise.all(
|
|
190
|
-
batch.map(async ([name,
|
|
296
|
+
batch.map(async ([name, versionMap]) => {
|
|
191
297
|
const encodedName = encodePackageName(name);
|
|
192
298
|
const basePath = baseUrl.pathname.replace(/\/$/, '');
|
|
193
299
|
const path = `${basePath}/${encodedName}`;
|
|
@@ -216,21 +322,29 @@ async function* checkCoverage(deps, { registry, auth, token, progress }) {
|
|
|
216
322
|
packumentVersions = packument.versions || {};
|
|
217
323
|
}
|
|
218
324
|
|
|
219
|
-
// Check each version
|
|
325
|
+
// Check each version, preserving integrity/resolved from original dep
|
|
220
326
|
const versionResults = [];
|
|
221
|
-
for (const version of
|
|
327
|
+
for (const [version, dep] of versionMap) {
|
|
222
328
|
const present = packumentVersions ? !!packumentVersions[version] : false;
|
|
223
|
-
|
|
329
|
+
const result = { name, version, present };
|
|
330
|
+
if (dep.integrity) result.integrity = dep.integrity;
|
|
331
|
+
if (dep.resolved) result.resolved = dep.resolved;
|
|
332
|
+
versionResults.push(result);
|
|
224
333
|
}
|
|
225
334
|
return versionResults;
|
|
226
335
|
} catch (err) {
|
|
227
336
|
// Return error for all versions of this package
|
|
228
|
-
return [...
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
337
|
+
return [...versionMap.values()].map(dep => {
|
|
338
|
+
const result = {
|
|
339
|
+
name: dep.name,
|
|
340
|
+
version: dep.version,
|
|
341
|
+
present: false,
|
|
342
|
+
error: err.message
|
|
343
|
+
};
|
|
344
|
+
if (dep.integrity) result.integrity = dep.integrity;
|
|
345
|
+
if (dep.resolved) result.resolved = dep.resolved;
|
|
346
|
+
return result;
|
|
347
|
+
});
|
|
234
348
|
}
|
|
235
349
|
})
|
|
236
350
|
);
|
|
@@ -300,10 +414,10 @@ function outputDeps(deps, { specs, json, ndjson, full }) {
|
|
|
300
414
|
|
|
301
415
|
/**
|
|
302
416
|
* Output coverage results
|
|
303
|
-
* @param {AsyncGenerator<{ name: string, version: string, present: boolean, error?: string }>} results
|
|
304
|
-
* @param {{ json: boolean, ndjson: boolean, summary: boolean }} options
|
|
417
|
+
* @param {AsyncGenerator<{ name: string, version: string, present: boolean, integrity?: string, resolved?: string, error?: string }>} results
|
|
418
|
+
* @param {{ json: boolean, ndjson: boolean, summary: boolean, full: boolean }} options
|
|
305
419
|
*/
|
|
306
|
-
async function outputCoverage(results, { json, ndjson, summary }) {
|
|
420
|
+
async function outputCoverage(results, { json, ndjson, summary, full }) {
|
|
307
421
|
const all = [];
|
|
308
422
|
let presentCount = 0;
|
|
309
423
|
let missingCount = 0;
|
|
@@ -317,7 +431,10 @@ async function outputCoverage(results, { json, ndjson, summary }) {
|
|
|
317
431
|
|
|
318
432
|
if (ndjson) {
|
|
319
433
|
// Stream immediately
|
|
320
|
-
|
|
434
|
+
const obj = { name: result.name, version: result.version, present: result.present };
|
|
435
|
+
if (full && result.integrity) obj.integrity = result.integrity;
|
|
436
|
+
if (full && result.resolved) obj.resolved = result.resolved;
|
|
437
|
+
console.log(JSON.stringify(obj));
|
|
321
438
|
} else {
|
|
322
439
|
all.push(result);
|
|
323
440
|
}
|
|
@@ -328,13 +445,25 @@ async function outputCoverage(results, { json, ndjson, summary }) {
|
|
|
328
445
|
all.sort((a, b) => a.name.localeCompare(b.name) || a.version.localeCompare(b.version));
|
|
329
446
|
|
|
330
447
|
if (json) {
|
|
331
|
-
const data = all.map(r =>
|
|
448
|
+
const data = all.map(r => {
|
|
449
|
+
const obj = { name: r.name, version: r.version, present: r.present };
|
|
450
|
+
if (full && r.integrity) obj.integrity = r.integrity;
|
|
451
|
+
if (full && r.resolved) obj.resolved = r.resolved;
|
|
452
|
+
return obj;
|
|
453
|
+
});
|
|
332
454
|
console.log(JSON.stringify(data, null, 2));
|
|
333
455
|
} else {
|
|
334
456
|
// CSV output
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
457
|
+
if (full) {
|
|
458
|
+
console.log('package,version,present,integrity,resolved');
|
|
459
|
+
for (const r of all) {
|
|
460
|
+
console.log(`${r.name},${r.version},${r.present},${r.integrity || ''},${r.resolved || ''}`);
|
|
461
|
+
}
|
|
462
|
+
} else {
|
|
463
|
+
console.log('package,version,present');
|
|
464
|
+
for (const r of all) {
|
|
465
|
+
console.log(`${r.name},${r.version},${r.present}`);
|
|
466
|
+
}
|
|
338
467
|
}
|
|
339
468
|
}
|
|
340
469
|
}
|
|
@@ -350,22 +479,41 @@ async function outputCoverage(results, { json, ndjson, summary }) {
|
|
|
350
479
|
}
|
|
351
480
|
|
|
352
481
|
try {
|
|
353
|
-
const lockfile = await FlatlockSet.fromPath(lockfilePath);
|
|
354
482
|
let deps;
|
|
355
483
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
484
|
+
// Determine input source and load dependencies
|
|
485
|
+
if (useStdin) {
|
|
486
|
+
// Read from stdin (NDJSON)
|
|
487
|
+
deps = await readStdinNdjson();
|
|
488
|
+
if (deps.length === 0) {
|
|
489
|
+
console.error('Error: No packages read from stdin');
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
} else if (values.list) {
|
|
493
|
+
// Read from JSON list file
|
|
494
|
+
deps = readJsonList(values.list);
|
|
495
|
+
if (deps.length === 0) {
|
|
496
|
+
console.error('Error: No packages found in --list file');
|
|
497
|
+
process.exit(1);
|
|
498
|
+
}
|
|
367
499
|
} else {
|
|
368
|
-
|
|
500
|
+
// Read from lockfile (existing behavior)
|
|
501
|
+
const lockfile = await FlatlockSet.fromPath(lockfilePath);
|
|
502
|
+
|
|
503
|
+
if (values.workspace) {
|
|
504
|
+
const repoDir = dirname(lockfilePath);
|
|
505
|
+
const workspacePkgPath = join(repoDir, values.workspace, 'package.json');
|
|
506
|
+
const workspacePkg = JSON.parse(readFileSync(workspacePkgPath, 'utf8'));
|
|
507
|
+
|
|
508
|
+
deps = await lockfile.dependenciesOf(workspacePkg, {
|
|
509
|
+
workspacePath: values.workspace,
|
|
510
|
+
repoDir,
|
|
511
|
+
dev: values.dev,
|
|
512
|
+
peer: values.peer
|
|
513
|
+
});
|
|
514
|
+
} else {
|
|
515
|
+
deps = lockfile;
|
|
516
|
+
}
|
|
369
517
|
}
|
|
370
518
|
|
|
371
519
|
if (values.cover) {
|
|
@@ -381,7 +529,8 @@ try {
|
|
|
381
529
|
await outputCoverage(results, {
|
|
382
530
|
json: values.json,
|
|
383
531
|
ndjson: values.ndjson,
|
|
384
|
-
summary: values.summary
|
|
532
|
+
summary: values.summary,
|
|
533
|
+
full: values.full
|
|
385
534
|
});
|
|
386
535
|
} else {
|
|
387
536
|
// Standard flatlock mode
|
package/bin/flatlock-cmp.js
CHANGED
|
@@ -187,7 +187,7 @@ Examples:
|
|
|
187
187
|
const wsNote = result.workspaceCount > 0 ? ` (${result.workspaceCount} workspaces excluded)` : '';
|
|
188
188
|
console.log(`✓ ${result.path}${wsNote}`);
|
|
189
189
|
console.log(` count: flatlock=${result.flatlockCount} ${result.source}=${result.comparisonCount}`);
|
|
190
|
-
console.log(` sets: equinumerous`);
|
|
190
|
+
console.log(` sets: equinumerous\n`);
|
|
191
191
|
}
|
|
192
192
|
} else {
|
|
193
193
|
// Determine if this is a "superset" (flatlock found more, expected for pnpm)
|
|
@@ -203,7 +203,7 @@ Examples:
|
|
|
203
203
|
console.log(`⊃ ${result.path}${wsNote}`);
|
|
204
204
|
console.log(` count: flatlock=${result.flatlockCount} ${result.source}=${result.comparisonCount}`);
|
|
205
205
|
console.log(` sets: SUPERSET (+${result.onlyInFlatlock.length} reachable deps)`);
|
|
206
|
-
console.log(` note: flatlock's reachability analysis found transitive deps pnpm omits`);
|
|
206
|
+
console.log(` note: flatlock's reachability analysis found transitive deps pnpm omits\n`);
|
|
207
207
|
}
|
|
208
208
|
} else {
|
|
209
209
|
mismatchCount++;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare flatlock output against established parser for a lockfile
|
|
3
|
+
* @param {string} filepath - Path to lockfile
|
|
4
|
+
* @param {CompareOptions} [options] - Options
|
|
5
|
+
* @returns {Promise<ComparisonResult>}
|
|
6
|
+
*/
|
|
7
|
+
export function compare(filepath: string, options?: CompareOptions): Promise<ComparisonResult>;
|
|
8
|
+
/**
|
|
9
|
+
* Compare multiple lockfiles
|
|
10
|
+
* @param {string[]} filepaths - Paths to lockfiles
|
|
11
|
+
* @param {CompareOptions} [options] - Options
|
|
12
|
+
* @returns {AsyncGenerator<ComparisonResult & { filepath: string }>}
|
|
13
|
+
*/
|
|
14
|
+
export function compareAll(filepaths: string[], options?: CompareOptions): AsyncGenerator<ComparisonResult & {
|
|
15
|
+
filepath: string;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Check which optional comparison parsers are available
|
|
19
|
+
* @returns {Promise<{ arborist: boolean, cyclonedx: boolean, pnpmLockfileFs: boolean, yarnCore: boolean }>}
|
|
20
|
+
*/
|
|
21
|
+
export function getAvailableParsers(): Promise<{
|
|
22
|
+
arborist: boolean;
|
|
23
|
+
cyclonedx: boolean;
|
|
24
|
+
pnpmLockfileFs: boolean;
|
|
25
|
+
yarnCore: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
export type CompareOptions = {
|
|
28
|
+
/**
|
|
29
|
+
* - Temp directory for Arborist/CycloneDX (npm only)
|
|
30
|
+
*/
|
|
31
|
+
tmpDir?: string;
|
|
32
|
+
/**
|
|
33
|
+
* - Workspace paths for CycloneDX (-w flag)
|
|
34
|
+
*/
|
|
35
|
+
workspace?: string[];
|
|
36
|
+
};
|
|
37
|
+
export type ComparisonResult = {
|
|
38
|
+
/**
|
|
39
|
+
* - Lockfile type
|
|
40
|
+
*/
|
|
41
|
+
type: string;
|
|
42
|
+
/**
|
|
43
|
+
* - Comparison source used (e.g., '@npmcli/arborist', '@cyclonedx/cyclonedx-npm')
|
|
44
|
+
*/
|
|
45
|
+
source?: string;
|
|
46
|
+
/**
|
|
47
|
+
* - Whether flatlock and comparison have same cardinality
|
|
48
|
+
*/
|
|
49
|
+
equinumerous: boolean | null;
|
|
50
|
+
/**
|
|
51
|
+
* - Number of packages found by flatlock
|
|
52
|
+
*/
|
|
53
|
+
flatlockCount: number;
|
|
54
|
+
/**
|
|
55
|
+
* - Number of packages found by comparison parser
|
|
56
|
+
*/
|
|
57
|
+
comparisonCount?: number;
|
|
58
|
+
/**
|
|
59
|
+
* - Number of workspace packages skipped
|
|
60
|
+
*/
|
|
61
|
+
workspaceCount?: number;
|
|
62
|
+
/**
|
|
63
|
+
* - Packages only found by flatlock
|
|
64
|
+
*/
|
|
65
|
+
onlyInFlatlock?: string[];
|
|
66
|
+
/**
|
|
67
|
+
* - Packages only found by comparison parser
|
|
68
|
+
*/
|
|
69
|
+
onlyInComparison?: string[];
|
|
70
|
+
};
|
|
71
|
+
export type PackagesResult = {
|
|
72
|
+
/**
|
|
73
|
+
* - Set of package@version strings
|
|
74
|
+
*/
|
|
75
|
+
packages: Set<string>;
|
|
76
|
+
/**
|
|
77
|
+
* - Number of workspace packages skipped
|
|
78
|
+
*/
|
|
79
|
+
workspaceCount: number;
|
|
80
|
+
/**
|
|
81
|
+
* - Comparison source used
|
|
82
|
+
*/
|
|
83
|
+
source: string;
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../src/compare.js"],"names":[],"mappings":"AAyhBA;;;;;GAKG;AACH,kCAJW,MAAM,YACN,cAAc,GACZ,OAAO,CAAC,gBAAgB,CAAC,CA4CrC;AAED;;;;;GAKG;AACH,sCAJW,MAAM,EAAE,YACR,cAAc,GACZ,cAAc,CAAC,gBAAgB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAMnE;AAED;;;GAGG;AACH,uCAFa,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAgB1G;;;;;aAjhBa,MAAM;;;;gBACN,MAAM,EAAE;;;;;;UAKR,MAAM;;;;aACN,MAAM;;;;kBACN,OAAO,GAAG,IAAI;;;;mBACd,MAAM;;;;sBACN,MAAM;;;;qBACN,MAAM;;;;qBACN,MAAM,EAAE;;;;uBACR,MAAM,EAAE;;;;;;cAKR,GAAG,CAAC,MAAM,CAAC;;;;oBACX,MAAM;;;;YACN,MAAM"}
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect lockfile type from content and/or path
|
|
3
|
+
*
|
|
4
|
+
* Content is the primary signal - we actually parse the content to verify
|
|
5
|
+
* it's a valid lockfile of the detected type. This prevents spoofing attacks
|
|
6
|
+
* where malicious content contains detection markers in strings/comments.
|
|
7
|
+
*
|
|
8
|
+
* Path is only used as a fallback hint when content is not provided.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} options - Detection options
|
|
11
|
+
* @param {string} [options.path] - Path to the lockfile (optional hint)
|
|
12
|
+
* @param {string} [options.content] - Lockfile content (primary signal)
|
|
13
|
+
* @returns {LockfileType}
|
|
14
|
+
* @throws {Error} If unable to detect lockfile type
|
|
15
|
+
*/
|
|
16
|
+
export function detectType({ path, content }?: {
|
|
17
|
+
path?: string | undefined;
|
|
18
|
+
content?: string | undefined;
|
|
19
|
+
}): LockfileType;
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {'npm' | 'pnpm' | 'yarn-classic' | 'yarn-berry'} LockfileType
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Lockfile type constants
|
|
25
|
+
*/
|
|
26
|
+
export const Type: Readonly<{
|
|
27
|
+
NPM: "npm";
|
|
28
|
+
PNPM: "pnpm";
|
|
29
|
+
YARN_CLASSIC: "yarn-classic";
|
|
30
|
+
YARN_BERRY: "yarn-berry";
|
|
31
|
+
}>;
|
|
32
|
+
export type LockfileType = "npm" | "pnpm" | "yarn-classic" | "yarn-berry";
|
|
33
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.js"],"names":[],"mappings":"AA6FA;;;;;;;;;;;;;;GAcG;AACH,+CALG;IAAyB,IAAI;IACJ,OAAO;CAChC,GAAU,YAAY,CAgDxB;AArJD;;GAEG;AAEH;;GAEG;AACH;;;;;GAKG;2BAXU,KAAK,GAAG,MAAM,GAAG,cAAc,GAAG,YAAY"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse lockfile from path (auto-detect type)
|
|
3
|
+
* @param {string} path - Path to lockfile
|
|
4
|
+
* @param {Object} [options] - Parser options
|
|
5
|
+
* @returns {AsyncGenerator<Dependency>}
|
|
6
|
+
*/
|
|
7
|
+
export function fromPath(path: string, options?: Object): AsyncGenerator<Dependency>;
|
|
8
|
+
/**
|
|
9
|
+
* Parse lockfile from string (auto-detect or use options.type)
|
|
10
|
+
* @param {string} content - Lockfile content
|
|
11
|
+
* @param {Object} [options] - Parser options
|
|
12
|
+
* @param {string} [options.path] - Path hint for type detection
|
|
13
|
+
* @param {LockfileType} [options.type] - Explicit type (skip detection)
|
|
14
|
+
* @returns {Generator<Dependency>}
|
|
15
|
+
*/
|
|
16
|
+
export function fromString(content: string, options?: {
|
|
17
|
+
path?: string | undefined;
|
|
18
|
+
type?: import("./detect.js").LockfileType | undefined;
|
|
19
|
+
}): Generator<Dependency>;
|
|
20
|
+
/**
|
|
21
|
+
* Try to parse lockfile from path (returns Result)
|
|
22
|
+
* @param {string} path - Path to lockfile
|
|
23
|
+
* @param {ParseOptions} [options] - Parser options
|
|
24
|
+
* @returns {Promise<import('./result.js').Result<AsyncGenerator<Dependency>>>}
|
|
25
|
+
*/
|
|
26
|
+
export function tryFromPath(path: string, options?: ParseOptions): Promise<import("./result.js").Result<AsyncGenerator<Dependency>>>;
|
|
27
|
+
/**
|
|
28
|
+
* Try to parse lockfile from string (returns Result)
|
|
29
|
+
* @param {string} content - Lockfile content
|
|
30
|
+
* @param {ParseOptions} [options] - Parser options
|
|
31
|
+
* @returns {import('./result.js').Result<Generator<Dependency>>}
|
|
32
|
+
*/
|
|
33
|
+
export function tryFromString(content: string, options?: ParseOptions): import("./result.js").Result<Generator<Dependency>>;
|
|
34
|
+
/**
|
|
35
|
+
* Parse yarn.lock (auto-detect classic vs berry)
|
|
36
|
+
* @param {string} content - Lockfile content
|
|
37
|
+
* @param {Object} [options] - Parser options
|
|
38
|
+
* @returns {Generator<Dependency>}
|
|
39
|
+
*/
|
|
40
|
+
export function fromYarnLock(content: string, options?: Object): Generator<Dependency>;
|
|
41
|
+
/**
|
|
42
|
+
* Collect all dependencies into an array
|
|
43
|
+
* @param {string} pathOrContent - Path to lockfile or content string
|
|
44
|
+
* @param {Object} [options] - Parser options
|
|
45
|
+
* @returns {Promise<Dependency[]>}
|
|
46
|
+
*/
|
|
47
|
+
export function collect(pathOrContent: string, options?: Object): Promise<Dependency[]>;
|
|
48
|
+
export type LockfileType = import("./detect.js").LockfileType;
|
|
49
|
+
export type Dependency = import("./parsers/npm.js").Dependency;
|
|
50
|
+
export type ParseOptions = {
|
|
51
|
+
/**
|
|
52
|
+
* - Path hint for type detection
|
|
53
|
+
*/
|
|
54
|
+
path?: string;
|
|
55
|
+
/**
|
|
56
|
+
* - Explicit type (skip detection)
|
|
57
|
+
*/
|
|
58
|
+
type?: LockfileType;
|
|
59
|
+
};
|
|
60
|
+
import { Type } from './detect.js';
|
|
61
|
+
import { detectType } from './detect.js';
|
|
62
|
+
import { Ok } from './result.js';
|
|
63
|
+
import { Err } from './result.js';
|
|
64
|
+
import { fromPackageLock } from './parsers/index.js';
|
|
65
|
+
import { fromPnpmLock } from './parsers/index.js';
|
|
66
|
+
import { fromYarnClassicLock } from './parsers/index.js';
|
|
67
|
+
import { fromYarnBerryLock } from './parsers/index.js';
|
|
68
|
+
import { FlatlockSet } from './set.js';
|
|
69
|
+
export { Type, detectType, Ok, Err, fromPackageLock, fromPnpmLock, fromYarnClassicLock, fromYarnBerryLock, FlatlockSet };
|
|
70
|
+
export { compare, compareAll, getAvailableParsers } from "./compare.js";
|
|
71
|
+
export { parseNpmKey, parsePnpmKey, parseYarnBerryKey, parseYarnClassicKey } from "./parsers/index.js";
|
|
72
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAgCA;;;;;GAKG;AACH,+BAJW,MAAM,YACN,MAAM,GACJ,cAAc,CAAC,UAAU,CAAC,CAOtC;AAED;;;;;;;GAOG;AACH,oCANW,MAAM,YAEd;IAAyB,IAAI;IACE,IAAI;CACnC,GAAU,SAAS,CAAC,UAAU,CAAC,CA0BjC;AAED;;;;;GAKG;AACH,kCAJW,MAAM,YACN,YAAY,GACV,OAAO,CAAC,OAAO,aAAa,EAAE,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAS7E;AAED;;;;;GAKG;AACH,uCAJW,MAAM,YACN,YAAY,GACV,OAAO,aAAa,EAAE,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAW/D;AAED;;;;;GAKG;AACH,sCAJW,MAAM,YACN,MAAM,GACJ,SAAS,CAAC,UAAU,CAAC,CAUjC;AAED;;;;;GAKG;AACH,uCAJW,MAAM,YACN,MAAM,GACJ,OAAO,CAAC,UAAU,EAAE,CAAC,CAmBjC;2BA3Ia,OAAO,aAAa,EAAE,YAAY;yBAClC,OAAO,kBAAkB,EAAE,UAAU;;;;;WAIrC,MAAM;;;;WACN,YAAY;;qBAhBO,aAAa;2BAAb,aAAa;mBAOtB,aAAa;oBAAb,aAAa;gCAD9B,oBAAoB;6BAApB,oBAAoB;oCAApB,oBAAoB;kCAApB,oBAAoB;4BAEC,UAAU"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { buildWorkspacePackages as buildNpmWorkspacePackages, extractWorkspacePaths as extractNpmWorkspacePaths, fromPackageLock, parseLockfileKey as parseNpmKey } from "./npm.js";
|
|
2
|
+
export { buildWorkspacePackages as buildPnpmWorkspacePackages, extractWorkspacePaths as extractPnpmWorkspacePaths, fromPnpmLock, parseLockfileKey as parsePnpmKey } from "./pnpm.js";
|
|
3
|
+
export { buildWorkspacePackages as buildYarnBerryWorkspacePackages, extractWorkspacePaths as extractYarnBerryWorkspacePaths, fromYarnBerryLock, parseLockfileKey as parseYarnBerryKey, parseResolution as parseYarnBerryResolution } from "./yarn-berry.js";
|
|
4
|
+
export { fromYarnClassicLock, parseLockfileKey as parseYarnClassicKey, parseYarnClassic } from "./yarn-classic.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parsers/index.js"],"names":[],"mappings":""}
|