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.
Files changed (38) hide show
  1. package/bin/flatcover.js +191 -42
  2. package/bin/flatlock-cmp.js +2 -2
  3. package/dist/compare.d.ts +85 -0
  4. package/dist/compare.d.ts.map +1 -0
  5. package/dist/detect.d.ts +33 -0
  6. package/dist/detect.d.ts.map +1 -0
  7. package/dist/index.d.ts +72 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/parsers/index.d.ts +5 -0
  10. package/dist/parsers/index.d.ts.map +1 -0
  11. package/dist/parsers/npm.d.ts +154 -0
  12. package/dist/parsers/npm.d.ts.map +1 -0
  13. package/dist/parsers/pnpm/detect.d.ts +136 -0
  14. package/dist/parsers/pnpm/detect.d.ts.map +1 -0
  15. package/dist/parsers/pnpm/index.d.ts +154 -0
  16. package/dist/parsers/pnpm/index.d.ts.map +1 -0
  17. package/dist/parsers/pnpm/internal.d.ts +5 -0
  18. package/dist/parsers/pnpm/internal.d.ts.map +1 -0
  19. package/dist/parsers/pnpm/shrinkwrap.d.ts +129 -0
  20. package/dist/parsers/pnpm/shrinkwrap.d.ts.map +1 -0
  21. package/dist/parsers/pnpm/v5.d.ts +139 -0
  22. package/dist/parsers/pnpm/v5.d.ts.map +1 -0
  23. package/dist/parsers/pnpm/v6plus.d.ts +212 -0
  24. package/dist/parsers/pnpm/v6plus.d.ts.map +1 -0
  25. package/dist/parsers/pnpm.d.ts +2 -0
  26. package/dist/parsers/pnpm.d.ts.map +1 -0
  27. package/dist/parsers/types.d.ts +23 -0
  28. package/dist/parsers/types.d.ts.map +1 -0
  29. package/dist/parsers/yarn-berry.d.ts +197 -0
  30. package/dist/parsers/yarn-berry.d.ts.map +1 -0
  31. package/dist/parsers/yarn-classic.d.ts +110 -0
  32. package/dist/parsers/yarn-classic.d.ts.map +1 -0
  33. package/dist/result.d.ts +12 -0
  34. package/dist/result.d.ts.map +1 -0
  35. package/dist/set.d.ts +238 -0
  36. package/dist/set.d.ts.map +1 -0
  37. package/package.json +9 -4
  38. 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 (values.help || positionals.length === 0) {
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 <lockfile> --cover --registry <url>
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
- /** @type {Map<string, Set<string>>} */
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 Set());
282
+ byPackage.set(dep.name, new Map());
177
283
  }
178
- byPackage.get(dep.name).add(dep.version);
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, versions]) => {
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 versions) {
327
+ for (const [version, dep] of versionMap) {
222
328
  const present = packumentVersions ? !!packumentVersions[version] : false;
223
- versionResults.push({ name, version, present });
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 [...versions].map(version => ({
229
- name,
230
- version,
231
- present: false,
232
- error: err.message
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
- console.log(JSON.stringify({ name: result.name, version: result.version, present: result.present }));
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 => ({ name: r.name, version: r.version, present: r.present }));
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
- console.log('package,version,present');
336
- for (const r of all) {
337
- console.log(`${r.name},${r.version},${r.present}`);
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
- if (values.workspace) {
357
- const repoDir = dirname(lockfilePath);
358
- const workspacePkgPath = join(repoDir, values.workspace, 'package.json');
359
- const workspacePkg = JSON.parse(readFileSync(workspacePkgPath, 'utf8'));
360
-
361
- deps = await lockfile.dependenciesOf(workspacePkg, {
362
- workspacePath: values.workspace,
363
- repoDir,
364
- dev: values.dev,
365
- peer: values.peer
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
- deps = lockfile;
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
@@ -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"}
@@ -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"}
@@ -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":""}