@packall/core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/archive-6sqTn1C4.js +274 -0
- package/dist/archive-6sqTn1C4.js.map +1 -0
- package/dist/archive.d.ts +47 -15
- package/dist/archive.d.ts.map +1 -1
- package/dist/bundle.d.ts +6 -3
- package/dist/bundle.d.ts.map +1 -1
- package/dist/dependency-range.d.ts.map +1 -1
- package/dist/download.d.ts +2 -1
- package/dist/download.d.ts.map +1 -1
- package/dist/enums/artifact-kind.d.ts.map +1 -1
- package/dist/enums/edge-kind.d.ts.map +1 -1
- package/dist/enums/input-file-kind.d.ts.map +1 -1
- package/dist/enums/layout.d.ts.map +1 -1
- package/dist/enums/lockfile-format.d.ts.map +1 -1
- package/dist/enums/phase.d.ts.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +12 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +169 -344
- package/dist/index.js.map +1 -1
- package/dist/input-file.d.ts.map +1 -1
- package/dist/integrity.d.ts +37 -5
- package/dist/integrity.d.ts.map +1 -1
- package/dist/layout.d.ts +41 -7
- package/dist/layout.d.ts.map +1 -1
- package/dist/locked-resolve.d.ts.map +1 -1
- package/dist/lockfile/bun.d.ts.map +1 -1
- package/dist/lockfile/detect.d.ts.map +1 -1
- package/dist/lockfile/json.d.ts.map +1 -1
- package/dist/lockfile/names.d.ts.map +1 -1
- package/dist/lockfile/npm.d.ts.map +1 -1
- package/dist/lockfile/parse.d.ts.map +1 -1
- package/dist/lockfile/pnpm.d.ts.map +1 -1
- package/dist/lockfile/tree-builder.d.ts.map +1 -1
- package/dist/lockfile/types.d.ts.map +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/node/archiver.d.ts +5 -0
- package/dist/node/archiver.d.ts.map +1 -0
- package/dist/node/index.d.ts +13 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +55 -0
- package/dist/node/index.js.map +1 -0
- package/dist/options.d.ts +3 -3
- package/dist/options.d.ts.map +1 -1
- package/dist/platform.d.ts +5 -10
- package/dist/platform.d.ts.map +1 -1
- package/dist/progress.d.ts.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/resolve.d.ts.map +1 -1
- package/dist/schemas/lenient.d.ts.map +1 -1
- package/dist/schemas/package-json.d.ts.map +1 -1
- package/dist/spec.d.ts.map +1 -1
- package/dist/types/value-of.d.ts.map +1 -1
- package/dist/utils/is-record.d.ts.map +1 -1
- package/package.json +11 -10
- package/src/archive.ts +95 -92
- package/src/bundle.ts +736 -616
- package/src/dependency-range.ts +121 -115
- package/src/download.ts +155 -133
- package/src/enums/artifact-kind.ts +5 -5
- package/src/enums/edge-kind.ts +13 -13
- package/src/enums/input-file-kind.ts +9 -9
- package/src/enums/layout.ts +20 -20
- package/src/enums/lockfile-format.ts +6 -6
- package/src/enums/phase.ts +8 -8
- package/src/errors.ts +200 -198
- package/src/index.ts +126 -113
- package/src/input-file.ts +246 -234
- package/src/integrity.ts +165 -121
- package/src/layout.ts +63 -29
- package/src/locked-resolve.ts +471 -461
- package/src/lockfile/bun.ts +214 -207
- package/src/lockfile/detect.ts +80 -80
- package/src/lockfile/json.ts +91 -92
- package/src/lockfile/names.ts +9 -9
- package/src/lockfile/npm.ts +328 -308
- package/src/lockfile/parse.ts +32 -32
- package/src/lockfile/pnpm.ts +230 -225
- package/src/lockfile/tree-builder.ts +116 -116
- package/src/lockfile/types.ts +52 -52
- package/src/manifest.ts +129 -133
- package/src/node/archiver.ts +85 -0
- package/src/node/index.ts +13 -0
- package/src/options.ts +84 -84
- package/src/platform.ts +97 -101
- package/src/progress.ts +105 -106
- package/src/registry.ts +116 -115
- package/src/resolve.ts +560 -537
- package/src/schemas/lenient.ts +67 -60
- package/src/schemas/package-json.ts +11 -10
- package/src/spec.ts +123 -119
- package/src/types/value-of.ts +1 -1
- package/src/utils/is-record.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -1,227 +1,16 @@
|
|
|
1
|
+
import { _ as OutputError, a as layerCallback, b as RegistryUnreachableError, c as ArchiveError, d as InvalidInputFileError, f as InvalidSpecError, g as NoMatchingVersionsError, h as LockfileOutOfDateError, i as emit, l as AuthenticationError, m as LockfileIncompleteError, n as createArchive, o as layerSilent, p as LockfileError, r as Progress, s as makeCollector, t as Archiver, u as IntegrityError, v as PackageNotFoundError, x as VersionNotFoundError, y as RegistryResponseError } from "./archive-6sqTn1C4.js";
|
|
1
2
|
import * as Effect from "effect/Effect";
|
|
2
3
|
import * as Option from "effect/Option";
|
|
3
4
|
import * as Schema from "effect/Schema";
|
|
4
5
|
import * as Getter from "effect/SchemaGetter";
|
|
5
6
|
import semver from "semver";
|
|
6
7
|
import * as Context from "effect/Context";
|
|
7
|
-
import * as
|
|
8
|
-
import * as
|
|
9
|
-
import { createHash, timingSafeEqual } from "node:crypto";
|
|
8
|
+
import * as Crypto from "effect/Crypto";
|
|
9
|
+
import * as Encoding from "effect/Encoding";
|
|
10
10
|
import { parse } from "yaml";
|
|
11
11
|
import * as FileSystem from "effect/FileSystem";
|
|
12
12
|
import * as Path from "effect/Path";
|
|
13
|
-
import { create } from "tar";
|
|
14
13
|
|
|
15
|
-
//#region src/errors.ts
|
|
16
|
-
var BundlerErrorBase = class extends Error {
|
|
17
|
-
constructor(message, options) {
|
|
18
|
-
super(message, options);
|
|
19
|
-
this.name = new.target.name;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
/** A package spec on the command line or in an input file could not be parsed. */
|
|
23
|
-
var InvalidSpecError = class extends BundlerErrorBase {
|
|
24
|
-
_tag = "InvalidSpecError";
|
|
25
|
-
spec;
|
|
26
|
-
reason;
|
|
27
|
-
constructor(spec, reason) {
|
|
28
|
-
super(`Invalid package spec ${JSON.stringify(spec)}: ${reason}`);
|
|
29
|
-
this.spec = spec;
|
|
30
|
-
this.reason = reason;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
/** `--file` pointed at something that is neither a usable package.json nor a spec list. */
|
|
34
|
-
var InvalidInputFileError = class extends BundlerErrorBase {
|
|
35
|
-
_tag = "InvalidInputFileError";
|
|
36
|
-
path;
|
|
37
|
-
reason;
|
|
38
|
-
constructor(path, reason, options) {
|
|
39
|
-
super(`Cannot read specs from ${path}: ${reason}`, options);
|
|
40
|
-
this.path = path;
|
|
41
|
-
this.reason = reason;
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
/** A lockfile could not be read, or is in a format we do not support. */
|
|
45
|
-
var LockfileError = class extends BundlerErrorBase {
|
|
46
|
-
_tag = "LockfileError";
|
|
47
|
-
path;
|
|
48
|
-
reason;
|
|
49
|
-
constructor(path, reason, options) {
|
|
50
|
-
super(`Lockfile ${path}: ${reason}`, options);
|
|
51
|
-
this.path = path;
|
|
52
|
-
this.reason = reason;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* The lockfile parsed, but does not pin everything the bundle needs.
|
|
57
|
-
*
|
|
58
|
-
* Falling back to a range here would defeat the entire point of reading a
|
|
59
|
-
* lockfile — you would get a bundle that is *mostly* what CI installed, with no
|
|
60
|
-
* indication of which parts were guessed. So this is fatal, and it names every
|
|
61
|
-
* gap at once so one `npm install` fixes all of them.
|
|
62
|
-
*/
|
|
63
|
-
var LockfileIncompleteError = class extends BundlerErrorBase {
|
|
64
|
-
_tag = "LockfileIncompleteError";
|
|
65
|
-
path;
|
|
66
|
-
missing;
|
|
67
|
-
constructor(path, missing, detail, remedy) {
|
|
68
|
-
const shown = missing.slice(0, 10).map((entry) => ` ${entry}`).join("\n");
|
|
69
|
-
const rest = missing.length > 10 ? `\n … and ${missing.length - 10} more` : "";
|
|
70
|
-
super(`${path} is incomplete — ${detail}:\n${shown}${rest}\n ${remedy}\n Or pass --lockfile off to resolve version ranges fresh instead.`);
|
|
71
|
-
this.path = path;
|
|
72
|
-
this.missing = missing;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* The lockfile pins versions the registry no longer serves.
|
|
77
|
-
*
|
|
78
|
-
* Unpublished, or never mirrored into a private registry. Either way the
|
|
79
|
-
* bundle cannot be built as specified, and quietly substituting a nearby
|
|
80
|
-
* version would produce exactly the mismatch this feature exists to prevent.
|
|
81
|
-
*/
|
|
82
|
-
var LockfileOutOfDateError = class extends BundlerErrorBase {
|
|
83
|
-
_tag = "LockfileOutOfDateError";
|
|
84
|
-
path;
|
|
85
|
-
registry;
|
|
86
|
-
missing;
|
|
87
|
-
constructor(path, registry, missing) {
|
|
88
|
-
const shown = missing.slice(0, 10).map((entry) => ` ${entry.name}@${entry.version} — ${entry.detail}`).join("\n");
|
|
89
|
-
const rest = missing.length > 10 ? `\n … and ${missing.length - 10} more` : "";
|
|
90
|
-
super(`${path} pins ${missing.length} version${missing.length === 1 ? "" : "s"} that ${registry} does not serve:\n${shown}${rest}\n Refresh the lockfile against this registry, or pass --lockfile off to\n resolve version ranges fresh instead.`);
|
|
91
|
-
this.path = path;
|
|
92
|
-
this.registry = registry;
|
|
93
|
-
this.missing = missing.map(({ name, version }) => ({
|
|
94
|
-
name,
|
|
95
|
-
version
|
|
96
|
-
}));
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
/**
|
|
100
|
-
* The registry could not be reached at all — DNS failure, refused connection,
|
|
101
|
-
* proxy blackhole, or a preflight that timed out.
|
|
102
|
-
*
|
|
103
|
-
* This is the error that fixes "hangs forever with no network": we surface it
|
|
104
|
-
* quickly and say which host we could not reach.
|
|
105
|
-
*/
|
|
106
|
-
var RegistryUnreachableError = class extends BundlerErrorBase {
|
|
107
|
-
_tag = "RegistryUnreachableError";
|
|
108
|
-
registry;
|
|
109
|
-
timeoutMs;
|
|
110
|
-
constructor(registry, detail, options) {
|
|
111
|
-
super(`Cannot reach registry ${registry}: ${detail}.\n Check your network connection, VPN, and the proxy settings in your .npmrc.`, options);
|
|
112
|
-
this.registry = registry;
|
|
113
|
-
this.timeoutMs = options?.timeoutMs;
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
/** The registry answered, but has never heard of this package. */
|
|
117
|
-
var PackageNotFoundError = class extends BundlerErrorBase {
|
|
118
|
-
_tag = "PackageNotFoundError";
|
|
119
|
-
packageName;
|
|
120
|
-
registry;
|
|
121
|
-
constructor(packageName, registry) {
|
|
122
|
-
super(`Package "${packageName}" was not found on ${registry}`);
|
|
123
|
-
this.packageName = packageName;
|
|
124
|
-
this.registry = registry;
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
/** The package exists but the exact version requested does not. */
|
|
128
|
-
var VersionNotFoundError = class extends BundlerErrorBase {
|
|
129
|
-
_tag = "VersionNotFoundError";
|
|
130
|
-
packageName;
|
|
131
|
-
version;
|
|
132
|
-
available;
|
|
133
|
-
constructor(packageName, version, available) {
|
|
134
|
-
const tail = available.slice(-5).join(", ");
|
|
135
|
-
super(`${packageName}@${version} does not exist.` + (tail.length > 0 ? ` Most recent published versions: ${tail}` : ""));
|
|
136
|
-
this.packageName = packageName;
|
|
137
|
-
this.version = version;
|
|
138
|
-
this.available = available;
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
/** A range (or dist-tag) matched nothing that is actually published. */
|
|
142
|
-
var NoMatchingVersionsError = class extends BundlerErrorBase {
|
|
143
|
-
_tag = "NoMatchingVersionsError";
|
|
144
|
-
packageName;
|
|
145
|
-
selector;
|
|
146
|
-
available;
|
|
147
|
-
constructor(packageName, selector, available) {
|
|
148
|
-
const tail = available.slice(-5).join(", ");
|
|
149
|
-
super(`No published version of ${packageName} satisfies "${selector}".` + (tail.length > 0 ? ` Most recent published versions: ${tail}` : ""));
|
|
150
|
-
this.packageName = packageName;
|
|
151
|
-
this.selector = selector;
|
|
152
|
-
this.available = available;
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
/** The registry responded, but with something we cannot use. */
|
|
156
|
-
var RegistryResponseError = class extends BundlerErrorBase {
|
|
157
|
-
_tag = "RegistryResponseError";
|
|
158
|
-
url;
|
|
159
|
-
status;
|
|
160
|
-
constructor(url, detail, options) {
|
|
161
|
-
super(`Unexpected response from ${url}: ${detail}`, options);
|
|
162
|
-
this.url = url;
|
|
163
|
-
this.status = options?.status;
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
/** 401/403 — almost always a missing or stale token in `.npmrc`. */
|
|
167
|
-
var AuthenticationError = class extends BundlerErrorBase {
|
|
168
|
-
_tag = "AuthenticationError";
|
|
169
|
-
registry;
|
|
170
|
-
status;
|
|
171
|
-
constructor(registry, status, packageName) {
|
|
172
|
-
super(`Registry ${registry} rejected the request with HTTP ${status}` + (packageName ? ` while fetching "${packageName}"` : "") + `.\n Add credentials to your .npmrc, e.g.\n //${safeHost(registry)}/:_authToken=\${NPM_TOKEN}`);
|
|
173
|
-
this.registry = registry;
|
|
174
|
-
this.status = status;
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
/**
|
|
178
|
-
* A downloaded tarball did not match the checksum the registry advertised.
|
|
179
|
-
*
|
|
180
|
-
* Never soft-fail this: a bundle is a supply-chain artifact and a corrupt or
|
|
181
|
-
* substituted tarball is exactly what integrity checking exists to catch.
|
|
182
|
-
*/
|
|
183
|
-
var IntegrityError = class extends BundlerErrorBase {
|
|
184
|
-
_tag = "IntegrityError";
|
|
185
|
-
packageName;
|
|
186
|
-
version;
|
|
187
|
-
expected;
|
|
188
|
-
actual;
|
|
189
|
-
constructor(packageName, version, expected, actual) {
|
|
190
|
-
super(`Integrity check failed for ${packageName}@${version}.\n expected: ${expected}\n actual: ${actual}\n The download was discarded. This is either corruption in transit or a tampered artifact.`);
|
|
191
|
-
this.packageName = packageName;
|
|
192
|
-
this.version = version;
|
|
193
|
-
this.expected = expected;
|
|
194
|
-
this.actual = actual;
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
/** Something went wrong writing the `.tgz`. */
|
|
198
|
-
var ArchiveError = class extends BundlerErrorBase {
|
|
199
|
-
_tag = "ArchiveError";
|
|
200
|
-
path;
|
|
201
|
-
constructor(path, detail, options) {
|
|
202
|
-
super(`Failed to create archive ${path}: ${detail}`, options);
|
|
203
|
-
this.path = path;
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
/** Something went wrong preparing or writing to the output directory. */
|
|
207
|
-
var OutputError = class extends BundlerErrorBase {
|
|
208
|
-
_tag = "OutputError";
|
|
209
|
-
path;
|
|
210
|
-
constructor(path, detail, options) {
|
|
211
|
-
super(`Output error at ${path}: ${detail}`, options);
|
|
212
|
-
this.path = path;
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
const safeHost = (registry) => {
|
|
216
|
-
try {
|
|
217
|
-
const url = new URL(registry);
|
|
218
|
-
return url.host + url.pathname.replace(/\/+$/, "");
|
|
219
|
-
} catch {
|
|
220
|
-
return registry;
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
//#endregion
|
|
225
14
|
//#region src/enums/artifact-kind.ts
|
|
226
15
|
/** What a bundle run wrote to the output directory. */
|
|
227
16
|
const ArtifactKind = {
|
|
@@ -764,49 +553,25 @@ const defaultBundleOptions = {
|
|
|
764
553
|
var Registry = class extends Context.Service()("@packall/core/Registry") {};
|
|
765
554
|
|
|
766
555
|
//#endregion
|
|
767
|
-
//#region src/
|
|
768
|
-
/** Service tag for progress reporting. */
|
|
769
|
-
var Progress = class extends Context.Service()("@packall/core/Progress") {};
|
|
770
|
-
/** Emits an event to whichever reporter is installed. */
|
|
771
|
-
const emit = (event) => Effect.gen(function* () {
|
|
772
|
-
yield* (yield* Progress).emit(event);
|
|
773
|
-
});
|
|
774
|
-
/**
|
|
775
|
-
* Discards every event.
|
|
776
|
-
*
|
|
777
|
-
* The default for library consumers and for tests that do not care about
|
|
778
|
-
* progress — silence should never require ceremony.
|
|
779
|
-
*/
|
|
780
|
-
const layerSilent = Layer.succeed(Progress)({ emit: () => Effect.void });
|
|
781
|
-
/** Sends every event to a callback. Used by the CLI renderer. */
|
|
782
|
-
const layerCallback = (onEvent) => Layer.succeed(Progress)({ emit: (event) => Effect.sync(() => onEvent(event)) });
|
|
556
|
+
//#region src/integrity.ts
|
|
783
557
|
/**
|
|
784
|
-
*
|
|
558
|
+
* The digests npm publishes, mapped to what `Crypto.digest` calls them.
|
|
785
559
|
*
|
|
786
|
-
*
|
|
787
|
-
*
|
|
560
|
+
* Doubles as the supported-algorithm list: an SRI token whose algorithm is not
|
|
561
|
+
* a key here is one we cannot check, and is dropped during parsing.
|
|
788
562
|
*/
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
//#endregion
|
|
798
|
-
//#region src/integrity.ts
|
|
799
|
-
const SUPPORTED = new Set([
|
|
800
|
-
"sha512",
|
|
801
|
-
"sha384",
|
|
802
|
-
"sha256",
|
|
803
|
-
"sha1"
|
|
804
|
-
]);
|
|
563
|
+
const digestAlgorithmByName = {
|
|
564
|
+
sha512: "SHA-512",
|
|
565
|
+
sha384: "SHA-384",
|
|
566
|
+
sha256: "SHA-256",
|
|
567
|
+
sha1: "SHA-1"
|
|
568
|
+
};
|
|
569
|
+
const isSupported = (algorithm) => Object.hasOwn(digestAlgorithmByName, algorithm);
|
|
805
570
|
/**
|
|
806
571
|
* Parses an SRI string such as `sha512-abc...==`.
|
|
807
572
|
*
|
|
808
573
|
* npm permits several space-separated hashes; we keep every one we can verify
|
|
809
|
-
* and ignore algorithms
|
|
574
|
+
* and ignore algorithms we cannot.
|
|
810
575
|
*/
|
|
811
576
|
const parseIntegrity = (integrity) => {
|
|
812
577
|
const hashes = [];
|
|
@@ -816,7 +581,7 @@ const parseIntegrity = (integrity) => {
|
|
|
816
581
|
if (dash <= 0) continue;
|
|
817
582
|
const algorithm = token.slice(0, dash).toLowerCase();
|
|
818
583
|
const digest = token.slice(dash + 1);
|
|
819
|
-
if (!
|
|
584
|
+
if (!isSupported(algorithm) || digest.length === 0) continue;
|
|
820
585
|
hashes.push({
|
|
821
586
|
algorithm,
|
|
822
587
|
digest
|
|
@@ -824,15 +589,26 @@ const parseIntegrity = (integrity) => {
|
|
|
824
589
|
}
|
|
825
590
|
return hashes;
|
|
826
591
|
};
|
|
592
|
+
const digestBytes = (data, algorithm) => Effect.gen(function* () {
|
|
593
|
+
return yield* (yield* Crypto.Crypto).digest(digestAlgorithmByName[algorithm], data);
|
|
594
|
+
});
|
|
827
595
|
/** Computes the base64 digest of some bytes under one algorithm. */
|
|
828
|
-
const digestOf = (data, algorithm) =>
|
|
596
|
+
const digestOf = (data, algorithm) => Effect.map(digestBytes(data, algorithm), Encoding.encodeBase64);
|
|
829
597
|
/** Computes the hex digest of some bytes — the form legacy `shasum` uses. */
|
|
830
|
-
const hexDigestOf = (data, algorithm) =>
|
|
831
|
-
/**
|
|
598
|
+
const hexDigestOf = (data, algorithm) => Effect.map(digestBytes(data, algorithm), Encoding.encodeHex);
|
|
599
|
+
/**
|
|
600
|
+
* Constant-time comparison of two digest strings.
|
|
601
|
+
*
|
|
602
|
+
* Written by hand rather than with `node:crypto`'s `timingSafeEqual`, which
|
|
603
|
+
* needs `Buffer` and would pin this module to Node. Both digests here are
|
|
604
|
+
* public knowledge, so the timing property is close to theatre — but it costs
|
|
605
|
+
* four lines, and the alternative is explaining in a review why it was dropped.
|
|
606
|
+
*/
|
|
832
607
|
const digestsEqual = (a, b) => {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
608
|
+
if (a.length !== b.length) return false;
|
|
609
|
+
let difference = 0;
|
|
610
|
+
for (let index = 0; index < a.length; index++) difference |= a.charCodeAt(index) ^ b.charCodeAt(index);
|
|
611
|
+
return difference === 0;
|
|
836
612
|
};
|
|
837
613
|
/**
|
|
838
614
|
* Checks bytes against whichever checksums the registry supplied.
|
|
@@ -842,14 +618,14 @@ const digestsEqual = (a, b) => {
|
|
|
842
618
|
* as `Unverifiable` rather than silently treated as a pass — the caller decides
|
|
843
619
|
* whether to tolerate it.
|
|
844
620
|
*/
|
|
845
|
-
const verify = (data, dist) => {
|
|
621
|
+
const verify = (data, dist) => Effect.gen(function* () {
|
|
846
622
|
if (dist.integrity !== void 0 && dist.integrity.length > 0) {
|
|
847
|
-
const [best] = parseIntegrity(dist.integrity).toSorted((a, b) =>
|
|
623
|
+
const [best] = parseIntegrity(dist.integrity).toSorted((a, b) => strengthByAlgorithm[b.algorithm] - strengthByAlgorithm[a.algorithm]);
|
|
848
624
|
if (best === void 0) return {
|
|
849
625
|
_tag: "Unverifiable",
|
|
850
626
|
reason: `no supported algorithm in "${dist.integrity}"`
|
|
851
627
|
};
|
|
852
|
-
const actual = digestOf(data, best.algorithm);
|
|
628
|
+
const actual = yield* digestOf(data, best.algorithm);
|
|
853
629
|
return digestsEqual(actual, best.digest) ? {
|
|
854
630
|
_tag: "Verified",
|
|
855
631
|
using: best.algorithm
|
|
@@ -860,7 +636,7 @@ const verify = (data, dist) => {
|
|
|
860
636
|
};
|
|
861
637
|
}
|
|
862
638
|
if (dist.shasum !== void 0 && dist.shasum.length > 0) {
|
|
863
|
-
const actual = hexDigestOf(data, "sha1");
|
|
639
|
+
const actual = yield* hexDigestOf(data, "sha1");
|
|
864
640
|
return digestsEqual(actual.toLowerCase(), dist.shasum.toLowerCase()) ? {
|
|
865
641
|
_tag: "Verified",
|
|
866
642
|
using: "sha1"
|
|
@@ -874,14 +650,12 @@ const verify = (data, dist) => {
|
|
|
874
650
|
_tag: "Unverifiable",
|
|
875
651
|
reason: "registry advertised no integrity or shasum"
|
|
876
652
|
};
|
|
877
|
-
};
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
default: return 1;
|
|
884
|
-
}
|
|
653
|
+
});
|
|
654
|
+
const strengthByAlgorithm = {
|
|
655
|
+
sha512: 4,
|
|
656
|
+
sha384: 3,
|
|
657
|
+
sha256: 2,
|
|
658
|
+
sha1: 1
|
|
885
659
|
};
|
|
886
660
|
|
|
887
661
|
//#endregion
|
|
@@ -895,10 +669,12 @@ const strength = (algorithm) => {
|
|
|
895
669
|
* @babel/core/-/core-7.24.0.tgz
|
|
896
670
|
*
|
|
897
671
|
* That is not an Artifactory convention — it is the path structure in every
|
|
898
|
-
* `dist.tarball` URL npm publishes
|
|
899
|
-
*
|
|
900
|
-
*
|
|
901
|
-
*
|
|
672
|
+
* `dist.tarball` URL npm publishes. See `notes/requirements.md` § FR-12 for
|
|
673
|
+
* what that buys.
|
|
674
|
+
*
|
|
675
|
+
* The one shape worth noticing before editing anything here: a scoped
|
|
676
|
+
* package's file name drops the scope, so `@babel/core` becomes
|
|
677
|
+
* `core-7.24.0.tgz` under an `@babel/core/-/` directory.
|
|
902
678
|
*/
|
|
903
679
|
/** Splits `@scope/name` into its parts. `scope` is undefined when unscoped. */
|
|
904
680
|
const splitName = (name) => {
|
|
@@ -934,13 +710,45 @@ const README_FILE = "IMPORT.md";
|
|
|
934
710
|
/**
|
|
935
711
|
* Name of the archive produced for one root spec in `per-spec` layout.
|
|
936
712
|
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
*
|
|
713
|
+
* It is the spec, with the one character a file name cannot hold taken out:
|
|
714
|
+
* `@types/react@19.2.18` becomes `@types-react@19.2.18.tgz`. So the name of the
|
|
715
|
+
* file answers the question somebody has when they are looking at a directory
|
|
716
|
+
* of them — which package, and which version — in the notation they would have
|
|
717
|
+
* typed to ask for it.
|
|
718
|
+
*
|
|
719
|
+
* The version is joined with `@` rather than `-` for the same reason. A
|
|
720
|
+
* hyphen made `foo-bar-1.0.0` ambiguous by eye: package `foo` at `bar-1.0.0`,
|
|
721
|
+
* or `foo-bar` at `1.0.0`? npm's own syntax has no such problem, because a
|
|
722
|
+
* package name may not contain `@` beyond its scope.
|
|
723
|
+
*
|
|
724
|
+
* **The scope's `@` stays**, and it is the difference between a correct bundle
|
|
725
|
+
* and a quietly incomplete one.
|
|
726
|
+
*
|
|
727
|
+
* Dropping it — which this did, matching what `npm pack` produces — meant
|
|
728
|
+
* `@something/somewhat` and `something-somewhat` were both
|
|
729
|
+
* `something-somewhat-1.0.0.tgz`. Two different packages, one path: the second
|
|
730
|
+
* landed on the first, and the run reported two artifacts for the one file that
|
|
731
|
+
* survived. A bundle short a package, called complete, found at the far end of
|
|
732
|
+
* an air gap.
|
|
733
|
+
*
|
|
734
|
+
* Keeping the `@` makes that impossible by construction rather than by a check,
|
|
735
|
+
* because an unscoped npm name may not begin with one. It is also the more
|
|
736
|
+
* honest name: the scope is part of what the package *is*, and a directory of
|
|
737
|
+
* archives that has forgotten which ones were scoped is a directory somebody
|
|
738
|
+
* has to cross-reference against a manifest.
|
|
739
|
+
*
|
|
740
|
+
* It does not make every collision impossible — `@a/b-c` and `@a-b/c` still
|
|
741
|
+
* meet — so `assertDistinctOutputs` still runs. That pair is pathological; the
|
|
742
|
+
* scoped-against-unscoped one is `@babel/core` beside `babel-core`, which is a
|
|
743
|
+
* real migration somebody could be bundling.
|
|
744
|
+
*
|
|
745
|
+
* The leading `@` is safe here in a way it is not inside the archive: tar's
|
|
746
|
+
* splice convention applies to the *file list* it is given, which is what
|
|
747
|
+
* `archive.ts` guards with `./`. This is the output path, not an entry.
|
|
940
748
|
*/
|
|
941
749
|
const perSpecArchiveName = (name, version) => {
|
|
942
750
|
const { scope, bare } = splitName(name);
|
|
943
|
-
return `${scope === void 0 ? bare : `${scope
|
|
751
|
+
return `${scope === void 0 ? bare : `${scope}-${bare}`}@${version}.tgz`;
|
|
944
752
|
};
|
|
945
753
|
/** Name of the archive produced in `single` layout. */
|
|
946
754
|
const singleArchiveName = (base = "bundle") => `${base}.tgz`;
|
|
@@ -2545,66 +2353,6 @@ const formatSpecErrors = (errors, rawLines) => {
|
|
|
2545
2353
|
};
|
|
2546
2354
|
const describeError = (error) => error instanceof Error ? error.message : String(error);
|
|
2547
2355
|
|
|
2548
|
-
//#endregion
|
|
2549
|
-
//#region src/archive.ts
|
|
2550
|
-
/**
|
|
2551
|
-
* Packs `entries` (paths relative to `cwd`) into a gzipped tar at `outPath`.
|
|
2552
|
-
*
|
|
2553
|
-
* `portable` normalises uid/gid/mtime so the same inputs produce byte-identical
|
|
2554
|
-
* output — which matters when a security team wants to diff two bundles or
|
|
2555
|
-
* re-derive one from a manifest.
|
|
2556
|
-
*/
|
|
2557
|
-
const createArchive = (options) => Effect.gen(function* () {
|
|
2558
|
-
const fs = yield* FileSystem.FileSystem;
|
|
2559
|
-
yield* emit({
|
|
2560
|
-
_tag: "ArchiveStarted",
|
|
2561
|
-
path: options.outPath,
|
|
2562
|
-
entryCount: options.entries.length
|
|
2563
|
-
});
|
|
2564
|
-
yield* Effect.tryPromise({
|
|
2565
|
-
try: () => create({
|
|
2566
|
-
gzip: options.gzipLevel === void 0 ? true : { level: options.gzipLevel },
|
|
2567
|
-
file: options.outPath,
|
|
2568
|
-
cwd: options.cwd,
|
|
2569
|
-
portable: true,
|
|
2570
|
-
onWriteEntry: (entry) => {
|
|
2571
|
-
entry.path = entry.path.replace(/^\.\//, "");
|
|
2572
|
-
}
|
|
2573
|
-
}, options.entries.toSorted().map(dotSlash)),
|
|
2574
|
-
catch: (cause) => new ArchiveError(options.outPath, describe(cause), { cause })
|
|
2575
|
-
});
|
|
2576
|
-
const info = yield* fs.stat(options.outPath);
|
|
2577
|
-
const bytes = Number(info.size);
|
|
2578
|
-
yield* emit({
|
|
2579
|
-
_tag: "ArchiveCompleted",
|
|
2580
|
-
path: options.outPath,
|
|
2581
|
-
bytes
|
|
2582
|
-
});
|
|
2583
|
-
return {
|
|
2584
|
-
path: options.outPath,
|
|
2585
|
-
bytes
|
|
2586
|
-
};
|
|
2587
|
-
});
|
|
2588
|
-
/**
|
|
2589
|
-
* Guards every entry against tar's `@` convention.
|
|
2590
|
-
*
|
|
2591
|
-
* In a tar file list, a leading `@` means "splice in the entries of this other
|
|
2592
|
-
* archive" — a GNU convention `node-tar` implements by stripping the `@` and
|
|
2593
|
-
* looking for what is left. Every scoped package is a top-level entry starting
|
|
2594
|
-
* with `@`, so this hit the bundler in both possible ways:
|
|
2595
|
-
*
|
|
2596
|
-
* - `@oxc-project` became `oxc-project`, which does not exist, and the run died
|
|
2597
|
-
* with an ENOENT naming a path that appears nowhere in the staging tree.
|
|
2598
|
-
* - `@esbuild` became `esbuild`, which *does* exist — the unscoped package of
|
|
2599
|
-
* the same name sitting right next to it. No error, and every scoped tarball
|
|
2600
|
-
* silently missing from a bundle that reported success. That is the dangerous
|
|
2601
|
-
* one: you would not find out until the install failed behind the firewall.
|
|
2602
|
-
*
|
|
2603
|
-
* `./@scope` is not subject to the convention and resolves identically.
|
|
2604
|
-
*/
|
|
2605
|
-
const dotSlash = (entry) => entry.startsWith("./") ? entry : `./${entry}`;
|
|
2606
|
-
const describe = (cause) => cause instanceof Error ? cause.message : String(cause);
|
|
2607
|
-
|
|
2608
2356
|
//#endregion
|
|
2609
2357
|
//#region src/download.ts
|
|
2610
2358
|
/**
|
|
@@ -2635,7 +2383,7 @@ const downloadAll = (packages, destDir, options) => Effect.gen(function* () {
|
|
|
2635
2383
|
});
|
|
2636
2384
|
const bytes = yield* registry.download(pkg.manifest);
|
|
2637
2385
|
if (options.verifyIntegrity) {
|
|
2638
|
-
const result = verify(bytes, pkg.manifest.dist);
|
|
2386
|
+
const result = yield* verify(bytes, pkg.manifest.dist);
|
|
2639
2387
|
if (result._tag === "Mismatch") return yield* Effect.fail(new IntegrityError(pkg.name, pkg.version, result.expected, result.actual));
|
|
2640
2388
|
if (result._tag === "Unverifiable") yield* emit({
|
|
2641
2389
|
_tag: "Warning",
|
|
@@ -2679,11 +2427,44 @@ const downloadAll = (packages, destDir, options) => Effect.gen(function* () {
|
|
|
2679
2427
|
|
|
2680
2428
|
//#endregion
|
|
2681
2429
|
//#region src/bundle.ts
|
|
2430
|
+
/**
|
|
2431
|
+
* The archives `per-spec` emits — one per distinct root package version.
|
|
2432
|
+
*
|
|
2433
|
+
* Roots are *specs*, and several specs can name one package: `react`,
|
|
2434
|
+
* `react@latest` and `react@19.2.8` are three ways of asking for the same
|
|
2435
|
+
* thing, and after resolution they are indistinguishable. Without this they
|
|
2436
|
+
* were three archives — the same bytes, under the same file name, written over
|
|
2437
|
+
* each other. The run then reported three artifacts and three times the size
|
|
2438
|
+
* for one file on disk, and the duplication ratio it computes to decide whether
|
|
2439
|
+
* to ask about the layout counted them too, so the tool would offer to
|
|
2440
|
+
* deduplicate a bundle it had duplicated itself.
|
|
2441
|
+
*
|
|
2442
|
+
* Keyed by name and version rather than by the archive's file name. A scoped
|
|
2443
|
+
* name is flattened to make a file name, so `@foo/bar` and `foo-bar` can spell
|
|
2444
|
+
* the same one — and those are two different packages, which should collide
|
|
2445
|
+
* loudly rather than being merged here.
|
|
2446
|
+
*
|
|
2447
|
+
* Roots only. The packages *inside* an archive were deduplicated by resolution
|
|
2448
|
+
* long before this, which is why the same closure fetched from twenty specs is
|
|
2449
|
+
* fetched once.
|
|
2450
|
+
*/
|
|
2451
|
+
const perSpecTargets = (resolution) => {
|
|
2452
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2453
|
+
return resolution.roots.flatMap((root) => root.versions.flatMap((version) => {
|
|
2454
|
+
const key = `${root.spec.name}@${version}`;
|
|
2455
|
+
if (seen.has(key)) return [];
|
|
2456
|
+
seen.add(key);
|
|
2457
|
+
return {
|
|
2458
|
+
root,
|
|
2459
|
+
version
|
|
2460
|
+
};
|
|
2461
|
+
}));
|
|
2462
|
+
};
|
|
2682
2463
|
/** Computes the plan summary for a resolution. */
|
|
2683
2464
|
const summarize = (resolution) => {
|
|
2684
2465
|
let perSpecArchives = 0;
|
|
2685
2466
|
let perSpecEntries = 0;
|
|
2686
|
-
for (const root
|
|
2467
|
+
for (const { root, version } of perSpecTargets(resolution)) {
|
|
2687
2468
|
perSpecArchives += 1;
|
|
2688
2469
|
perSpecEntries += (root.closures.get(version) ?? []).length;
|
|
2689
2470
|
}
|
|
@@ -2775,11 +2556,11 @@ const bundleResolved = (resolution, options) => Effect.gen(function* () {
|
|
|
2775
2556
|
const plannedOutputs = (resolution, options) => {
|
|
2776
2557
|
if (options.layout === Layout.Dir) return [{ file: MANIFEST_FILE }, { file: README_FILE }];
|
|
2777
2558
|
if (options.layout === Layout.Single) return [{ file: singleArchiveName(options.archiveName ?? "bundle") }];
|
|
2778
|
-
return resolution.
|
|
2559
|
+
return perSpecTargets(resolution).map(({ root, version }) => ({
|
|
2779
2560
|
file: perSpecArchiveName(root.spec.name, version),
|
|
2780
2561
|
name: root.spec.name,
|
|
2781
2562
|
version
|
|
2782
|
-
}))
|
|
2563
|
+
}));
|
|
2783
2564
|
};
|
|
2784
2565
|
const runBundle = (resolution, options) => Effect.gen(function* () {
|
|
2785
2566
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -2889,7 +2670,7 @@ const emitPerSpec = (input, staging) => Effect.gen(function* () {
|
|
|
2889
2670
|
const index = indexPackages(input.resolution);
|
|
2890
2671
|
const artifacts = [];
|
|
2891
2672
|
let counter = 0;
|
|
2892
|
-
for (const root of input.resolution
|
|
2673
|
+
for (const { root, version } of perSpecTargets(input.resolution)) {
|
|
2893
2674
|
if (input.options.skipExisting?.has(perSpecArchiveName(root.spec.name, version)) === true) continue;
|
|
2894
2675
|
const included = includedPackages(index, root.closures.get(version) ?? []);
|
|
2895
2676
|
const rootDir = path.join(staging, "roots", String(counter++));
|
|
@@ -2970,6 +2751,48 @@ const topLevelEntries = (dir) => Effect.gen(function* () {
|
|
|
2970
2751
|
return (yield* (yield* FileSystem.FileSystem).readDirectory(dir)).toSorted();
|
|
2971
2752
|
});
|
|
2972
2753
|
/**
|
|
2754
|
+
* Two different packages must not want the same file name.
|
|
2755
|
+
*
|
|
2756
|
+
* Most of what this used to catch is now impossible: an archive is named for
|
|
2757
|
+
* the spec, `@types-react@19.2.18.tgz`, and an unscoped npm name may not begin
|
|
2758
|
+
* with `@`, so it cannot spell a scoped one. What survives is **two scoped
|
|
2759
|
+
* packages whose scope boundary falls in a different place**, because that
|
|
2760
|
+
* boundary is the one character the file name drops:
|
|
2761
|
+
*
|
|
2762
|
+
* @a/b-c -> @a-b-c@1.0.0.tgz
|
|
2763
|
+
* @a-b/c -> @a-b-c@1.0.0.tgz
|
|
2764
|
+
*
|
|
2765
|
+
* Pathological rather than plausible — it needs somebody to hold the `@a-b`
|
|
2766
|
+
* scope as well as `@a`, and both packages in one run at one version. It is
|
|
2767
|
+
* kept anyway because of what it costs to be wrong: they are *different
|
|
2768
|
+
* packages*, so deduplicating them would be wrong, and writing both is worse —
|
|
2769
|
+
* the second lands on the first and the run reports two artifacts for the one
|
|
2770
|
+
* file that survived. A bundle short a package, called complete, found at the
|
|
2771
|
+
* far end of an air gap by somebody who cannot go and fetch the missing one.
|
|
2772
|
+
*
|
|
2773
|
+
* Nothing else would catch it. The overwrite check below compares this run's
|
|
2774
|
+
* planned files against what is *already on disk*, and both of these are new.
|
|
2775
|
+
*
|
|
2776
|
+
* So it refuses, and names both specs. Not silently renamed: an archive whose
|
|
2777
|
+
* name nobody chose is a file somebody has to identify later.
|
|
2778
|
+
*
|
|
2779
|
+
* Unconditional, unlike the overwrite check: `--force` is permission to replace
|
|
2780
|
+
* what was already in the directory, not to let one run overwrite itself.
|
|
2781
|
+
*/
|
|
2782
|
+
const assertDistinctOutputs = (resolution, options) => Effect.gen(function* () {
|
|
2783
|
+
const owners = /* @__PURE__ */ new Map();
|
|
2784
|
+
for (const planned of plannedOutputs(resolution, options)) {
|
|
2785
|
+
const claimed = owners.get(planned.file);
|
|
2786
|
+
const spec = planned.name === void 0 ? planned.file : `${planned.name}@${planned.version}`;
|
|
2787
|
+
if (claimed === void 0) owners.set(planned.file, [spec]);
|
|
2788
|
+
else claimed.push(spec);
|
|
2789
|
+
}
|
|
2790
|
+
const collisions = [...owners].filter(([, specs]) => specs.length > 1);
|
|
2791
|
+
if (collisions.length === 0) return;
|
|
2792
|
+
const described = collisions.map(([file, specs]) => `${specs.join(" and ")} would both be ${file}`).join("; ");
|
|
2793
|
+
return yield* Effect.fail(new OutputError(options.outDir, `two packages want the same archive name — ${described}. The \`/\` in a scope becomes a \`-\` to make a file name, so \`@a/b-c\` and \`@a-b/c\` meet. Use --layout single, or bundle them into separate --out directories.`));
|
|
2794
|
+
});
|
|
2795
|
+
/**
|
|
2973
2796
|
* Fails if the run would overwrite something, unless `--force`.
|
|
2974
2797
|
*
|
|
2975
2798
|
* The guard is on the *files this run writes*, not on whether the directory has
|
|
@@ -2979,12 +2802,14 @@ const topLevelEntries = (dir) => Effect.gen(function* () {
|
|
|
2979
2802
|
* default `.` unusable.
|
|
2980
2803
|
*
|
|
2981
2804
|
* Checked before anything is downloaded. Discovering the collision *after* a
|
|
2982
|
-
* twenty-minute download would be a uniquely irritating way to fail
|
|
2805
|
+
* twenty-minute download would be a uniquely irritating way to fail — which is
|
|
2806
|
+
* also why `assertDistinctOutputs` runs from here rather than at emit time.
|
|
2983
2807
|
*/
|
|
2984
2808
|
const prepareOutputDir = (resolution, options) => Effect.gen(function* () {
|
|
2985
2809
|
const fs = yield* FileSystem.FileSystem;
|
|
2986
2810
|
const path = yield* Path.Path;
|
|
2987
2811
|
const outDir = options.outDir;
|
|
2812
|
+
yield* assertDistinctOutputs(resolution, options);
|
|
2988
2813
|
if (!options.force) {
|
|
2989
2814
|
const clashes = [];
|
|
2990
2815
|
for (const planned of plannedOutputs(resolution, options)) {
|
|
@@ -2998,5 +2823,5 @@ const prepareOutputDir = (resolution, options) => Effect.gen(function* () {
|
|
|
2998
2823
|
});
|
|
2999
2824
|
|
|
3000
2825
|
//#endregion
|
|
3001
|
-
export { ArchiveError, ArtifactKind, AuthenticationError, DirectOnlyKind, EdgeKind, InputFileKind, IntegrityError, InvalidInputFileError, InvalidSpecError, Layout, Layouts, LockedRootKind, LockfileError, LockfileFormat, LockfileIncompleteError, LockfileOutOfDateError, MANIFEST_FILE, MANIFEST_VERSION, NoMatchingVersionsError, OptionalFlagRecordOrAbsentSchema, OptionalFlagRecordSchema, OptionalStringArraySchema, OptionalStringRecordSchema, OutputError, PackageNotFoundError, Phase, Progress, README_FILE, Registry, RegistryResponseError, RegistryUnreachableError, StringArraySchema, StringRecordSchema, VersionNotFoundError, allPlatforms, buildManifest, bundle, bundleLocked, bundleResolved, currentPlatform, dedupeSpecs, defaultBundleOptions, defaultInputFileOptions, defaultResolveOptions, defaultScope, detectLockfile, digestOf, emit as emitProgress, formatByLockfileName, formatPlatformFilter, formatSelector, formatSpec, hexDigestOf, importGuide, indexPackages, isIncluded, isRecord, layerCallback as layerCallbackProgress, layerSilent as layerSilentProgress, lockfileNamesFor, makeCollector as makeProgressCollector, packageKey, packagePath, parseDependencyTarget, parseInputFile, parseIntegrity, parseLockfile, parsePlatformTarget, parseSpec, parseSpecs, perSpecArchiveName, plan, planLocked, plannedOutputs, platformTargets, readInputFile, resolve, resolveLocked, selectVersions, serializeManifest, singleArchiveName, splitName, summarize, tarballFileName, tolerant, verify as verifyIntegrity, withLayout };
|
|
2826
|
+
export { ArchiveError, Archiver, ArtifactKind, AuthenticationError, DirectOnlyKind, EdgeKind, InputFileKind, IntegrityError, InvalidInputFileError, InvalidSpecError, Layout, Layouts, LockedRootKind, LockfileError, LockfileFormat, LockfileIncompleteError, LockfileOutOfDateError, MANIFEST_FILE, MANIFEST_VERSION, NoMatchingVersionsError, OptionalFlagRecordOrAbsentSchema, OptionalFlagRecordSchema, OptionalStringArraySchema, OptionalStringRecordSchema, OutputError, PackageNotFoundError, Phase, Progress, README_FILE, Registry, RegistryResponseError, RegistryUnreachableError, StringArraySchema, StringRecordSchema, VersionNotFoundError, allPlatforms, buildManifest, bundle, bundleLocked, bundleResolved, createArchive, currentPlatform, dedupeSpecs, defaultBundleOptions, defaultInputFileOptions, defaultResolveOptions, defaultScope, detectLockfile, digestOf, emit as emitProgress, formatByLockfileName, formatPlatformFilter, formatSelector, formatSpec, hexDigestOf, importGuide, indexPackages, isIncluded, isRecord, layerCallback as layerCallbackProgress, layerSilent as layerSilentProgress, lockfileNamesFor, makeCollector as makeProgressCollector, packageKey, packagePath, parseDependencyTarget, parseInputFile, parseIntegrity, parseLockfile, parsePlatformTarget, parseSpec, parseSpecs, perSpecArchiveName, plan, planLocked, plannedOutputs, platformTargets, readInputFile, resolve, resolveLocked, selectVersions, serializeManifest, singleArchiveName, splitName, summarize, tarballFileName, tolerant, verify as verifyIntegrity, withLayout };
|
|
3002
2827
|
//# sourceMappingURL=index.js.map
|