@packall/core 0.1.0 → 0.3.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 (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/archive-DmF9XNl7.js +284 -0
  4. package/dist/archive-DmF9XNl7.js.map +1 -0
  5. package/dist/archive.d.ts +47 -15
  6. package/dist/archive.d.ts.map +1 -1
  7. package/dist/bundle.d.ts +6 -3
  8. package/dist/bundle.d.ts.map +1 -1
  9. package/dist/dependency-range.d.ts.map +1 -1
  10. package/dist/download.d.ts +2 -1
  11. package/dist/download.d.ts.map +1 -1
  12. package/dist/enums/artifact-kind.d.ts.map +1 -1
  13. package/dist/enums/edge-kind.d.ts.map +1 -1
  14. package/dist/enums/input-file-kind.d.ts.map +1 -1
  15. package/dist/enums/layout.d.ts.map +1 -1
  16. package/dist/enums/lockfile-format.d.ts.map +1 -1
  17. package/dist/enums/phase.d.ts.map +1 -1
  18. package/dist/errors.d.ts +11 -1
  19. package/dist/errors.d.ts.map +1 -1
  20. package/dist/index.d.ts +12 -10
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +169 -344
  23. package/dist/index.js.map +1 -1
  24. package/dist/input-file.d.ts.map +1 -1
  25. package/dist/integrity.d.ts +37 -5
  26. package/dist/integrity.d.ts.map +1 -1
  27. package/dist/layout.d.ts +41 -7
  28. package/dist/layout.d.ts.map +1 -1
  29. package/dist/locked-resolve.d.ts.map +1 -1
  30. package/dist/lockfile/bun.d.ts.map +1 -1
  31. package/dist/lockfile/detect.d.ts.map +1 -1
  32. package/dist/lockfile/json.d.ts.map +1 -1
  33. package/dist/lockfile/names.d.ts.map +1 -1
  34. package/dist/lockfile/npm.d.ts.map +1 -1
  35. package/dist/lockfile/parse.d.ts.map +1 -1
  36. package/dist/lockfile/pnpm.d.ts.map +1 -1
  37. package/dist/lockfile/tree-builder.d.ts.map +1 -1
  38. package/dist/lockfile/types.d.ts.map +1 -1
  39. package/dist/manifest.d.ts.map +1 -1
  40. package/dist/node/archiver.d.ts +5 -0
  41. package/dist/node/archiver.d.ts.map +1 -0
  42. package/dist/node/index.d.ts +13 -0
  43. package/dist/node/index.d.ts.map +1 -0
  44. package/dist/node/index.js +55 -0
  45. package/dist/node/index.js.map +1 -0
  46. package/dist/options.d.ts +3 -3
  47. package/dist/options.d.ts.map +1 -1
  48. package/dist/platform.d.ts +5 -10
  49. package/dist/platform.d.ts.map +1 -1
  50. package/dist/progress.d.ts.map +1 -1
  51. package/dist/registry.d.ts.map +1 -1
  52. package/dist/resolve.d.ts.map +1 -1
  53. package/dist/schemas/lenient.d.ts.map +1 -1
  54. package/dist/schemas/package-json.d.ts.map +1 -1
  55. package/dist/spec.d.ts.map +1 -1
  56. package/dist/types/value-of.d.ts.map +1 -1
  57. package/dist/utils/is-record.d.ts.map +1 -1
  58. package/package.json +11 -10
  59. package/src/archive.ts +95 -92
  60. package/src/bundle.ts +736 -616
  61. package/src/dependency-range.ts +121 -115
  62. package/src/download.ts +155 -133
  63. package/src/enums/artifact-kind.ts +5 -5
  64. package/src/enums/edge-kind.ts +13 -13
  65. package/src/enums/input-file-kind.ts +9 -9
  66. package/src/enums/layout.ts +20 -20
  67. package/src/enums/lockfile-format.ts +6 -6
  68. package/src/enums/phase.ts +8 -8
  69. package/src/errors.ts +211 -199
  70. package/src/index.ts +126 -113
  71. package/src/input-file.ts +246 -234
  72. package/src/integrity.ts +165 -121
  73. package/src/layout.ts +63 -29
  74. package/src/locked-resolve.ts +471 -461
  75. package/src/lockfile/bun.ts +214 -207
  76. package/src/lockfile/detect.ts +80 -80
  77. package/src/lockfile/json.ts +91 -92
  78. package/src/lockfile/names.ts +9 -9
  79. package/src/lockfile/npm.ts +328 -308
  80. package/src/lockfile/parse.ts +32 -32
  81. package/src/lockfile/pnpm.ts +230 -225
  82. package/src/lockfile/tree-builder.ts +116 -116
  83. package/src/lockfile/types.ts +52 -52
  84. package/src/manifest.ts +129 -133
  85. package/src/node/archiver.ts +85 -0
  86. package/src/node/index.ts +13 -0
  87. package/src/options.ts +84 -84
  88. package/src/platform.ts +97 -101
  89. package/src/progress.ts +105 -106
  90. package/src/registry.ts +116 -115
  91. package/src/resolve.ts +560 -537
  92. package/src/schemas/lenient.ts +67 -60
  93. package/src/schemas/package-json.ts +11 -10
  94. package/src/spec.ts +123 -119
  95. package/src/types/value-of.ts +1 -1
  96. package/src/utils/is-record.ts +1 -1
@@ -4,13 +4,14 @@
4
4
  * Packages, edges and skip warnings accumulate here while a parser walks a
5
5
  * file, which is what lets each parser stay focused on its own encoding.
6
6
  */
7
- import semver from "semver"
8
- import { parseDependencyTarget } from "../dependency-range.js"
9
- import { LockedRootKind } from "../enums/edge-kind.js"
10
- import type { LockfileFormat } from "../enums/lockfile-format.js"
11
- import { LockfileError } from "../errors.js"
12
- import { packageKey } from "../resolve.js"
13
- import type { LockedEdge, LockedPackage, LockedRoot, LockedTree } from "./types.js"
7
+ import semver from "semver";
8
+
9
+ import { parseDependencyTarget } from "../dependency-range.js";
10
+ import { LockedRootKind } from "../enums/edge-kind.js";
11
+ import type { LockfileFormat } from "../enums/lockfile-format.js";
12
+ import { LockfileError } from "../errors.js";
13
+ import { packageKey } from "../resolve.js";
14
+ import type { LockedEdge, LockedPackage, LockedRoot, LockedTree } from "./types.js";
14
15
 
15
16
  /**
16
17
  * Which manifest block each root kind is declared in.
@@ -20,101 +21,101 @@ import type { LockedEdge, LockedPackage, LockedRoot, LockedTree } from "./types.
20
21
  * rather than iterating this.
21
22
  */
22
23
  export const fieldByRootKind = {
23
- [LockedRootKind.Prod]: "dependencies",
24
- [LockedRootKind.Optional]: "optionalDependencies",
25
- [LockedRootKind.Dev]: "devDependencies",
26
- [LockedRootKind.Peer]: "peerDependencies"
27
- } as const satisfies Record<LockedRootKind, string>
24
+ [LockedRootKind.Prod]: "dependencies",
25
+ [LockedRootKind.Optional]: "optionalDependencies",
26
+ [LockedRootKind.Dev]: "devDependencies",
27
+ [LockedRootKind.Peer]: "peerDependencies",
28
+ } as const satisfies Record<LockedRootKind, string>;
28
29
 
29
30
  export class TreeBuilder {
30
- readonly packages = new Map<string, LockedPackage>()
31
- readonly roots: Array<LockedRoot> = []
32
- readonly warnings: Array<string> = []
33
- readonly incomplete: Array<string> = []
34
- /** Names dropped on purpose, so an edge into one is explained, not alarming. */
35
- private readonly skipped = new Set<string>()
36
- private readonly seenWarnings = new Set<string>()
31
+ readonly packages = new Map<string, LockedPackage>();
32
+ readonly roots: Array<LockedRoot> = [];
33
+ readonly warnings: Array<string> = [];
34
+ readonly incomplete: Array<string> = [];
35
+ /** Names dropped on purpose, so an edge into one is explained, not alarming. */
36
+ private readonly skipped = new Set<string>();
37
+ private readonly seenWarnings = new Set<string>();
37
38
 
38
- add(pkg: LockedPackage): void {
39
- const key = packageKey(pkg.name, pkg.version)
40
- const existing = this.packages.get(key)
41
- if (existing === undefined) {
42
- this.packages.set(key, pkg)
43
- return
44
- }
45
- // The same package at the same version can appear at several paths in a
46
- // nested layout. They are the same bytes; merge the edge sets so a copy
47
- // recorded with fewer dependencies does not lose any.
48
- this.packages.set(key, {
49
- ...existing,
50
- dependencies: mergeEdges(existing.dependencies, pkg.dependencies)
51
- })
52
- }
39
+ add(pkg: LockedPackage): void {
40
+ const key = packageKey(pkg.name, pkg.version);
41
+ const existing = this.packages.get(key);
42
+ if (existing === undefined) {
43
+ this.packages.set(key, pkg);
44
+ return;
45
+ }
46
+ // The same package at the same version can appear at several paths in a
47
+ // nested layout. They are the same bytes; merge the edge sets so a copy
48
+ // recorded with fewer dependencies does not lose any.
49
+ this.packages.set(key, {
50
+ ...existing,
51
+ dependencies: mergeEdges(existing.dependencies, pkg.dependencies),
52
+ });
53
+ }
53
54
 
54
- addRoot(root: LockedRoot): void {
55
- if (this.roots.some((existing) => existing.name === root.name)) return
56
- this.roots.push(root)
57
- }
55
+ addRoot(root: LockedRoot): void {
56
+ if (this.roots.some((existing) => existing.name === root.name)) return;
57
+ this.roots.push(root);
58
+ }
58
59
 
59
- warn(message: string): void {
60
- if (this.seenWarnings.has(message)) return
61
- this.seenWarnings.add(message)
62
- this.warnings.push(message)
63
- }
60
+ warn(message: string): void {
61
+ if (this.seenWarnings.has(message)) return;
62
+ this.seenWarnings.add(message);
63
+ this.warnings.push(message);
64
+ }
64
65
 
65
- /** Notes an entry that is real but cannot come from a registry. */
66
- skip(name: string, spec: string, reason: string): void {
67
- this.skipped.add(name)
68
- this.warn(`skipped ${name}@${spec} (${reason}) — not fetchable from a registry`)
69
- }
66
+ /** Notes an entry that is real but cannot come from a registry. */
67
+ skip(name: string, spec: string, reason: string): void {
68
+ this.skipped.add(name);
69
+ this.warn(`skipped ${name}@${spec} (${reason}) — not fetchable from a registry`);
70
+ }
70
71
 
71
- /**
72
- * Records a required edge that resolved to nothing.
73
- *
74
- * An edge into something skipped on purpose is expected — you cannot bundle a
75
- * `workspace:` sibling from a registry — so it stays a warning. Anything else
76
- * means the file does not pin what it claims to.
77
- */
78
- dangling(from: string, name: string): void {
79
- if (this.skipped.has(name)) {
80
- this.warn(`${from} requires ${name}, which is not fetchable from a registry`)
81
- return
82
- }
83
- this.incomplete.push(`${name} (required by ${from})`)
84
- }
72
+ /**
73
+ * Records a required edge that resolved to nothing.
74
+ *
75
+ * An edge into something skipped on purpose is expected — you cannot bundle a
76
+ * `workspace:` sibling from a registry — so it stays a warning. Anything else
77
+ * means the file does not pin what it claims to.
78
+ */
79
+ dangling(from: string, name: string): void {
80
+ if (this.skipped.has(name)) {
81
+ this.warn(`${from} requires ${name}, which is not fetchable from a registry`);
82
+ return;
83
+ }
84
+ this.incomplete.push(`${name} (required by ${from})`);
85
+ }
85
86
 
86
- finish(input: {
87
- readonly format: LockfileFormat
88
- readonly lockfileVersion: string
89
- readonly path: string
90
- readonly importer?: string | undefined
91
- }): LockedTree {
92
- if (this.packages.size === 0) {
93
- throw new LockfileError(input.path, "lockfile pins no packages that can be bundled")
94
- }
95
- return {
96
- format: input.format,
97
- lockfileVersion: input.lockfileVersion,
98
- path: input.path,
99
- importer: input.importer,
100
- packages: this.packages,
101
- roots: this.roots,
102
- warnings: this.warnings,
103
- incomplete: this.incomplete
104
- }
105
- }
87
+ finish(input: {
88
+ readonly format: LockfileFormat;
89
+ readonly lockfileVersion: string;
90
+ readonly path: string;
91
+ readonly importer?: string | undefined;
92
+ }): LockedTree {
93
+ if (this.packages.size === 0) {
94
+ throw new LockfileError(input.path, "lockfile pins no packages that can be bundled");
95
+ }
96
+ return {
97
+ format: input.format,
98
+ lockfileVersion: input.lockfileVersion,
99
+ path: input.path,
100
+ importer: input.importer,
101
+ packages: this.packages,
102
+ roots: this.roots,
103
+ warnings: this.warnings,
104
+ incomplete: this.incomplete,
105
+ };
106
+ }
106
107
  }
107
108
 
108
109
  const mergeEdges = (
109
- a: ReadonlyArray<LockedEdge>,
110
- b: ReadonlyArray<LockedEdge>
110
+ a: ReadonlyArray<LockedEdge>,
111
+ b: ReadonlyArray<LockedEdge>,
111
112
  ): ReadonlyArray<LockedEdge> => {
112
- const merged = new Map<string, LockedEdge>()
113
- for (const edge of [...a, ...b]) {
114
- merged.set(`${edge.name}@${edge.version}|${edge.kind}`, edge)
115
- }
116
- return [...merged.values()]
117
- }
113
+ const merged = new Map<string, LockedEdge>();
114
+ for (const edge of [...a, ...b]) {
115
+ merged.set(`${edge.name}@${edge.version}|${edge.kind}`, edge);
116
+ }
117
+ return [...merged.values()];
118
+ };
118
119
 
119
120
  /**
120
121
  * Reads the right-hand side of a locked entry as an exact version.
@@ -125,18 +126,18 @@ const mergeEdges = (
125
126
  * stays identical between the two paths.
126
127
  */
127
128
  export const asExactVersion = (
128
- name: string,
129
- raw: string
129
+ name: string,
130
+ raw: string,
130
131
  ):
131
- | { readonly _tag: "Version"; readonly version: string }
132
- | { readonly _tag: "Unsupported"; readonly reason: string } => {
133
- const exact = semver.valid(raw, { loose: true })
134
- if (exact !== null) return { _tag: "Version", version: exact }
132
+ | { readonly _tag: "Version"; readonly version: string }
133
+ | { readonly _tag: "Unsupported"; readonly reason: string } => {
134
+ const exact = semver.valid(raw, { loose: true });
135
+ if (exact !== null) return { _tag: "Version", version: exact };
135
136
 
136
- const target = parseDependencyTarget(name, raw)
137
- if (target._tag === "Unsupported") return { _tag: "Unsupported", reason: target.reason }
138
- return { _tag: "Unsupported", reason: "not an exact version" }
139
- }
137
+ const target = parseDependencyTarget(name, raw);
138
+ if (target._tag === "Unsupported") return { _tag: "Unsupported", reason: target.reason };
139
+ return { _tag: "Unsupported", reason: "not an exact version" };
140
+ };
140
141
 
141
142
  /**
142
143
  * Raised when a lockfile found by walking up turns out not to cover the
@@ -146,21 +147,20 @@ export const asExactVersion = (
146
147
  * readable, it simply belongs to a different project, and bundling it would
147
148
  * produce someone else's dependency set.
148
149
  */
149
- export const notCovered = (
150
- path: string,
151
- importer: string,
152
- known: ReadonlyArray<string>
153
- ): never => {
154
- const listed = known.slice(0, 8).map((entry) => ` ${entry}`).join("\n")
155
- throw new LockfileError(
156
- path,
157
- `it does not cover ${importer}.\n` +
158
- (known.length === 0
159
- ? " It records no workspace members."
160
- : ` Workspace members it does record:\n${listed}` +
161
- (known.length > 8 ? `\n … and ${known.length - 8} more` : ""))
162
- )
163
- }
150
+ export const notCovered = (path: string, importer: string, known: ReadonlyArray<string>): never => {
151
+ const listed = known
152
+ .slice(0, 8)
153
+ .map((entry) => ` ${entry}`)
154
+ .join("\n");
155
+ throw new LockfileError(
156
+ path,
157
+ `it does not cover ${importer}.\n` +
158
+ (known.length === 0
159
+ ? " It records no workspace members."
160
+ : ` Workspace members it does record:\n${listed}` +
161
+ (known.length > 8 ? `\n … and ${known.length - 8} more` : "")),
162
+ );
163
+ };
164
164
 
165
165
  /** `""` is how npm and bun spell the project at the lockfile's own level. */
166
- export const importerKey = (importer: string): string => (importer === "." ? "" : importer)
166
+ export const importerKey = (importer: string): string => (importer === "." ? "" : importer);
@@ -5,70 +5,70 @@
5
5
  * different ways; once flattened into this graph, nothing downstream has to
6
6
  * know which manager wrote the file.
7
7
  */
8
- import type { EdgeKind, LockedRootKind } from "../enums/edge-kind.js"
9
- import type { LockfileFormat } from "../enums/lockfile-format.js"
8
+ import type { EdgeKind, LockedRootKind } from "../enums/edge-kind.js";
9
+ import type { LockfileFormat } from "../enums/lockfile-format.js";
10
10
 
11
11
  /** One edge out of a locked package, already pinned to an exact version. */
12
12
  export type LockedEdge = {
13
- readonly name: string
14
- readonly version: string
15
- readonly kind: EdgeKind
16
- }
13
+ readonly name: string;
14
+ readonly version: string;
15
+ readonly kind: EdgeKind;
16
+ };
17
17
 
18
18
  /** One package pinned by a lockfile. */
19
19
  export type LockedPackage = {
20
- readonly name: string
21
- readonly version: string
22
- readonly dependencies: ReadonlyArray<LockedEdge>
23
- }
20
+ readonly name: string;
21
+ readonly version: string;
22
+ readonly dependencies: ReadonlyArray<LockedEdge>;
23
+ };
24
24
 
25
25
  /** A direct dependency of the locked project. */
26
26
  export type LockedRoot = {
27
- readonly name: string
28
- readonly version: string
29
- readonly kind: LockedRootKind
30
- /** The range the manifest asked for, kept for reporting. */
31
- readonly specifier?: string | undefined
32
- }
27
+ readonly name: string;
28
+ readonly version: string;
29
+ readonly kind: LockedRootKind;
30
+ /** The range the manifest asked for, kept for reporting. */
31
+ readonly specifier?: string | undefined;
32
+ };
33
33
 
34
34
  /** Options for reading a lockfile. */
35
35
  export type LockfileParseOptions = {
36
- /**
37
- * Which workspace member's dependencies become the roots, as a path relative
38
- * to the lockfile in POSIX form — `"packages/core"`, or `"."` for the project
39
- * at the lockfile's own level.
40
- *
41
- * Left undefined, every workspace member in the file contributes, which is
42
- * what pointing `--file` straight at a lockfile should mean. It matters when
43
- * a package.json found a lockfile by walking *up*: the file then pins the
44
- * whole repository, and only one member of it was asked for.
45
- */
46
- readonly importer?: string | undefined
47
- }
36
+ /**
37
+ * Which workspace member's dependencies become the roots, as a path relative
38
+ * to the lockfile in POSIX form — `"packages/core"`, or `"."` for the project
39
+ * at the lockfile's own level.
40
+ *
41
+ * Left undefined, every workspace member in the file contributes, which is
42
+ * what pointing `--file` straight at a lockfile should mean. It matters when
43
+ * a package.json found a lockfile by walking *up*: the file then pins the
44
+ * whole repository, and only one member of it was asked for.
45
+ */
46
+ readonly importer?: string | undefined;
47
+ };
48
48
 
49
49
  /** A whole lockfile, flattened. */
50
50
  export type LockedTree = {
51
- readonly format: LockfileFormat
52
- /** As the file declares it: `3`, `'9.0'`, `1`. */
53
- readonly lockfileVersion: string
54
- /** Where it was read from, for error messages. */
55
- readonly path: string
56
- /** The workspace member the roots came from, when one was selected. */
57
- readonly importer?: string | undefined
58
- /** Every pinned package, keyed by `name@version`. */
59
- readonly packages: ReadonlyMap<string, LockedPackage>
60
- /** The project's direct dependencies. */
61
- readonly roots: ReadonlyArray<LockedRoot>
62
- /** Entries that were recognised but cannot be fetched from a registry. */
63
- readonly warnings: ReadonlyArray<string>
64
- /**
65
- * Required edges pointing at nothing the file pins.
66
- *
67
- * Distinct from `warnings`: a `workspace:` dependency that cannot be bundled
68
- * is expected and gets a warning, while an ordinary registry dependency with
69
- * no entry means the lockfile is genuinely partial and the bundle it produces
70
- * would be missing a package. The caller decides what to do about it, but the
71
- * two must not be confused.
72
- */
73
- readonly incomplete: ReadonlyArray<string>
74
- }
51
+ readonly format: LockfileFormat;
52
+ /** As the file declares it: `3`, `'9.0'`, `1`. */
53
+ readonly lockfileVersion: string;
54
+ /** Where it was read from, for error messages. */
55
+ readonly path: string;
56
+ /** The workspace member the roots came from, when one was selected. */
57
+ readonly importer?: string | undefined;
58
+ /** Every pinned package, keyed by `name@version`. */
59
+ readonly packages: ReadonlyMap<string, LockedPackage>;
60
+ /** The project's direct dependencies. */
61
+ readonly roots: ReadonlyArray<LockedRoot>;
62
+ /** Entries that were recognised but cannot be fetched from a registry. */
63
+ readonly warnings: ReadonlyArray<string>;
64
+ /**
65
+ * Required edges pointing at nothing the file pins.
66
+ *
67
+ * Distinct from `warnings`: a `workspace:` dependency that cannot be bundled
68
+ * is expected and gets a warning, while an ordinary registry dependency with
69
+ * no entry means the lockfile is genuinely partial and the bundle it produces
70
+ * would be missing a package. The caller decides what to do about it, but the
71
+ * two must not be confused.
72
+ */
73
+ readonly incomplete: ReadonlyArray<string>;
74
+ };