@vltpkg/cli-js 1.1.1 → 1.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.
Files changed (82) hide show
  1. package/{access-YYMAAPHV.js → access-FTHPFPUJ.js} +8 -8
  2. package/{bugs-RK5JBPSC.js → bugs-QS6HQUO2.js} +17 -17
  3. package/{build-DVJSYCNF.js → build-H27SXMAS.js} +11 -11
  4. package/{cache-JKQNGZ7L.js → cache-IB4HMFDT.js} +143 -14
  5. package/cache-unzip-src-unzip.js +2934 -164
  6. package/{chunk-BWEH7DHQ.js → chunk-3CUFJPBK.js} +1 -1
  7. package/{chunk-CBASIDQH.js → chunk-55DYW2MP.js} +1 -1
  8. package/{chunk-6FT6K5YN.js → chunk-5BD4QGW2.js} +25 -7
  9. package/{chunk-WE2TQN32.js → chunk-5PP77V44.js} +2 -2
  10. package/{chunk-LN2A74PF.js → chunk-5YB6UIC3.js} +247 -37
  11. package/{chunk-CEHPRPGQ.js → chunk-6A3XS52F.js} +33 -0
  12. package/{chunk-X2AWQGYG.js → chunk-6G7MTZGS.js} +6 -6
  13. package/{chunk-V4YCZZHY.js → chunk-7KFMQ3BR.js} +16 -8
  14. package/{chunk-JZJWASXL.js → chunk-B2FK2HHL.js} +79 -58
  15. package/{chunk-DF5K4CYR.js → chunk-FOJCK57G.js} +5 -5
  16. package/{chunk-GDT6KI4I.js → chunk-FSZF2RSQ.js} +4 -4
  17. package/{chunk-VFBCWAOC.js → chunk-G5BSQ6DP.js} +7 -0
  18. package/{chunk-XKVU23NC.js → chunk-G5NJ7CYI.js} +1 -1
  19. package/{chunk-34TE4HZS.js → chunk-GUATFMMU.js} +2 -2
  20. package/{chunk-YBVTY6ZW.js → chunk-JTUZP2OD.js} +1 -1
  21. package/{chunk-MZA6N2TA.js → chunk-LD4BQJBG.js} +4 -4
  22. package/{chunk-KDF7PEBY.js → chunk-LEGPLJWK.js} +5 -5
  23. package/{chunk-XIF734VO.js → chunk-LKQ5R4VX.js} +4 -4
  24. package/{chunk-DR4Z63B6.js → chunk-NJZSCUD4.js} +3 -3
  25. package/{chunk-2PWSMJD7.js → chunk-O44Z6MAV.js} +1 -1
  26. package/{chunk-DS7YMIHP.js → chunk-OE6VHHN4.js} +135 -36
  27. package/{src-MOKZQTH2.js → chunk-QA6RE2GS.js} +249 -28
  28. package/chunk-SPRVRSEL.js +75 -0
  29. package/{chunk-YOE3VZFX.js → chunk-SYGF27W7.js} +2 -2
  30. package/{chunk-LR42WTKN.js → chunk-UNWQVOLY.js} +14 -12
  31. package/{chunk-SNOBGFNM.js → chunk-UQUIO7LW.js} +47 -6
  32. package/{chunk-QQYTPGIL.js → chunk-VHHLO5L6.js} +1 -1
  33. package/{chunk-3LUM7NX5.js → chunk-YFNQ6HZ3.js} +3 -3
  34. package/{chunk-GDJ7JT2R.js → chunk-YTT3UHUB.js} +1 -1
  35. package/{ci-WVYVR4XK.js → ci-XJBYXFFJ.js} +10 -10
  36. package/cli-package.json +2 -1
  37. package/{config-VYOTYBGS.js → config-4Y46SH5K.js} +8 -8
  38. package/{config-2MFZVP2N.js → config-H2Q4ABSS.js} +12 -12
  39. package/{create-OJPLHBRR.js → create-LUYL2QI4.js} +18 -18
  40. package/{deprecate-V4PKGFF7.js → deprecate-5ANCI2L6.js} +13 -13
  41. package/{dist-tag-DNLV4VDE.js → dist-tag-SD2CRWIL.js} +8 -8
  42. package/{docs-LXHDCGL5.js → docs-B6LVNWTP.js} +17 -17
  43. package/{exec-P32YSP44.js → exec-CJ5KQA72.js} +18 -18
  44. package/{exec-cache-JZ5PDTOY.js → exec-cache-EE7DAOW2.js} +16 -16
  45. package/{exec-local-HHA464H4.js → exec-local-HT3VBN6S.js} +15 -15
  46. package/{help-GVTTOYUL.js → help-GZ44WYDS.js} +4 -4
  47. package/{init-RENGG74N.js → init-HVXRXFVY.js} +10 -10
  48. package/{install-V6VXOBH2.js → install-E2C7EE6P.js} +16 -16
  49. package/{list-7N7Y5OXB.js → list-V4V6SHJP.js} +18 -18
  50. package/{login-D2QSAUJC.js → login-VRFV7MCE.js} +12 -12
  51. package/{logout-QPOYBQNF.js → logout-SU75R7BN.js} +8 -8
  52. package/{pack-4YYGXP3A.js → pack-IVGKUXTL.js} +11 -11
  53. package/package.json +1 -1
  54. package/{ping-QYRIZONU.js → ping-WB222SDT.js} +8 -8
  55. package/{pkg-WQTNABKT.js → pkg-LAAFNONA.js} +11 -11
  56. package/{profile-PRI7WXK7.js → profile-P3AVHRDI.js} +8 -8
  57. package/{publish-PSBYJQS7.js → publish-IUNB3KH5.js} +17 -17
  58. package/{query-UFYLQW4N.js → query-XCN55INS.js} +18 -18
  59. package/{registry-YTJZ3AQD.js → registry-QQLWJHJI.js} +5 -5
  60. package/registry-client-src-revalidate.js +5940 -5804
  61. package/{repo-4S6ETFXX.js → repo-BQDWUCX5.js} +17 -17
  62. package/{reporter-GLWYHI2Y.js → reporter-5BVUNWQ6.js} +13 -13
  63. package/{run-OU7NUB7A.js → run-QT7DX243.js} +15 -15
  64. package/{run-exec-QPBY7ULE.js → run-exec-XYUI7GLN.js} +15 -15
  65. package/security-archive-src-update-expired.js +1 -1
  66. package/{setup-23QXG473.js → setup-CXDJXRAG.js} +18 -18
  67. package/{src-3GN7TDNH.js → src-2BUT4WYP.js} +7 -5
  68. package/src-OKMGXMAD.js +212 -0
  69. package/{src-575IYJOL.js → src-UBAERP25.js} +14 -14
  70. package/{token-FCIA3RGD.js → token-CBOK6DMT.js} +8 -8
  71. package/{uninstall-BQVZVKB5.js → uninstall-27VP46KJ.js} +12 -12
  72. package/{unpublish-YGJ5HEYF.js → unpublish-F4637OQX.js} +8 -8
  73. package/{update-WCGV66JK.js → update-NGWLJJ22.js} +15 -15
  74. package/{version-7EA3UQG7.js → version-WPD2KNVC.js} +13 -13
  75. package/{view-5FWIENVE.js → view-YKFTRRPD.js} +11 -11
  76. package/vlr.js +1 -1
  77. package/vlrx.js +1 -1
  78. package/vlt.js +1 -1
  79. package/vlx.js +1 -1
  80. package/vlxl.js +1 -1
  81. package/{whoami-O4ZTBIC4.js → whoami-I5Q6RTAP.js} +8 -8
  82. package/chunk-WD74RJXW.js +0 -75
@@ -11,7 +11,7 @@ import {createRequire as _vlt_createRequire} from "node:module";
11
11
  var require = _vlt_createRequire(import.meta.filename);
12
12
  import {
13
13
  stderr
14
- } from "./chunk-LR42WTKN.js";
14
+ } from "./chunk-UNWQVOLY.js";
15
15
  import {
16
16
  urlOpen
17
17
  } from "./chunk-APMYKJI2.js";
@@ -12,7 +12,7 @@ var require = _vlt_createRequire(import.meta.filename);
12
12
  import {
13
13
  commandAliases,
14
14
  jack
15
- } from "./chunk-CEHPRPGQ.js";
15
+ } from "./chunk-6A3XS52F.js";
16
16
 
17
17
  // ../../src/cli-sdk/src/config/usage.ts
18
18
  var toArr = (v) => Array.isArray(v) ? v : [v];
@@ -11,36 +11,40 @@ import {createRequire as _vlt_createRequire} from "node:module";
11
11
  var require = _vlt_createRequire(import.meta.filename);
12
12
  import {
13
13
  PackageInfoClient
14
- } from "./chunk-LN2A74PF.js";
14
+ } from "./chunk-5YB6UIC3.js";
15
15
  import {
16
16
  Monorepo,
17
17
  PackageJson,
18
18
  PathScurry
19
- } from "./chunk-X2AWQGYG.js";
19
+ } from "./chunk-6G7MTZGS.js";
20
20
  import {
21
21
  resetCaches
22
- } from "./chunk-YOE3VZFX.js";
22
+ } from "./chunk-SYGF27W7.js";
23
23
  import {
24
24
  commands,
25
25
  defaultValues,
26
26
  definition,
27
27
  getCommand,
28
28
  isRecordField,
29
- recordFields
30
- } from "./chunk-CEHPRPGQ.js";
29
+ recordFields,
30
+ storeLinkers
31
+ } from "./chunk-6A3XS52F.js";
31
32
  import {
32
33
  find,
33
34
  load,
34
35
  reload,
35
36
  save
36
37
  } from "./chunk-HTOTG4TS.js";
38
+ import {
39
+ storeRoot
40
+ } from "./chunk-UQUIO7LW.js";
37
41
  import {
38
42
  assertRecordStringString,
39
43
  assertRecordStringT,
40
44
  getOptions,
41
45
  isObject,
42
46
  isRecordStringString
43
- } from "./chunk-VFBCWAOC.js";
47
+ } from "./chunk-G5BSQ6DP.js";
44
48
  import {
45
49
  error
46
50
  } from "./chunk-WZWDS3W4.js";
@@ -175,7 +179,7 @@ var pairsToRecords = (obj) => {
175
179
  var recordsToPairs = (obj) => {
176
180
  return Object.fromEntries(
177
181
  Object.entries(obj).filter(
178
- ([k]) => !(k === "scurry" || k === "packageJson" || k === "monorepo" || k === "projectRoot" || k === "packageInfo")
182
+ ([k]) => !(k === "scurry" || k === "packageJson" || k === "monorepo" || k === "projectRoot" || k === "storeRoot" || k === "packageInfo")
179
183
  ).map(([k, v]) => [
180
184
  k,
181
185
  k === "command" && v && typeof v === "object" ? (
@@ -236,6 +240,7 @@ var Config = class _Config {
236
240
  const asRecords = pairsToRecords(this.parse().values);
237
241
  const extras = {
238
242
  projectRoot: this.projectRoot,
243
+ storeRoot: storeRoot(asRecords.cache),
239
244
  scurry,
240
245
  packageJson,
241
246
  monorepo: Monorepo.maybeLoad(this.projectRoot, {
@@ -330,6 +335,9 @@ var Config = class _Config {
330
335
  parse(args = process.argv) {
331
336
  if (isParsed(this)) return this;
332
337
  this.#originalArgs = [...args];
338
+ const badLinker = process.env.VLT_STORE_LINKER;
339
+ const fixLinker = badLinker !== void 0 && !storeLinkers.includes(badLinker);
340
+ if (fixLinker) process.env.VLT_STORE_LINKER = "unpack";
333
341
  const envKeys = Object.keys(defaultValues).filter(
334
342
  (k) => process.env[envKey(k)] !== void 0
335
343
  );
@@ -346,6 +354,10 @@ var Config = class _Config {
346
354
  const cmdPairs = cmdSpecific ? recordsToPairs(cmdSpecific) : {};
347
355
  if (cmdSpecific) this.jack.setConfigValues(cmdPairs);
348
356
  this.jack.applyDefaults(p);
357
+ if (p.values["store-linker"] === "auto" && process.platform !== "linux") {
358
+ p.values["store-linker"] = "unpack";
359
+ envValues["store-linker"] = "unpack";
360
+ }
349
361
  const values = p.values;
350
362
  const explicit = {};
351
363
  for (const k of /* @__PURE__ */ new Set([...envKeys, ...cliKeys])) {
@@ -386,6 +398,12 @@ var Config = class _Config {
386
398
  if (p.values.verbose && p.values.loglevel === "info") {
387
399
  p.values.loglevel = "verbose";
388
400
  }
401
+ const { loglevel } = p.values;
402
+ if (fixLinker && loglevel !== "silent" && loglevel !== "error") {
403
+ console.error(
404
+ `Warning: invalid VLT_STORE_LINKER ${JSON.stringify(badLinker)}, using ${p.values["store-linker"]}`
405
+ );
406
+ }
389
407
  if (!isParsed(this)) throw error("failed to parse config");
390
408
  return this;
391
409
  }
@@ -16,10 +16,10 @@ import {
16
16
  } from "./chunk-ZWKI5OSF.js";
17
17
  import {
18
18
  minimatch
19
- } from "./chunk-X2AWQGYG.js";
19
+ } from "./chunk-6G7MTZGS.js";
20
20
  import {
21
21
  Spec
22
- } from "./chunk-XKVU23NC.js";
22
+ } from "./chunk-G5NJ7CYI.js";
23
23
  import {
24
24
  parse,
25
25
  stringify
@@ -20,22 +20,25 @@ import {
20
20
  pickManifest,
21
21
  resolve,
22
22
  revs
23
- } from "./chunk-X2AWQGYG.js";
23
+ } from "./chunk-6G7MTZGS.js";
24
24
  import {
25
25
  Spec
26
- } from "./chunk-XKVU23NC.js";
26
+ } from "./chunk-G5NJ7CYI.js";
27
27
  import {
28
- registryErrorMessage
29
- } from "./chunk-SNOBGFNM.js";
28
+ registryErrorMessage,
29
+ storeRoot,
30
+ tokenRefusalAdvice
31
+ } from "./chunk-UQUIO7LW.js";
30
32
  import {
31
33
  logRequest
32
34
  } from "./chunk-U7TEQCBN.js";
33
- import {
34
- asPackument
35
- } from "./chunk-VFBCWAOC.js";
36
35
  import {
37
36
  XDG
38
37
  } from "./chunk-XNLSTHDK.js";
38
+ import {
39
+ asPackument,
40
+ integrityHex
41
+ } from "./chunk-G5BSQ6DP.js";
39
42
  import {
40
43
  error
41
44
  } from "./chunk-WZWDS3W4.js";
@@ -63,6 +66,59 @@ import {
63
66
  } from "node:path";
64
67
  import { debuglog } from "node:util";
65
68
 
69
+ // ../../src/package-info/src/capabilities.ts
70
+ var none = Object.freeze({});
71
+ var asked = /* @__PURE__ */ new Map();
72
+ var known = /* @__PURE__ */ new Map();
73
+ var peekCapabilities = (client, registry) => {
74
+ const url = capabilitiesUrl(registry);
75
+ const seen = known.get(url);
76
+ if (seen) return seen;
77
+ void getCapabilities(client, registry);
78
+ return void 0;
79
+ };
80
+ var getCapabilities = async (client, registry) => {
81
+ const url = capabilitiesUrl(registry);
82
+ const seen = asked.get(url);
83
+ if (seen) return seen;
84
+ const doc = fetchCapabilities(client, url).then((caps) => {
85
+ known.set(url, caps);
86
+ return caps;
87
+ });
88
+ asked.set(url, doc);
89
+ return doc;
90
+ };
91
+ var capabilitiesUrl = (registry) => String(new URL("-/vlt/capabilities", registry));
92
+ var fetchCapabilities = async (client, url) => {
93
+ try {
94
+ const response = await client.request(url, {
95
+ headers: { accept: "application/json" }
96
+ });
97
+ if (response.statusCode !== 200) return none;
98
+ return asCapabilities(response.json());
99
+ } catch {
100
+ return none;
101
+ }
102
+ };
103
+ var asCapabilities = (body) => {
104
+ if (typeof body !== "object" || body === null) return none;
105
+ const doc = body;
106
+ const caps = {};
107
+ for (const key of [
108
+ "manifests",
109
+ "resolve",
110
+ "stable-filter"
111
+ ]) {
112
+ const version = doc[key];
113
+ if (typeof version === "string") caps[key] = version;
114
+ }
115
+ const { mimeTypes } = doc;
116
+ if (Array.isArray(mimeTypes) && mimeTypes.every((type) => typeof type === "string")) {
117
+ caps.mimeTypes = mimeTypes;
118
+ }
119
+ return caps;
120
+ };
121
+
66
122
  // ../../src/package-info/src/rename.ts
67
123
  import { rename as fsRename, rm } from "node:fs/promises";
68
124
  var { platform } = process;
@@ -86,13 +142,20 @@ var rename = platform !== "win32" ? fsRename : (async function(oldPath, newPath)
86
142
  var debug = debuglog("vlt");
87
143
  var xdg = new XDG("vlt");
88
144
  var delimiter = "~";
89
- var PACKUMENT_ACCEPT = "application/vnd.vlt.packument-v1+json; q=1.0, application/json; q=0.8, */*; q=0.1";
145
+ var VLT_PACKUMENT_MIME = "application/vnd.vlt.packument-v1+json";
146
+ var PACKUMENT_ACCEPT = `${VLT_PACKUMENT_MIME}; q=1.0, application/json; q=0.8, */*; q=0.1`;
90
147
  var manifestCacheMaxAge = 5 * 60 * 1e3;
91
148
  var noRegistryError = (spec) => error(
92
149
  'No registry configured to resolve this spec. Set "registry" in vlt.json, pass --registry, or run `vlt login --registry=<url>`. See https://docs.vlt.sh/cli',
93
150
  { code: "ECONFIG", spec }
94
151
  );
95
152
  var isMovingSelector = (f) => !!(f.distTag || f.range?.isAny);
153
+ var isStableSelector = (f) => {
154
+ const { range } = f;
155
+ if (!range) return false;
156
+ return !range.isAny && !range.raw.includes("-");
157
+ };
158
+ var storeLinkers = /* @__PURE__ */ new Set(["auto", "hardlink", "copy"]);
96
159
  var PackageInfoClient = class {
97
160
  #registryClient;
98
161
  #projectRoot;
@@ -102,8 +165,26 @@ var PackageInfoClient = class {
102
165
  packageJson;
103
166
  monorepo;
104
167
  #trustedIntegrities = /* @__PURE__ */ new Map();
168
+ // `${registry}${name}` of every packument served as VLT_PACKUMENT_MIME
169
+ #vltPackuments = /* @__PURE__ */ new Set();
105
170
  #manifestCacheMinAge = Date.now() - manifestCacheMaxAge;
106
171
  #cachePath;
172
+ #storeRoot;
173
+ #storeLinker;
174
+ #storeHits = { link: 0, copy: 0 };
175
+ #storeMisses = 0;
176
+ #storeHitRateLogged = false;
177
+ #logStoreHitRate = () => {
178
+ const { link, copy } = this.#storeHits;
179
+ const n = Math.max(1, link + copy + this.#storeMisses);
180
+ debug(
181
+ "global store: linked=%d copied=%d missed=%d hit rate=%s%%",
182
+ link,
183
+ copy,
184
+ this.#storeMisses,
185
+ ((link + copy) / n * 100).toFixed(1)
186
+ );
187
+ };
107
188
  // In-flight coalescing key is `${registry}${name}` — no representation
108
189
  // component. Safe only because every caller requests the same full
109
190
  // packument (see #fetchPackument). The one thing that does vary per
@@ -123,15 +204,47 @@ var PackageInfoClient = class {
123
204
  #tarPoolPromise;
124
205
  async getRegistryClient() {
125
206
  if (this.#registryClient) return this.#registryClient;
126
- this.#registryClientPromise ??= import("./src-3GN7TDNH.js").then(({ RegistryClient }) => {
207
+ this.#registryClientPromise ??= import("./src-2BUT4WYP.js").then(({ RegistryClient }) => {
127
208
  this.#registryClient = new RegistryClient(this.options);
128
209
  return this.#registryClient;
129
210
  });
130
211
  return this.#registryClientPromise;
131
212
  }
213
+ /**
214
+ * The vlt extensions `registry` serves, from its
215
+ * `GET /-/vlt/capabilities` document. A registry that does not answer
216
+ * one reads as an empty document, so a missing key means unsupported.
217
+ */
218
+ async capabilities(registry) {
219
+ return getCapabilities(await this.getRegistryClient(), registry);
220
+ }
221
+ /**
222
+ * Whether the packument for `f` can be fetched with `?stable`: the
223
+ * selector has to be one a prerelease cannot answer, and the registry
224
+ * must not have told us it does not serve the filter.
225
+ *
226
+ * Never waits on the capability document, which would put a round trip in
227
+ * front of the first packument of every cold install. Until that document
228
+ * arrives the answer is yes: a registry that does not know `?stable`
229
+ * ignores the parameter and serves the full packument, which resolution
230
+ * reads just as well. Once the document does arrive it is authoritative,
231
+ * so a registry that does not serve the filter stops being asked with it.
232
+ */
233
+ #stable(f) {
234
+ const { registry } = f;
235
+ if (!registry || !isStableSelector(f)) return false;
236
+ const client = this.#registryClient;
237
+ if (!client) {
238
+ void this.capabilities(registry).catch(() => {
239
+ });
240
+ return true;
241
+ }
242
+ const caps = peekCapabilities(client, registry);
243
+ return !caps || !!caps["stable-filter"];
244
+ }
132
245
  async getTarPool() {
133
246
  if (this.#tarPool) return this.#tarPool;
134
- this.#tarPoolPromise ??= import("./src-MOKZQTH2.js").then(
247
+ this.#tarPoolPromise ??= import("./src-OKMGXMAD.js").then(
135
248
  ({ Pool }) => {
136
249
  this.#tarPool = new Pool();
137
250
  return this.#tarPool;
@@ -154,6 +267,9 @@ var PackageInfoClient = class {
154
267
  packageJson: this.packageJson
155
268
  });
156
269
  this.#cachePath = options.cache ?? xdg.cache();
270
+ this.#storeRoot = options.storeRoot ?? storeRoot(this.#cachePath);
271
+ const linker = options["store-linker"];
272
+ this.#storeLinker = linker && storeLinkers.has(linker) ? linker : "unpack";
157
273
  void mkdir(pathResolve(this.#cachePath, "package-info"), {
158
274
  recursive: true
159
275
  }).catch(() => {
@@ -166,7 +282,8 @@ var PackageInfoClient = class {
166
282
  from = this.#projectRoot,
167
283
  integrity,
168
284
  resolved,
169
- fromLockfile = false
285
+ fromLockfile = false,
286
+ installScripts = false
170
287
  } = options;
171
288
  const f = spec.final;
172
289
  const alreadyResolved = !!(integrity && resolved);
@@ -204,14 +321,44 @@ var PackageInfoClient = class {
204
321
  }
205
322
  }
206
323
  case "registry": {
207
- const cached = (await this.getRegistryClient()).cachedBody(
208
- r.resolved,
209
- { integrity: r.integrity }
210
- );
324
+ const pool = await this.getTarPool();
325
+ const hex = f.type === "registry" && this.#storeLinker !== "unpack" ? integrityHex(r.integrity) : void 0;
326
+ const copy = this.#storeLinker === "copy" || installScripts;
327
+ if (hex) {
328
+ if (debug.enabled && !this.#storeHitRateLogged) {
329
+ this.#storeHitRateLogged = true;
330
+ process.once("beforeExit", this.#logStoreHitRate);
331
+ }
332
+ const linked = await pool.linkFromStore(
333
+ pathResolve(this.#storeRoot, hex),
334
+ target,
335
+ { copy }
336
+ );
337
+ if (linked) {
338
+ const { how, index } = linked;
339
+ this.#storeHits[how]++;
340
+ logRequest(r.resolved, how === "link" ? "store" : "cache");
341
+ return {
342
+ ...r,
343
+ manifest: index.manifest,
344
+ // implies scripts, so most packages skip the scan
345
+ bindingGyp: index.scripts && index.files.some((f2) => f2[0] === "binding.gyp")
346
+ };
347
+ }
348
+ this.#storeMisses++;
349
+ }
350
+ const rc = await this.getRegistryClient();
351
+ const cached = rc.cachedBody(r.resolved, {
352
+ integrity: r.integrity
353
+ });
211
354
  if (cached) {
212
355
  try {
213
- await (await this.getTarPool()).unpack(cached.body, target);
356
+ await pool.unpack(cached.body, target);
214
357
  logRequest(r.resolved, "cache");
358
+ r.integrity ??= cached.integrity;
359
+ if (hex || cached.gzip) {
360
+ rc.queueForStore(cached.key, r.integrity);
361
+ }
215
362
  return r;
216
363
  } catch (er) {
217
364
  debug(
@@ -226,6 +373,7 @@ var PackageInfoClient = class {
226
373
  const response = await (await this.getRegistryClient()).request(r.resolved, {
227
374
  integrity: r.integrity,
228
375
  trustIntegrity,
376
+ verifyDigest: r.digestRequired ? "required" : true,
229
377
  ...useCache === false ? { useCache } : {}
230
378
  });
231
379
  if (response.statusCode !== 200) {
@@ -246,19 +394,25 @@ var PackageInfoClient = class {
246
394
  );
247
395
  }
248
396
  const buf2 = response.buffer();
249
- if (r.integrity && !fromLockfile && !response.fromCache) {
250
- const hash = createHash("sha512");
251
- hash.update(buf2);
252
- const computed = `sha512-${hash.digest("base64")}`;
253
- if (computed !== r.integrity) {
254
- throw error("Tarball integrity check failed", {
255
- code: "EINTEGRITY",
256
- spec,
257
- url: r.resolved,
258
- wanted: r.integrity,
259
- found: computed
260
- });
397
+ if (r.integrity) {
398
+ if (!fromLockfile && !response.fromCache) {
399
+ const hash = createHash("sha512");
400
+ hash.update(buf2);
401
+ const computed = `sha512-${hash.digest("base64")}`;
402
+ if (computed !== r.integrity) {
403
+ throw error("Tarball integrity check failed", {
404
+ code: "EINTEGRITY",
405
+ spec,
406
+ url: r.resolved,
407
+ wanted: r.integrity,
408
+ found: computed
409
+ });
410
+ }
261
411
  }
412
+ } else if (response.fromCache) {
413
+ r.integrity = response.integrity;
414
+ } else {
415
+ r.integrity = response.integrityActual;
262
416
  }
263
417
  return buf2;
264
418
  };
@@ -460,6 +614,9 @@ var PackageInfoClient = class {
460
614
  ...options,
461
615
  integrity,
462
616
  trustIntegrity,
617
+ // no dist.integrity: checked against the digest the registry
618
+ // sent with it instead, before the client caches it
619
+ verifyDigest: this.#vltPackuments.has(`${f.registry}${f.name}`) ? "required" : true,
463
620
  ...useCache === false ? { useCache } : {}
464
621
  });
465
622
  if (response.statusCode !== 200) {
@@ -595,21 +752,30 @@ var PackageInfoClient = class {
595
752
  });
596
753
  throw new Error("manifest cache expired");
597
754
  }
755
+ if (json.__VLT_PACKUMENT) {
756
+ this.#vltPackuments.add(`${f.registry}${f.name}`);
757
+ delete json.__VLT_PACKUMENT;
758
+ }
598
759
  return json;
599
760
  } catch {
600
761
  }
601
762
  }
602
763
  const mani = pickManifest(
603
- await this.packument(f, options),
764
+ await this.#packument(f, options, this.#stable(f)),
604
765
  spec,
605
766
  options
606
767
  );
607
768
  if (!mani) throw this.#resolveError(spec, options);
608
769
  if (cachePath && !this.#manifestWritePaths.has(cachePath)) {
609
770
  this.#manifestWritePaths.add(cachePath);
771
+ const vlt = this.#vltPackuments.has(
772
+ `${f.registry}${f.name}`
773
+ );
610
774
  void this.#writeManifestCache(
611
775
  cachePath,
612
- JSON.stringify(mani)
776
+ JSON.stringify(
777
+ vlt ? { ...mani, __VLT_PACKUMENT: true } : mani
778
+ )
613
779
  );
614
780
  }
615
781
  return mani;
@@ -699,7 +865,15 @@ var PackageInfoClient = class {
699
865
  }
700
866
  }
701
867
  }
868
+ /**
869
+ * The packument for `spec`, with every version the registry has. Callers
870
+ * that only pick a manifest out of it go through `#packument` instead,
871
+ * which can ask for the prerelease-free one.
872
+ */
702
873
  async packument(spec, options = {}) {
874
+ return this.#packument(spec, options, false);
875
+ }
876
+ async #packument(spec, options, stable) {
703
877
  if (typeof spec === "string")
704
878
  spec = Spec.parse(spec, this.options);
705
879
  const f = spec.final;
@@ -738,12 +912,25 @@ var PackageInfoClient = class {
738
912
  case "registry": {
739
913
  const { registry, name } = f;
740
914
  if (!registry) throw noRegistryError(spec);
741
- const packumentKey = `${registry}${name}`;
915
+ if (options.full)
916
+ return this.#fetchPackument(
917
+ spec,
918
+ options,
919
+ new URL(name, registry)
920
+ );
921
+ const fullKey = `${registry}${name}`;
922
+ const packumentKey = stable ? `${fullKey}?stable` : fullKey;
742
923
  const forced = isMovingSelector(f);
743
- const inflight = this.#packumentPromises.get(packumentKey);
924
+ const inflight = this.#packumentPromises.get(packumentKey) ?? // the full packument is a superset of the stable one, so an
925
+ // in-flight full request answers a stable ask too, and a package
926
+ // wanted both ways is still fetched once
927
+ (stable ? this.#packumentPromises.get(fullKey) : void 0);
744
928
  if (inflight && (!forced || inflight.forced))
745
929
  return inflight.promise;
746
- const pakuURL = new URL(name, registry);
930
+ const pakuURL = new URL(
931
+ stable ? `${name}?stable` : name,
932
+ registry
933
+ );
747
934
  const promise = this.#fetchPackument(spec, options, pakuURL);
748
935
  const record = { promise, forced };
749
936
  this.#packumentPromises.set(packumentKey, record);
@@ -759,8 +946,10 @@ var PackageInfoClient = class {
759
946
  }
760
947
  async #fetchPackument(spec, options, pakuURL, useCache) {
761
948
  const response = await (await this.getRegistryClient()).request(pakuURL, {
762
- headers: { accept: PACKUMENT_ACCEPT },
763
- ...useCache === false ? { useCache } : {},
949
+ headers: {
950
+ accept: options.full ? "application/json" : PACKUMENT_ACCEPT
951
+ },
952
+ ...useCache === false || options.full ? { useCache: false } : {},
764
953
  // costs a conditional GET per moving selector on an otherwise warm
765
954
  // cache, install included. 304s are cheap but not free; the
766
955
  // alternative is serving a dist tag that moved (#1656).
@@ -777,14 +966,21 @@ var PackageInfoClient = class {
777
966
  }
778
967
  );
779
968
  }
969
+ let paku;
780
970
  try {
781
- return response.json();
971
+ paku = response.json();
782
972
  } catch (er) {
783
973
  if (useCache !== false) {
784
974
  return this.#fetchPackument(spec, options, pakuURL, false);
785
975
  }
786
976
  throw er;
787
977
  }
978
+ const { registry, name } = spec.final;
979
+ if (response.contentType.startsWith(VLT_PACKUMENT_MIME)) {
980
+ this.#vltPackuments.add(`${registry}${name}`);
981
+ }
982
+ if (registry) absolutizeTarballs(paku, registry);
983
+ return paku;
788
984
  }
789
985
  async resolve(spec, options = {}) {
790
986
  const memoKey = String(spec);
@@ -839,6 +1035,9 @@ var PackageInfoClient = class {
839
1035
  signatures,
840
1036
  spec
841
1037
  };
1038
+ if (!integrity && this.#vltPackuments.has(`${f.registry}${f.name}`)) {
1039
+ r.digestRequired = true;
1040
+ }
842
1041
  this.#resolutions.set(memoKey, r);
843
1042
  return r;
844
1043
  }
@@ -922,8 +1121,10 @@ var PackageInfoClient = class {
922
1121
  // error resolving
923
1122
  #resolveError(spec, options = {}, message = "Could not resolve", extra = {}) {
924
1123
  const { from = this.#projectRoot } = options;
1124
+ const advice = spec?.final.type === "registry" ? tokenRefusalAdvice(extra.response, extra.url) : void 0;
925
1125
  const er = error(
926
- message,
1126
+ advice ? `${message}
1127
+ \u26A0\uFE0F ${advice}` : message,
927
1128
  {
928
1129
  code: "ERESOLVE",
929
1130
  spec,
@@ -935,6 +1136,15 @@ var PackageInfoClient = class {
935
1136
  return er;
936
1137
  }
937
1138
  };
1139
+ var absolutizeTarballs = (paku, registry) => {
1140
+ const base = registry.endsWith("/") ? registry : registry + "/";
1141
+ for (const { dist } of Object.values(paku.versions)) {
1142
+ const tarball = dist?.tarball;
1143
+ if (tarball && !/^[a-z][a-z0-9+.-]*:/i.test(tarball)) {
1144
+ dist.tarball = String(new URL(tarball, base));
1145
+ }
1146
+ }
1147
+ };
938
1148
 
939
1149
  export {
940
1150
  PackageInfoClient
@@ -1164,6 +1164,12 @@ var recordFields = [
1164
1164
  "jsr-registries"
1165
1165
  ];
1166
1166
  var isRecordField = (s) => recordFields.includes(s);
1167
+ var storeLinkers = [
1168
+ "auto",
1169
+ "hardlink",
1170
+ "copy",
1171
+ "unpack"
1172
+ ];
1167
1173
  var j2 = jack({
1168
1174
  envPrefix: "VLT",
1169
1175
  allowPositionals: true,
@@ -1357,6 +1363,32 @@ var definition = j2.heading("Configuration").description(
1357
1363
  `,
1358
1364
  default: cacheDir
1359
1365
  },
1366
+ "store-linker": {
1367
+ hint: "auto | hardlink | copy | unpack",
1368
+ description: `How packages are placed in \`node_modules\`. Every
1369
+ linker but \`unpack\` fills a global store under
1370
+ \`cache\` in the background, then places packages
1371
+ from it.
1372
+
1373
+ - auto: Default. On Linux, hardlink from the global
1374
+ store, copying any file that cannot be linked.
1375
+ Elsewhere, same as \`unpack\`: a hardlink is the
1376
+ slowest way to place a file on APFS, so macOS and
1377
+ Windows unpack by default.
1378
+ - hardlink: Hardlink from the global store on every
1379
+ platform.
1380
+ - copy: Copy from the global store. Use this when
1381
+ editing files in \`node_modules\` in place, since
1382
+ a hardlink shares its content with every project.
1383
+ - unpack: Unpack each package tarball, skipping the
1384
+ global store.
1385
+
1386
+ Packages with install scripts are always copied.
1387
+ An invalid \`VLT_STORE_LINKER\` warns and uses
1388
+ \`unpack\`.`,
1389
+ validOptions: storeLinkers,
1390
+ default: "auto"
1391
+ },
1360
1392
  tag: {
1361
1393
  description: `Default \`dist-tag\` to install or publish`,
1362
1394
  default: "latest"
@@ -1847,6 +1879,7 @@ export {
1847
1879
  getCommand,
1848
1880
  recordFields,
1849
1881
  isRecordField,
1882
+ storeLinkers,
1850
1883
  definition,
1851
1884
  defaultValues,
1852
1885
  getSortedCliOptions,
@@ -13,10 +13,10 @@ import {
13
13
  graphRun,
14
14
  graphRunSync,
15
15
  joinDepIDTuple
16
- } from "./chunk-YOE3VZFX.js";
16
+ } from "./chunk-SYGF27W7.js";
17
17
  import {
18
18
  Spec
19
- } from "./chunk-XKVU23NC.js";
19
+ } from "./chunk-G5NJ7CYI.js";
20
20
  import {
21
21
  load,
22
22
  parse as parse2,
@@ -28,6 +28,9 @@ import {
28
28
  promiseSpawn,
29
29
  whichSync
30
30
  } from "./chunk-U7TEQCBN.js";
31
+ import {
32
+ ts
33
+ } from "./chunk-VE6CCH33.js";
31
34
  import {
32
35
  Range,
33
36
  Version,
@@ -38,10 +41,7 @@ import {
38
41
  normalizeManifest,
39
42
  parse,
40
43
  satisfies
41
- } from "./chunk-VFBCWAOC.js";
42
- import {
43
- ts
44
- } from "./chunk-VE6CCH33.js";
44
+ } from "./chunk-G5BSQ6DP.js";
45
45
  import {
46
46
  error
47
47
  } from "./chunk-WZWDS3W4.js";