@vltpkg/cli-js 1.3.3 → 1.3.5

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 (76) hide show
  1. package/{access-KMBBQIIT.js → access-X2ZZH2BV.js} +4 -4
  2. package/{bugs-CABZX76X.js → bugs-SF56YATZ.js} +12 -12
  3. package/{build-WNHKYA4E.js → build-RRJ645SV.js} +6 -6
  4. package/{cache-KKT4GIYD.js → cache-NETXHYQU.js} +6 -6
  5. package/{chunk-SH24EMXO.js → chunk-2B5STVCP.js} +150 -40
  6. package/{chunk-GZWJAGBL.js → chunk-35OBZITS.js} +3 -3
  7. package/{chunk-ROCFQPP2.js → chunk-75F2WCA5.js} +2 -2
  8. package/{chunk-MOL2DUEQ.js → chunk-7DHDJ5XY.js} +368 -51
  9. package/{chunk-YU2W5UPG.js → chunk-ADDCQBGR.js} +1 -1
  10. package/{chunk-MC6CESRA.js → chunk-AFUXQ3P4.js} +1 -1
  11. package/{chunk-LY5FNWRY.js → chunk-B2CTWVQM.js} +3 -3
  12. package/{chunk-CU752KW7.js → chunk-BCANUHR3.js} +1 -1
  13. package/{chunk-GA4LA6B2.js → chunk-EGWLQVDZ.js} +4 -4
  14. package/chunk-EWZQGHYC.js +75 -0
  15. package/{chunk-HCMAX6Q5.js → chunk-GO5X2SSV.js} +3 -3
  16. package/{chunk-TJBBYDUN.js → chunk-I2DZC2J3.js} +4 -4
  17. package/{chunk-BUKZHMMK.js → chunk-INEKXXUQ.js} +1 -0
  18. package/{chunk-QBXGMTDE.js → chunk-LHB4CVEH.js} +1 -1
  19. package/{chunk-I6QGYSB4.js → chunk-LHIDMYN2.js} +1 -1
  20. package/chunk-OFHNSAQ5.js +2155 -0
  21. package/{chunk-ZBJRZXSU.js → chunk-PBHCZOUY.js} +3 -3
  22. package/{chunk-3IDIPSH7.js → chunk-QJHTK7ML.js} +1 -1
  23. package/{chunk-ZWKI5OSF.js → chunk-SRWDPPLX.js} +0 -1377
  24. package/{chunk-CSZTT7QN.js → chunk-TOKUDCSW.js} +2 -2
  25. package/{chunk-ENSGPOLR.js → chunk-XU5WNX4S.js} +3 -1
  26. package/{chunk-DCFLXO2M.js → chunk-XUGOPOLF.js} +1 -1
  27. package/{chunk-ZRRNOLEG.js → chunk-ZZFFMXI2.js} +214 -105
  28. package/{ci-BGVGC5RF.js → ci-YX56QIMV.js} +7 -7
  29. package/cli-package.json +1 -1
  30. package/{config-AWX2VEKL.js → config-HO7N7FPS.js} +5 -5
  31. package/{config-FIKZ4VWG.js → config-SHRCBOCI.js} +6 -6
  32. package/{create-CHTG7KO3.js → create-LO6BUYPU.js} +13 -13
  33. package/{deprecate-W7KQ62HF.js → deprecate-CUMWWMAF.js} +7 -7
  34. package/{dist-tag-66CC6HT7.js → dist-tag-DJJDBPTJ.js} +4 -4
  35. package/{docs-HW4U5TGD.js → docs-KDCDJRO5.js} +12 -12
  36. package/{exec-BVVNAGFI.js → exec-KF4ANRPM.js} +13 -13
  37. package/{exec-cache-L3ONPM5C.js → exec-cache-GBLLYU5O.js} +11 -11
  38. package/{exec-local-NR3RYQDQ.js → exec-local-VCP4KR6K.js} +10 -10
  39. package/{help-5YL6SUAN.js → help-MV53HEUI.js} +4 -4
  40. package/{init-RI2WVCGL.js → init-N3BM2FKC.js} +7 -7
  41. package/{install-OWAI4EOW.js → install-DDYIRJWL.js} +12 -12
  42. package/{list-4ZFX7ANG.js → list-VTCG6GBS.js} +12 -12
  43. package/{login-6TL3EE6Q.js → login-MCX6LCP5.js} +8 -8
  44. package/{logout-VQWHVHBE.js → logout-FPNGIQDZ.js} +4 -4
  45. package/{pack-Q5IRRAMU.js → pack-KCVLNNVZ.js} +9 -9
  46. package/package.json +1 -1
  47. package/{ping-2WV3V7GB.js → ping-MXTB7GFE.js} +4 -4
  48. package/{pkg-RXB6BRS5.js → pkg-QDEHGO34.js} +8 -8
  49. package/{profile-GNGWS5H2.js → profile-GQUM6NRG.js} +4 -4
  50. package/{publish-PI74W4GH.js → publish-UEL4KXDZ.js} +12 -12
  51. package/{query-NESDWXGE.js → query-MJTEJIZW.js} +12 -12
  52. package/{registry-MXAA4HVJ.js → registry-PY2L5ZWC.js} +2 -2
  53. package/registry-client-src-revalidate.js +150 -40
  54. package/{repo-PPBN3SKP.js → repo-XDMXV744.js} +12 -12
  55. package/{reporter-XBHCUW7W.js → reporter-K3D4AJBF.js} +6 -6
  56. package/{run-7GTWIFMV.js → run-MKEP7YKW.js} +10 -10
  57. package/{run-exec-VLSH5SOB.js → run-exec-Q7JNLVVV.js} +10 -10
  58. package/security-archive-src-update-expired.js +1 -1
  59. package/{setup-6O7RFGU2.js → setup-DYQY6DIX.js} +10 -10
  60. package/{src-EJSVFFXB.js → src-3MEJQYBG.js} +21 -7
  61. package/{src-6E7KCC7J.js → src-BZJHOEYT.js} +9 -9
  62. package/{src-U4MCZFJT.js → src-ODLA6WPY.js} +2 -2
  63. package/{token-NZ4HODXE.js → token-RUBMQURU.js} +4 -4
  64. package/{uninstall-KBZ6PJBG.js → uninstall-3NJXWOKS.js} +8 -8
  65. package/{unpublish-JBWSIBRX.js → unpublish-3ETADFWK.js} +4 -4
  66. package/{update-X4EC6MPP.js → update-L6ETRX6Q.js} +11 -11
  67. package/{version-QEHPAFAE.js → version-RHWAFXPK.js} +7 -7
  68. package/{view-WGGZTYAT.js → view-ABHAULFH.js} +7 -7
  69. package/vlr.js +1 -1
  70. package/vlrx.js +1 -1
  71. package/vlt.js +1 -1
  72. package/vlx.js +1 -1
  73. package/vlxl.js +1 -1
  74. package/{whoami-RYNXOULV.js → whoami-YDR7Q4VT.js} +4 -4
  75. package/chunk-7JK53T6G.js +0 -75
  76. package/chunk-KH3ERNCA.js +0 -783
@@ -10,9 +10,8 @@ globalThis.clearInterval = _vlt_clearInterval;
10
10
  import {createRequire as _vlt_createRequire} from "node:module";
11
11
  var require = _vlt_createRequire(import.meta.filename);
12
12
  import {
13
- Qn,
14
- require_lib
15
- } from "./chunk-ZWKI5OSF.js";
13
+ Qn
14
+ } from "./chunk-SRWDPPLX.js";
16
15
  import {
17
16
  Monorepo,
18
17
  PackageJson,
@@ -20,7 +19,7 @@ import {
20
19
  pickManifest,
21
20
  resolve,
22
21
  revs
23
- } from "./chunk-BUKZHMMK.js";
22
+ } from "./chunk-INEKXXUQ.js";
24
23
  import {
25
24
  Spec
26
25
  } from "./chunk-64VFL4WI.js";
@@ -44,12 +43,67 @@ import {
44
43
  import {
45
44
  error
46
45
  } from "./chunk-WZWDS3W4.js";
47
- import {
48
- __toESM
49
- } from "./chunk-PZLD7RTK.js";
46
+
47
+ // ../../src/package-info/src/resolve-batch.ts
48
+ import { createInterface } from "node:readline";
49
+ var fetchResolve = async (client, registry, request, onRecord, signal) => {
50
+ if (!request.roots.length) return;
51
+ let body;
52
+ try {
53
+ const response = await client.requestStream(
54
+ new URL("-/vlt/resolve", registry),
55
+ {
56
+ // POST, not QUERY: CloudFront's allowed-method sets are fixed and
57
+ // none includes QUERY. The registry answers both.
58
+ method: "POST",
59
+ signal,
60
+ headers: {
61
+ "content-type": "application/json",
62
+ accept: "application/x-ndjson"
63
+ },
64
+ body: JSON.stringify(request)
65
+ }
66
+ );
67
+ if (response.statusCode !== 207) {
68
+ response.body.resume();
69
+ return;
70
+ }
71
+ body = response.body;
72
+ } catch {
73
+ return;
74
+ }
75
+ try {
76
+ for await (const line of createInterface({ input: body })) {
77
+ const record = parseRecord(line);
78
+ if (record) onRecord(record);
79
+ }
80
+ } catch {
81
+ body.destroy();
82
+ }
83
+ };
84
+ var parseRecord = (l) => {
85
+ let record;
86
+ try {
87
+ record = JSON.parse(l);
88
+ } catch {
89
+ return;
90
+ }
91
+ const { status, name, requested, manifest } = record;
92
+ if (status !== 200 || typeof name !== "string") return;
93
+ if (!Array.isArray(requested) || !requested.every((s) => typeof s === "string"))
94
+ return;
95
+ if (typeof manifest !== "object" || manifest === null) return;
96
+ const mani = manifest;
97
+ if (mani.name !== name || typeof mani.version !== "string") return;
98
+ return {
99
+ name,
100
+ version: mani.version,
101
+ requested,
102
+ manifest: mani
103
+ };
104
+ };
50
105
 
51
106
  // ../../src/package-info/src/index.ts
52
- var import_ssri = __toESM(require_lib(), 1);
53
107
  import { createHash, randomBytes } from "node:crypto";
54
108
  import {
55
109
  mkdir,
@@ -66,6 +120,7 @@ import {
66
120
  resolve as pathResolve,
67
121
  relative
68
122
  } from "node:path";
123
+ import { setTimeout } from "node:timers/promises";
69
124
  import { debuglog } from "node:util";
70
125
 
71
126
  // ../../src/package-info/src/capabilities.ts
@@ -147,6 +202,18 @@ var delimiter = "~";
147
202
  var VLT_PACKUMENT_MIME = "application/vnd.vlt.packument-v1+json";
148
203
  var VLT_PACKUMENT_V2_MIME = "application/vnd.vlt.packument-v2+json";
149
204
  var PACKUMENT_ACCEPT = `${VLT_PACKUMENT_V2_MIME}; q=1.0, ${VLT_PACKUMENT_MIME}; q=0.9, application/json; q=0.8, */*; q=0.1`;
205
+ var resolveWaitMs = () => {
206
+ const configured = Number(process.env.VLT_BATCH_RESOLVE_WAIT_MS);
207
+ return Number.isFinite(configured) && configured >= 0 ? configured : 250;
208
+ };
209
+ var graceOver = /* @__PURE__ */ Symbol("graceOver");
210
+ var SELECTION_OPTIONS = [
211
+ "before",
212
+ "os",
213
+ "arch",
214
+ "libc",
215
+ "node-version"
216
+ ];
150
217
  var manifestCacheMaxAge = 5 * 60 * 1e3;
151
218
  var noRegistryError = (spec) => error(
152
219
  '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',
@@ -160,6 +227,7 @@ var isStableSelector = (f) => {
160
227
  return !range.isAny && !range.raw.includes("-");
161
228
  };
162
229
  var storeLinkers = /* @__PURE__ */ new Set(["auto", "hardlink", "copy"]);
230
+ var storedIntegrity = (response) => !response.fromCache ? response.integrityActual : !response.contentType ? void 0 : response.integrity ?? (response.isGzip ? response.integrityActual : void 0);
163
231
  var PackageInfoClient = class {
164
232
  #registryClient;
165
233
  #projectRoot;
@@ -204,11 +272,36 @@ var PackageInfoClient = class {
204
272
  // needed. entries are removed when the on-disk file is invalidated
205
273
  // so the refreshed manifest can be written again.
206
274
  #manifestWritePaths = /* @__PURE__ */ new Set();
275
+ // Manifests a resolve batch delivered, keyed both as
276
+ // `${registry}${name}@${range}` for every spec that chose a version and
277
+ // `${registry}${name}@${version}` for the version itself.
278
+ #resolvedManifests = /* @__PURE__ */ new Map();
279
+ /**
280
+ * The resolve requests in flight against each registry, with when the
281
+ * registry last delivered a record: the grace a lookup gives the stream
282
+ * is measured from there, and it is set when the request is sent, so
283
+ * a registry with a request in flight always has a time.
284
+ */
285
+ #resolveInflight = /* @__PURE__ */ new Map();
286
+ /**
287
+ * each request in flight, by its `#resolveSent` key: its controller and
288
+ * how many builds are still reading it. a request that settled on its
289
+ * own stays in `#resolveSent`, since its records are still here
290
+ */
291
+ #resolveOwners = /* @__PURE__ */ new Map();
292
+ // Requests already sent, so the same ask is not repeated. Keyed on the
293
+ // request rather than the registry: a later step asking for a different
294
+ // root set is a different question.
295
+ #resolveSent = /* @__PURE__ */ new Set();
296
+ // Keys `manifest()` is waiting for, each woken by the record that carries
297
+ // it. Waiting per key rather than on the whole response is what lets a
298
+ // build place level N while the server still walks level N+1.
299
+ #resolveWaiters = /* @__PURE__ */ new Map();
207
300
  #registryClientPromise;
208
301
  #tarPoolPromise;
209
302
  async getRegistryClient() {
210
303
  if (this.#registryClient) return this.#registryClient;
211
- this.#registryClientPromise ??= import("./src-U4MCZFJT.js").then(({ RegistryClient }) => {
304
+ this.#registryClientPromise ??= import("./src-ODLA6WPY.js").then(({ RegistryClient }) => {
212
305
  this.#registryClient = new RegistryClient(this.options);
213
306
  return this.#registryClient;
214
307
  });
@@ -248,7 +341,7 @@ var PackageInfoClient = class {
248
341
  }
249
342
  async getTarPool() {
250
343
  if (this.#tarPool) return this.#tarPool;
251
- this.#tarPoolPromise ??= import("./src-EJSVFFXB.js").then(
344
+ this.#tarPoolPromise ??= import("./src-3MEJQYBG.js").then(
252
345
  ({ Pool }) => {
253
346
  this.#tarPool = new Pool();
254
347
  return this.#tarPool;
@@ -298,8 +391,7 @@ var PackageInfoClient = class {
298
391
  from = this.#projectRoot,
299
392
  integrity,
300
393
  resolved,
301
- fromLockfile = false,
302
- installScripts = false
394
+ fromLockfile = false
303
395
  } = options;
304
396
  const f = spec.final;
305
397
  const brotli = !!resolved && tarballFormat(resolved) === "brotli";
@@ -345,7 +437,7 @@ var PackageInfoClient = class {
345
437
  const pool = await this.getTarPool();
346
438
  const format = tarballFormat(r.resolved);
347
439
  const storeOn = f.type === "registry" && this.#storeLinker !== "unpack";
348
- const copy = this.#storeLinker === "copy" || installScripts;
440
+ const copy = this.#storeLinker === "copy";
349
441
  const hex = !storeOn ? void 0 : r.integrity ? integrityHex(r.integrity) : integrityHex(
350
442
  (await this.getRegistryClient()).cachedIntegrity(
351
443
  r.resolved
@@ -467,30 +559,71 @@ var PackageInfoClient = class {
467
559
  return r;
468
560
  }
469
561
  case "remote": {
470
- const response = await (await this.getRegistryClient()).request(r.resolved);
471
- if (response.statusCode !== 200) {
472
- throw this.#resolveError(
473
- spec,
474
- options,
475
- `failed to fetch remote tarball: ${registryErrorMessage(response)}`,
476
- {
477
- url: r.resolved,
478
- response
562
+ const rc = await this.getRegistryClient();
563
+ const { CacheEntry: Entry, cacheKey } = await import("./src-ODLA6WPY.js");
564
+ const key = cacheKey("GET", new URL(r.resolved));
565
+ const unzipped = (entry) => {
566
+ try {
567
+ return entry.unzippedIntegrity() === r.integrity;
568
+ } catch {
569
+ return false;
570
+ }
571
+ };
572
+ const fetchTarball = async (readCache) => {
573
+ let response2;
574
+ try {
575
+ response2 = await rc.request(r.resolved, {
576
+ integrity: r.integrity,
577
+ ...readCache === false ? { readCache } : {}
578
+ });
579
+ } catch (er) {
580
+ const cause = er.cause;
581
+ if (cause?.code !== "EINTEGRITY" || !(cause.response instanceof Entry) || !unzipped(cause.response)) {
582
+ throw er;
479
583
  }
480
- );
584
+ response2 = cause.response;
585
+ r.integrity = response2.integrityActual;
586
+ response2.labelArtifact();
587
+ rc.cache.set(key, response2.encode(), {
588
+ integrity: r.integrity
589
+ });
590
+ }
591
+ if (response2.statusCode !== 200) {
592
+ throw this.#resolveError(
593
+ spec,
594
+ options,
595
+ `failed to fetch remote tarball: ${registryErrorMessage(response2)}`,
596
+ {
597
+ url: r.resolved,
598
+ response: response2
599
+ }
600
+ );
601
+ }
602
+ return response2;
603
+ };
604
+ let response = await fetchTarball();
605
+ let found = storedIntegrity(response);
606
+ if (response.fromCache && found && r.integrity && r.integrity !== found && unzipped(response)) {
607
+ r.integrity = found;
481
608
  }
482
- const buf = response.buffer();
483
- const computed = import_ssri.default.fromData(buf, { algorithms: ["sha512"] }).toString();
484
- if (r.integrity && r.integrity !== computed) {
609
+ if (response.fromCache && (!found || r.integrity && r.integrity !== found)) {
610
+ rc.cache.delete(key, true, found);
611
+ if (r.integrity) rc.cache.delete(key, true, r.integrity);
612
+ await rc.cache.promise();
613
+ response = await fetchTarball(false);
614
+ found = storedIntegrity(response);
615
+ }
616
+ if (!found || r.integrity && r.integrity !== found) {
485
617
  throw error("Integrity check failure", {
486
618
  code: "EINTEGRITY",
487
619
  spec,
488
620
  url: r.resolved,
489
621
  wanted: r.integrity,
490
- found: computed
622
+ found
491
623
  });
492
624
  }
493
- r.integrity = computed;
625
+ r.integrity = found;
626
+ const buf = response.buffer();
494
627
  try {
495
628
  await (await this.getTarPool()).unpack(buf, target);
496
629
  } catch (er) {
@@ -760,6 +893,168 @@ var PackageInfoClient = class {
760
893
  }
761
894
  }
762
895
  }
896
+ /**
897
+ * Start a server-side resolve for `request` against `registry` and
898
+ * return without waiting. `manifest()` answers from what has arrived,
899
+ * and blocks on the request only when it needs a key that has not.
900
+ *
901
+ * A no-op unless the registry says it serves the endpoint. Anything it
902
+ * does not deliver is left to `manifest()`.
903
+ *
904
+ * Returns the function that releases the request. A build calls it once
905
+ * it has placed its last node; when the last build reading a request has
906
+ * released it, the stream is ended, so an install never waits on a
907
+ * stream nothing will read. The records it delivered stay answerable.
908
+ */
909
+ prefetchResolve(registry, request) {
910
+ if (!request.roots.length) return () => {
911
+ };
912
+ if (!registry.endsWith("/")) registry += "/";
913
+ const sent = `${registry}\0${JSON.stringify(request)}`;
914
+ const owned = this.#resolveOwners.get(sent);
915
+ if (owned) {
916
+ owned.readers++;
917
+ return () => this.#releaseResolve(sent, owned);
918
+ }
919
+ if (this.#resolveSent.has(sent)) return () => {
920
+ };
921
+ this.#resolveSent.add(sent);
922
+ const owner = { controller: new AbortController(), readers: 1 };
923
+ this.#resolveOwners.set(sent, owner);
924
+ let inflight = this.#resolveInflight.get(registry);
925
+ if (!inflight) {
926
+ inflight = { requests: /* @__PURE__ */ new Set(), lastRecordAt: 0 };
927
+ this.#resolveInflight.set(registry, inflight);
928
+ }
929
+ inflight.lastRecordAt = Date.now();
930
+ const promise = this.#runResolve(
931
+ registry,
932
+ request,
933
+ owner.controller.signal,
934
+ inflight
935
+ );
936
+ inflight.requests.add(promise);
937
+ const clear = () => {
938
+ inflight.requests.delete(promise);
939
+ if (this.#resolveOwners.get(sent) === owner)
940
+ this.#resolveOwners.delete(sent);
941
+ };
942
+ promise.then(clear, clear);
943
+ return () => this.#releaseResolve(sent, owner);
944
+ }
945
+ /**
946
+ * One build is done reading the request; the last one out ends it, and
947
+ * the same question can then be asked again.
948
+ */
949
+ #releaseResolve(sent, owner) {
950
+ if (--owner.readers > 0) return;
951
+ if (this.#resolveOwners.get(sent) === owner) {
952
+ this.#resolveOwners.delete(sent);
953
+ this.#resolveSent.delete(sent);
954
+ }
955
+ owner.controller.abort();
956
+ }
957
+ /** Never rejects: a resolve that fails leaves every spec to manifest(). */
958
+ async #runResolve(registry, request, signal, inflight) {
959
+ try {
960
+ if ((await this.capabilities(registry)).resolve === void 0)
961
+ return;
962
+ const client = await this.getRegistryClient();
963
+ await fetchResolve(
964
+ client,
965
+ registry,
966
+ request,
967
+ (record) => {
968
+ inflight.lastRecordAt = Date.now();
969
+ absolutizeTarball(
970
+ record.manifest,
971
+ `${registry}${record.name}/-/`
972
+ );
973
+ this.#vltPackuments.add(`${registry}${record.name}`);
974
+ for (const spec of record.requested) {
975
+ this.#deliverResolved(
976
+ `${registry}${record.name}@${spec}`,
977
+ record.manifest
978
+ );
979
+ }
980
+ this.#deliverResolved(
981
+ `${registry}${record.name}@${record.version}`,
982
+ record.manifest
983
+ );
984
+ },
985
+ signal
986
+ );
987
+ } catch {
988
+ }
989
+ }
990
+ /** Record an arrived manifest and wake whoever was waiting for it. */
991
+ #deliverResolved(key, manifest) {
992
+ debug("resolve delivered %s", key);
993
+ this.#resolvedManifests.set(key, manifest);
994
+ const waiters = this.#resolveWaiters.get(key);
995
+ if (waiters) {
996
+ this.#resolveWaiters.delete(key);
997
+ for (const wake of waiters) wake(manifest);
998
+ }
999
+ }
1000
+ /**
1001
+ * The manifest for `key` from an in-flight resolve, or from `fetch` when
1002
+ * the resolve is not carrying it. Undefined when nothing is in flight.
1003
+ *
1004
+ * A stream that is delivering gets a short grace to carry the key, since
1005
+ * its records come in the order the build asks for them; one that has
1006
+ * gone quiet gets none, so a server that is slow to start never charges
1007
+ * that grace to every level of the build. After the grace `fetch` runs as
1008
+ * well and the first answer wins, so a slow server can never hold a
1009
+ * lookup longer than fetching the packument would have. A fetch that
1010
+ * fails while the stream is still open waits for the stream's verdict.
1011
+ */
1012
+ async #awaitResolved(registry, key, fetch) {
1013
+ const inflight = this.#resolveInflight.get(registry);
1014
+ if (!inflight?.requests.size) return void 0;
1015
+ let wake;
1016
+ const arrival = new Promise((res) => {
1017
+ wake = res;
1018
+ });
1019
+ let waiters = this.#resolveWaiters.get(key);
1020
+ if (!waiters) this.#resolveWaiters.set(key, waiters = /* @__PURE__ */ new Set());
1021
+ waiters.add(wake);
1022
+ const settled = Promise.all([...inflight.requests]).then(
1023
+ () => this.#resolvedManifests.get(key)
1024
+ );
1025
+ const quietFor = Date.now() - inflight.lastRecordAt;
1026
+ const grace = Math.max(0, resolveWaitMs() - quietFor);
1027
+ try {
1028
+ const early = await Promise.race([
1029
+ arrival,
1030
+ settled,
1031
+ // unref'd: a pending wait must never be what keeps the process up
1032
+ setTimeout(grace, graceOver, { ref: false })
1033
+ ]);
1034
+ if (early !== graceOver) return early ?? await fetch();
1035
+ debug("resolve hedge %s", key);
1036
+ const hedge = fetch();
1037
+ const outcome = await Promise.race([
1038
+ arrival,
1039
+ settled,
1040
+ hedge.catch(() => void 0)
1041
+ ]);
1042
+ if (outcome) return outcome;
1043
+ const found = await Promise.race([
1044
+ arrival,
1045
+ settled,
1046
+ setTimeout(resolveWaitMs(), graceOver, { ref: false })
1047
+ ]);
1048
+ return found && found !== graceOver ? found : await hedge;
1049
+ } finally {
1050
+ waiters.delete(wake);
1051
+ if (!waiters.size) this.#resolveWaiters.delete(key);
1052
+ }
1053
+ }
1054
+ /** How many manifests resolve batches have delivered. For tests. */
1055
+ get resolvedManifestCount() {
1056
+ return this.#resolvedManifests.size;
1057
+ }
763
1058
  async manifest(spec, options = {}) {
764
1059
  const { from = this.#projectRoot } = options;
765
1060
  if (typeof spec === "string")
@@ -767,6 +1062,14 @@ var PackageInfoClient = class {
767
1062
  const f = spec.final;
768
1063
  switch (f.type) {
769
1064
  case "registry": {
1065
+ const selecting = SELECTION_OPTIONS.some(
1066
+ (k) => options[k] !== void 0
1067
+ );
1068
+ const resolveKey = `${f.registry ?? ""}${f.name}@${f.bareSpec}`;
1069
+ if (!selecting) {
1070
+ const resolved = this.#resolvedManifests.get(resolveKey);
1071
+ if (resolved) return resolved;
1072
+ }
770
1073
  const cachePath = this._manifestCachePath(spec, options);
771
1074
  if (cachePath) {
772
1075
  try {
@@ -789,25 +1092,36 @@ var PackageInfoClient = class {
789
1092
  } catch {
790
1093
  }
791
1094
  }
792
- const mani = pickManifest(
793
- await this.#packument(f, options, this.#stable(f)),
794
- spec,
795
- options
796
- );
797
- if (!mani) throw this.#resolveError(spec, options);
798
- if (cachePath && !this.#manifestWritePaths.has(cachePath)) {
799
- this.#manifestWritePaths.add(cachePath);
800
- const vlt = this.#vltPackuments.has(
801
- `${f.registry}${f.name}`
1095
+ const fromPackument = async () => {
1096
+ const mani = pickManifest(
1097
+ await this.#packument(f, options, this.#stable(f)),
1098
+ spec,
1099
+ options
802
1100
  );
803
- void this.#writeManifestCache(
804
- cachePath,
805
- JSON.stringify(
806
- vlt ? { ...mani, __VLT_PACKUMENT: true } : mani
807
- )
1101
+ if (!mani) throw this.#resolveError(spec, options);
1102
+ if (cachePath && !this.#manifestWritePaths.has(cachePath)) {
1103
+ this.#manifestWritePaths.add(cachePath);
1104
+ const vlt = this.#vltPackuments.has(
1105
+ `${f.registry}${f.name}`
1106
+ );
1107
+ void this.#writeManifestCache(
1108
+ cachePath,
1109
+ JSON.stringify(
1110
+ vlt ? { ...mani, __VLT_PACKUMENT: true } : mani
1111
+ )
1112
+ );
1113
+ }
1114
+ return mani;
1115
+ };
1116
+ if (!selecting && f.registry) {
1117
+ const resolved = await this.#awaitResolved(
1118
+ f.registry,
1119
+ resolveKey,
1120
+ fromPackument
808
1121
  );
1122
+ if (resolved) return resolved;
809
1123
  }
810
- return mani;
1124
+ return fromPackument();
811
1125
  }
812
1126
  case "git": {
813
1127
  const {
@@ -849,7 +1163,7 @@ var PackageInfoClient = class {
849
1163
  );
850
1164
  }
851
1165
  const buf = response.buffer();
852
- const computed = import_ssri.default.fromData(buf, { algorithms: ["sha512"] }).toString();
1166
+ const integrity = storedIntegrity(response);
853
1167
  try {
854
1168
  await (await this.getTarPool()).unpack(buf, dir);
855
1169
  } catch (er) {
@@ -861,7 +1175,7 @@ var PackageInfoClient = class {
861
1175
  );
862
1176
  }
863
1177
  const mani = this.#readExtracted(dir, s, options);
864
- mani.dist = { integrity: computed };
1178
+ if (integrity) mani.dist = { integrity };
865
1179
  return mani;
866
1180
  });
867
1181
  }
@@ -1179,11 +1493,14 @@ var PackageInfoClient = class {
1179
1493
  }
1180
1494
  };
1181
1495
  var absolutizeTarballs = (paku, base) => {
1182
- for (const { dist } of Object.values(paku.versions)) {
1183
- const tarball = dist?.tarball;
1184
- if (tarball && !/^[a-z][a-z0-9+.-]*:/i.test(tarball)) {
1185
- dist.tarball = String(new URL(tarball, base));
1186
- }
1496
+ for (const manifest of Object.values(paku.versions)) {
1497
+ absolutizeTarball(manifest, base);
1498
+ }
1499
+ };
1500
+ var absolutizeTarball = (manifest, base) => {
1501
+ const dist = manifest.dist;
1502
+ if (dist?.tarball && !/^[a-z][a-z0-9+.-]*:/i.test(dist.tarball)) {
1503
+ dist.tarball = String(new URL(dist.tarball, base));
1187
1504
  }
1188
1505
  };
1189
1506
 
@@ -14,7 +14,7 @@ var require = _vlt_createRequire(import.meta.filename);
14
14
  var package_default = {
15
15
  name: "@vltpkg/user-agent",
16
16
  description: "The User-Agent string that vlt sends with every outbound request",
17
- version: "1.3.3",
17
+ version: "1.3.5",
18
18
  repository: {
19
19
  type: "git",
20
20
  url: "git+https://github.com/vltpkg/vltpkg.git",
@@ -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-CSZTT7QN.js";
14
+ } from "./chunk-TOKUDCSW.js";
15
15
  import {
16
16
  urlOpen
17
17
  } from "./chunk-APMYKJI2.js";
@@ -13,10 +13,10 @@ import {
13
13
  actual,
14
14
  callLimit,
15
15
  createVirtualRoot
16
- } from "./chunk-ZRRNOLEG.js";
16
+ } from "./chunk-ZZFFMXI2.js";
17
17
  import {
18
18
  SecurityArchive
19
- } from "./chunk-DCFLXO2M.js";
19
+ } from "./chunk-XUGOPOLF.js";
20
20
  import {
21
21
  error
22
22
  } from "./chunk-WZWDS3W4.js";
@@ -140,7 +140,7 @@ var reloadConfig = async (folder) => {
140
140
  unload("project");
141
141
  } catch {
142
142
  }
143
- const { Config } = await import("./config-AWX2VEKL.js");
143
+ const { Config } = await import("./config-HO7N7FPS.js");
144
144
  return Config.load(folder, process.argv, true);
145
145
  };
146
146
 
@@ -14,7 +14,7 @@ import {
14
14
  } from "./chunk-WGDTG44V.js";
15
15
  import {
16
16
  commandAliases
17
- } from "./chunk-ENSGPOLR.js";
17
+ } from "./chunk-XU5WNX4S.js";
18
18
 
19
19
  // ../../node_modules/.vlt/~npm~package-json-from-dist@1.0.1/node_modules/package-json-from-dist/dist/esm/index.js
20
20
  import { readFileSync } from "node:fs";
@@ -11,19 +11,19 @@ import {createRequire as _vlt_createRequire} from "node:module";
11
11
  var require = _vlt_createRequire(import.meta.filename);
12
12
  import {
13
13
  openUrl
14
- } from "./chunk-MC6CESRA.js";
14
+ } from "./chunk-AFUXQ3P4.js";
15
15
  import {
16
16
  stderr
17
- } from "./chunk-CSZTT7QN.js";
17
+ } from "./chunk-TOKUDCSW.js";
18
18
  import {
19
19
  mermaidOutput
20
- } from "./chunk-ZRRNOLEG.js";
20
+ } from "./chunk-ZZFFMXI2.js";
21
21
  import {
22
22
  ViewClass
23
23
  } from "./chunk-4KCU2KZL.js";
24
24
  import {
25
25
  getTokenByURL
26
- } from "./chunk-SH24EMXO.js";
26
+ } from "./chunk-2B5STVCP.js";
27
27
 
28
28
  // ../../src/cli-sdk/src/query-auth.ts
29
29
  var createGetAuthHeader = (conf) => async (url) => getTokenByURL(url, conf.options.identity);