zyrk 0.6.6 → 0.7.1

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/README.md CHANGED
@@ -27,6 +27,14 @@ the shared store, exact dependency context, mutation rules, and cleanup contract
27
27
  central to its design. Storage savings, speed, and framework compatibility will
28
28
  be reported only after reproducible measurements and compatibility tests.
29
29
 
30
+ ### The name and the idea
31
+
32
+ The project began as **snpm**, short for “space-efficient npm.” That name was
33
+ already in use, so it became **nexu**; npm's package-name similarity policy
34
+ then blocked publishing that name, and the project became **zyrk**. The names
35
+ changed, while the goal stayed the same: make shared package storage practical,
36
+ careful, and useful in everyday Node.js development.
37
+
30
38
  ## Try the current CLI
31
39
 
32
40
  Install from npm:
@@ -46,11 +54,17 @@ From a checkout:
46
54
  ```bash
47
55
  git clone https://github.com/kingjethro999/zyrk.git zyrk
48
56
  cd zyrk
49
- npm ci
50
- node bin/zyrk.js --help
51
- node bin/zyrk.js --version
57
+ npm install --global zyrk@0.6.6
58
+ zyrk install --frozen-lockfile
59
+ zyrk --help
60
+ zyrk --version
52
61
  ```
53
62
 
63
+ The global npm install bootstraps the `zyrk` executable. Project dependencies
64
+ are installed by zyrk itself. On a fresh checkout, use the published bootstrap
65
+ version first because the source checkout's own development dependencies are
66
+ needed to run its local CLI.
67
+
54
68
  ### One-line install (shell)
55
69
 
56
70
  Linux / macOS:
@@ -90,8 +104,12 @@ Offline installs use locked verified sources and fail if an input is missing.
90
104
  Manifest, lock, and projection changes are journaled for process-crash recovery;
91
105
  a failed resolution, verification, or build-policy check keeps the prior graph.
92
106
 
93
- Dependency install scripts and implicit `binding.gyp` builds require explicit
94
- approval. Review first, trust the selected sources, then install:
107
+ Dependency `preinstall`, `install`, and `postinstall` scripts (plus implicit
108
+ `binding.gyp` builds) are supported. In an interactive terminal, `zyrk install`
109
+ shows the exact candidates and lets you select which scripts to run. Press Enter
110
+ to skip all pending scripts and continue with an intentionally unbuilt install.
111
+ Headless installs also skip unapproved scripts and report that readiness state.
112
+ Review candidates separately with:
95
113
 
96
114
  ```bash
97
115
  zyrk approve-builds # inspect exact sources and commands; executes no package code
@@ -101,8 +119,12 @@ zyrk rebuild --offline # rebuild the complete approved locked graph
101
119
  ```
102
120
 
103
121
  Approvals are local to the exact graph, sources, patch contents, and script
104
- policy. Mandatory user denies override project approvals. Approved scripts run
105
- with your account's permissions; permission is not a sandbox. Packages that need
122
+ policy. Interactive selections are written to the checked-in
123
+ `zyrk-workspace.json` file with exact package source identities, so collaborators
124
+ can reuse the review alongside the lockfile. Manual `zyrk approve-builds
125
+ <package>` approvals are recorded there too. Mandatory user denies override
126
+ project approvals. Approved scripts run with your account's permissions;
127
+ permission is not a sandbox. Packages that need
106
128
  writes use a complete private dependency generation under `.zyrk-local/`, with
107
129
  independent writable files and dependency links confined to that generation.
108
130
  Build failure keeps the previous project graph active. `--ignore-scripts`
@@ -111,6 +133,24 @@ hooks never run automatically. A project can request runtime writable packages
111
133
  with `"zyrk": { "mutablePackages": ["package-name"] }`; this also uses a complete
112
134
  private graph.
113
135
 
136
+ `zyrk-workspace.json` is portable review state, not a machine-specific store
137
+ manifest. It records each approved package name, version, and exact source ID;
138
+ the file can be committed with `package-lock.json` so collaborators do not have
139
+ to repeat the same approval for the same verified source. User-level denials
140
+ still take precedence.
141
+
142
+ ### Run project scripts
143
+
144
+ Use `zyrk run <script>` or the package-manager shortcut `zyrk <script>`; both
145
+ use the same project-script policy, installed-generation checks, and local
146
+ binary path. Arguments after `--` are passed to the script:
147
+
148
+ ```bash
149
+ zyrk dev
150
+ zyrk test -- --watch
151
+ zyrk run build
152
+ ```
153
+
114
154
  Default registry and authentication settings are loaded from user/project
115
155
  `.npmrc` for installation, dependency edits, `dlx`, and `create`. Environment
116
156
  token placeholders are supported. Credentials are restricted to their configured
@@ -122,7 +162,10 @@ registry; private names never fall back to a public endpoint. Disabled TLS
122
162
  verification is rejected before lookup. Git sources and non-workspace local sources remain unsupported;
123
163
  they fail explicitly. Optional dependencies incompatible with the selected platform
124
164
  are omitted; missing packages, integrity failures, and other optional failures
125
- remain errors. Locks retain exact platform-qualified optional variants; each machine
165
+ remain errors. Platform-specific optional packages that do not match this machine
166
+ are summarized once after installation; these are normal variants for packages
167
+ supporting multiple operating systems and CPUs. Locks retain exact
168
+ platform-qualified optional variants; each machine
126
169
  selects its compatible local closure without rewriting the lock or recording store
127
170
  paths. Older platform-pruned locks need an intentional non-frozen refresh; incomplete
128
171
  npm locks must be freshly resolved before portable adoption. macOS, Windows, and
@@ -359,24 +402,34 @@ own options and configuration.
359
402
  ## Develop and contribute
360
403
 
361
404
  ```bash
362
- npm ci
363
- npm run setup:hooks
364
- npm run typecheck
365
- npm test
366
- npm run check
367
- npm run format:check
368
- npm run format
405
+ npm install --global zyrk@0.6.6 # bootstrap the CLI, once per machine
406
+ zyrk install --frozen-lockfile
407
+ zyrk run setup:hooks
408
+ zyrk run typecheck
409
+ zyrk run test
410
+ zyrk run check
411
+ zyrk run format:check
412
+ zyrk run format
369
413
  ```
370
414
 
415
+ Linux and macOS development, CI, and release verification use zyrk to install
416
+ the repository's dependencies. The Windows contract job continues to use npm
417
+ while native Windows filesystem and link behavior is being validated. npm is
418
+ also used to publish the package; that is a registry operation, not a project
419
+ dependency install. Vercel deployments should set the install command to
420
+ the commands in [web/vercel.json](web/vercel.json) and set the Vercel project
421
+ root directory to `web/`. The included site uses zyrk for project dependencies
422
+ after npm bootstraps the CLI.
423
+
371
424
  The commit hook runs the full check gate, increments the version in
372
425
  `package.json`, `package-lock.json`, and `VERSION`, and creates a matching version
373
426
  tag after the commit. Keep changes focused and never bypass the hooks.
374
427
 
375
- CI checks pushed commits and pull requests. The separate
376
- [npm publishing action](.github/workflows/npm-publish.yml) is triggered when a
377
- GitHub release is published. It verifies the release tag, formatting, typecheck,
378
- build, and tests, then publishes that exact commit. Ordinary commits do not
379
- publish packages. Dependabot and a scheduled advisory check supplement CI; an
428
+ CI checks pushed commits and pull requests. The
429
+ [npm publishing action](.github/workflows/npm-publish.yml) runs when the commit
430
+ hook's version tag is pushed, verifies formatting, typecheck, build, tests, and
431
+ the tag/version match, then publishes that exact commit. A manual workflow
432
+ dispatch can publish a prior version tag for release backfills. Dependabot and a scheduled advisory check supplement CI; an
380
433
  unavailable advisory service fails separately from a clean result.
381
434
 
382
435
  The contributor guide is [AGENTS.md](AGENTS.md). Bug reports and reproducible
@@ -412,10 +465,29 @@ lists each unique package/version linked after the project transaction commits.
412
465
  Source counts differ from dependency contexts when peers split a package.
413
466
  `--json` disables progress.
414
467
 
415
- Install resolution has a five-minute deadline; each archive extraction has a
416
- two-minute deadline. Set `ZYRK_RESOLVE_TIMEOUT_MS` or `ZYRK_EXTRACT_TIMEOUT_MS`
417
- to an integer from `1` to `900000` milliseconds to adjust these timeouts.
418
- Graph, archive-entry, and byte limits still apply.
468
+ Install resolution has a five-minute deadline, registry requests (including
469
+ archive downloads) have a fifteen-minute deadline, a complete fetch operation
470
+ has a fifteen-minute deadline, and each archive extraction has a two-minute
471
+ deadline. On a slow or unreliable connection, increase the request and complete
472
+ fetch deadlines together. For example, this gives a single request up to 30
473
+ minutes and the entire fetch phase up to 45 minutes:
474
+
475
+ ```bash
476
+ ZYRK_REQUEST_TIMEOUT_MS=1800000 ZYRK_FETCH_TIMEOUT_MS=2700000 zyrk install
477
+ ```
478
+
479
+ To keep the settings for future commands in the current shell, use
480
+ `export ZYRK_REQUEST_TIMEOUT_MS=1800000` and
481
+ `export ZYRK_FETCH_TIMEOUT_MS=2700000`. Both values are milliseconds.
482
+ Resolution and extraction timeouts accept `1` to `900000` (15 minutes); request
483
+ and whole-fetch timeouts accept `1` to `3600000` (60 minutes). For a very slow
484
+ connection, the largest supported combination is:
485
+
486
+ ```bash
487
+ ZYRK_REQUEST_TIMEOUT_MS=3600000 ZYRK_FETCH_TIMEOUT_MS=3600000 zyrk install
488
+ ```
489
+
490
+ These are finite deadlines; graph, archive-entry, and byte limits still apply.
419
491
 
420
492
  Platform gates run the complete check on Ubuntu and macOS/Node 24 and a
421
493
  Windows CLI/artifact contract suite, including paths with spaces and Unicode.
package/VERSION CHANGED
@@ -1 +1 @@
1
- v0.6.6
1
+ v0.7.1
package/bin/zyrk.js CHANGED
@@ -12,6 +12,7 @@ const { runCli } = await import(
12
12
  process.exitCode = await runCli({
13
13
  argv: process.argv.slice(2),
14
14
  env: process.env,
15
+ stdin: process.stdin,
15
16
  stdout: process.stdout,
16
17
  stderr: process.stderr,
17
18
  });
@@ -25,6 +25,7 @@ export type ToolEnvironment = {
25
25
  export type RunCliOptions = {
26
26
  argv: readonly string[];
27
27
  env: NodeJS.ProcessEnv;
28
+ stdin?: NodeJS.ReadStream;
28
29
  stdout: {
29
30
  write(chunk: string): unknown;
30
31
  };
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AA4BA,OAAO,EAAe,SAAS,EAAe,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,OAAO,EAAoB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAM3C,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAEhF,eAAO,MAAM,uBAAuB,IAAI,CAAC;AA6DzC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAyBhD;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AA6EF;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,GACnD,SAAS,CAgDX;AAmBD,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC1C,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;CACxC,CAAC;AAEF,gFAAgF;AAChF,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA8bpE"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAe,SAAS,EAAe,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,OAAO,EAAoB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAM3C,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAEhF,eAAO,MAAM,uBAAuB,IAAI,CAAC;AA6DzC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0BhD;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AA6EF;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,GACnD,SAAS,CAgDX;AAmBD,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC1B,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC1C,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;CACxC,CAAC;AAuDF,gFAAgF;AAChF,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA2fpE"}
package/dist/cli/main.js CHANGED
@@ -12,6 +12,8 @@ import { sanitizeDiagnostic, redactSecrets } from "../log.js";
12
12
  import { installProject } from "../install/run.js";
13
13
  import { installRegistry } from "../install/registry.js";
14
14
  import { readFileSync } from "node:fs";
15
+ import { readFile } from "node:fs/promises";
16
+ import { createInterface } from "node:readline/promises";
15
17
  import * as path from "node:path";
16
18
  import { fileURLToPath } from "node:url";
17
19
  import { runCreate, starterSpecFor } from "../dlx/create.js";
@@ -22,7 +24,7 @@ import { runStoreCommand } from "../commands/store.js";
22
24
  import { exitCodeFor, ZyrkError, toZyrkError } from "../errors.js";
23
25
  export { ZyrkError };
24
26
  import { DEFAULT_REGISTRY, RegistryClient } from "../fetch/registry.js";
25
- import { resolveStoreRoot } from "../config/paths.js";
27
+ import { findProjectBoundary, resolveStoreRoot } from "../config/paths.js";
26
28
  import { Store } from "../store/layout.js";
27
29
  import { delegateToNpm, delegatedHelpLines, isDelegatedCommand, } from "./npm-delegate.js";
28
30
  import { isGlobalInstallRequest, parseArgs } from "./parse.js";
@@ -89,6 +91,7 @@ export function helpText(version) {
89
91
  `zyrk v${version} — one verified store per user, shared across projects through links.`,
90
92
  "",
91
93
  "Usage: zyrk <command> [args] [--json]",
94
+ " zyrk <script> [args] (shortcut for zyrk run <script> [args])",
92
95
  "",
93
96
  "Dependency commands:",
94
97
  ];
@@ -100,7 +103,7 @@ export function helpText(version) {
100
103
  }
101
104
  lines.push("", "Registry publishing and account commands (via your npm):");
102
105
  lines.push(...delegatedHelpLines());
103
- lines.push("", "Install timeouts: ZYRK_RESOLVE_TIMEOUT_MS and ZYRK_EXTRACT_TIMEOUT_MS (1..900000 ms).", "zyrk never installs packages globally and never runs npm to do it.", "Need a global package? Run npm directly: npm install --global <package-spec>");
106
+ lines.push("", "Install timeouts: ZYRK_RESOLVE_TIMEOUT_MS and ZYRK_EXTRACT_TIMEOUT_MS (1..900000 ms); ZYRK_REQUEST_TIMEOUT_MS (1..3600000 ms); ZYRK_FETCH_TIMEOUT_MS (1..3600000 ms).", "zyrk never installs packages globally and never runs npm to do it.", "Need a global package? Run npm directly: npm install --global <package-spec>");
104
107
  return `${lines.join("\n")}\n`;
105
108
  }
106
109
  function ok(stdout, parsed) {
@@ -212,6 +215,39 @@ function readCliVersion() {
212
215
  return "unknown";
213
216
  }
214
217
  }
218
+ async function promptForBuildApproval(builds, input, output) {
219
+ output.write("\nDependency lifecycle scripts need approval. Review these exact package sources:\n");
220
+ builds.forEach((build, index) => {
221
+ const stages = build.stages
222
+ .map((stage) => `${stage.name}: ${redactSecrets(stage.command)
223
+ .replace(/[\x00-\x1f\x7f-\x9f]/g, "")
224
+ .slice(0, 180)}`)
225
+ .join("; ");
226
+ output.write(` ${index + 1}. ${build.name}@${build.version} (${build.sourceId}) — ${stages}\n`);
227
+ });
228
+ output.write("Enter numbers separated by commas to approve selected sources, `all` to approve all listed sources, or press Enter to skip their scripts and continue unbuilt.\n");
229
+ const prompt = createInterface({
230
+ input,
231
+ output: output,
232
+ terminal: true,
233
+ });
234
+ try {
235
+ const answer = (await prompt.question("Approve builds> ")).trim();
236
+ if (!answer)
237
+ return [];
238
+ if (answer.toLowerCase() === "all")
239
+ return ["*"];
240
+ const numbers = answer.split(/[ ,]+/).map((value) => Number(value));
241
+ if (numbers.some((number) => !Number.isInteger(number) || number < 1 || number > builds.length))
242
+ throw new ZyrkError("ZYRK_USAGE", "Build selection contains an invalid candidate number.", {
243
+ remedy: `Select numbers from 1 to ${builds.length}, enter all, or press Enter to skip scripts.`,
244
+ });
245
+ return [...new Set(numbers)].map((number) => builds[number - 1].sourceId);
246
+ }
247
+ finally {
248
+ prompt.close();
249
+ }
250
+ }
215
251
  /** Parse, dispatch, delegate, or run a tool, and emit. The only impure seam. */
216
252
  export async function runCli(options) {
217
253
  const parsed = parseArgs(options.argv);
@@ -464,6 +500,11 @@ export async function runCli(options) {
464
500
  env: options.env,
465
501
  specs,
466
502
  projectDir: cwd,
503
+ ...(options.stdin?.isTTY && options.stderr.isTTY && !parsed.json
504
+ ? {
505
+ requestBuildApproval: (builds) => promptForBuildApproval(builds, options.stdin, options.stderr),
506
+ }
507
+ : {}),
467
508
  ...tools,
468
509
  });
469
510
  progress.close();
@@ -520,6 +561,11 @@ export async function runCli(options) {
520
561
  projectDir: options.cwd ?? process.cwd(),
521
562
  store: tools.store,
522
563
  registry: tools.registry,
564
+ ...(options.stdin?.isTTY && options.stderr.isTTY && !parsed.json
565
+ ? {
566
+ requestBuildApproval: (builds) => promptForBuildApproval(builds, options.stdin, options.stderr),
567
+ }
568
+ : {}),
523
569
  });
524
570
  progress.close();
525
571
  if (parsed.json) {
@@ -602,6 +648,48 @@ export async function runCli(options) {
602
648
  return emit(fail(toZyrkError(error, `zyrk ${parsed.command} failed.`), parsed));
603
649
  }
604
650
  }
651
+ // Package scripts are accepted as ergonomic aliases for `zyrk run <name>`.
652
+ // Route through the same project runner so policy, generation leases,
653
+ // local-bin PATH and lifecycle environment stay identical.
654
+ if (!parsed.help &&
655
+ !parsed.version &&
656
+ parsed.command !== undefined &&
657
+ !KNOWN_COMMANDS.has(parsed.command) &&
658
+ parsed.argsBeforeCommand.length === 0) {
659
+ const cwd = options.cwd ?? process.cwd();
660
+ const boundary = await findProjectBoundary(cwd);
661
+ if (boundary) {
662
+ let declared = false;
663
+ try {
664
+ const manifest = JSON.parse(await readFile(path.join(boundary, "package.json"), "utf8"));
665
+ if (manifest.scripts !== null &&
666
+ typeof manifest.scripts === "object" &&
667
+ !Array.isArray(manifest.scripts) &&
668
+ typeof manifest.scripts[parsed.command] === "string")
669
+ declared = true;
670
+ }
671
+ catch {
672
+ // Preserve the normal unknown-command diagnostic when no valid
673
+ // project script can be identified.
674
+ }
675
+ if (declared) {
676
+ try {
677
+ return await runProjectCommand({
678
+ command: "run",
679
+ args: [parsed.command, ...parsed.commandArgs],
680
+ cwd,
681
+ env: options.env,
682
+ stdout: options.stdout,
683
+ stderr: options.stderr,
684
+ json: parsed.json,
685
+ });
686
+ }
687
+ catch (error) {
688
+ return emit(fail(toZyrkError(error, `zyrk ${parsed.command} failed.`), parsed));
689
+ }
690
+ }
691
+ }
692
+ }
605
693
  return emit(dispatch(parsed, { version, env: options.env }));
606
694
  }
607
695
  async function defaultTools(env, version, cwd = process.cwd()) {
@@ -10,5 +10,6 @@ export declare function runDependencyCommand(options: {
10
10
  registry: RegistryClient;
11
11
  env?: NodeJS.ProcessEnv;
12
12
  onProgress?: InstallOptions["onProgress"];
13
+ requestBuildApproval?: InstallOptions["requestBuildApproval"];
13
14
  }): Promise<InstallResult>;
14
15
  //# sourceMappingURL=dependencies.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dependencies.d.ts","sourceRoot":"","sources":["../../src/commands/dependencies.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAavE,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;CAC3C,GAAG,OAAO,CAAC,aAAa,CAAC,CA4HzB"}
1
+ {"version":3,"file":"dependencies.d.ts","sourceRoot":"","sources":["../../src/commands/dependencies.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAavE,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;IAC1C,oBAAoB,CAAC,EAAE,cAAc,CAAC,sBAAsB,CAAC,CAAC;CAC/D,GAAG,OAAO,CAAC,aAAa,CAAC,CA+HzB"}
@@ -16,6 +16,9 @@ export async function runDependencyCommand(options) {
16
16
  registry: options.registry,
17
17
  ...(options.env ? { env: options.env } : {}),
18
18
  ...(options.onProgress ? { onProgress: options.onProgress } : {}),
19
+ ...(options.requestBuildApproval
20
+ ? { requestBuildApproval: options.requestBuildApproval }
21
+ : {}),
19
22
  };
20
23
  const names = [];
21
24
  let from;
@@ -13,6 +13,10 @@ import type { Logger } from "../log.ts";
13
13
  import { type HashedBytes } from "../util/integrity.ts";
14
14
  /** 512 MiB: large for an npm package, small enough to bound mishaps. */
15
15
  export declare const DEFAULT_MAX_TARBALL_BYTES: number;
16
+ /** Bounds the complete metadata closure while allowing large framework graphs. */
17
+ export declare const DEFAULT_MAX_TOTAL_METADATA_BYTES: number;
18
+ /** Fifteen minutes per registry operation, including archive transfer. */
19
+ export declare const DEFAULT_REQUEST_DEADLINE_MS = 900000;
16
20
  /** Default registry for tool environments (SPEC 13.1). */
17
21
  export declare const DEFAULT_REGISTRY = "https://registry.npmjs.org/";
18
22
  export interface PackumentDist {
@@ -92,6 +96,10 @@ export declare class RegistryClient {
92
96
  packument(name: string, options?: {
93
97
  signal?: AbortSignal;
94
98
  }): Promise<Packument>;
99
+ /** Fetch one exact version without transferring every release in a packument. */
100
+ versionManifest(name: string, version: string, options?: {
101
+ signal?: AbortSignal;
102
+ }): Promise<PackumentVersion>;
95
103
  /** Registry advisory request; private names may never follow an external redirect. */
96
104
  auditAdvisories(packages: Readonly<Record<string, readonly string[]>>, options?: {
97
105
  signal?: AbortSignal;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/fetch/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/fetch/registry.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,10 @@ import { assertIntegrityOk, canonicalStrongSri, IntegrityHasher, base64FromHex,
17
17
  const transport = createRequire(import.meta.url)("make-fetch-happen");
18
18
  /** 512 MiB: large for an npm package, small enough to bound mishaps. */
19
19
  export const DEFAULT_MAX_TARBALL_BYTES = 512 * 1024 * 1024;
20
+ /** Bounds the complete metadata closure while allowing large framework graphs. */
21
+ export const DEFAULT_MAX_TOTAL_METADATA_BYTES = 256 * 1024 * 1024;
22
+ /** Fifteen minutes per registry operation, including archive transfer. */
23
+ export const DEFAULT_REQUEST_DEADLINE_MS = 900_000;
20
24
  /** Default registry for tool environments (SPEC 13.1). */
21
25
  export const DEFAULT_REGISTRY = "https://registry.npmjs.org/";
22
26
  /** npm's abbreviated packument media type, used for installs. */
@@ -66,9 +70,9 @@ export class RegistryClient {
66
70
  (scope.pathname.startsWith(boundary.pathname) ||
67
71
  boundary.pathname.startsWith(scope.pathname)));
68
72
  this.#maxMetadataBytes = positiveLimit(options.maxMetadataBytes ?? 32 * 1024 * 1024, "maxMetadataBytes");
69
- this.#maxTotalMetadataBytes = positiveLimit(options.maxTotalMetadataBytes ?? 128 * 1024 * 1024, "maxTotalMetadataBytes");
73
+ this.#maxTotalMetadataBytes = positiveLimit(options.maxTotalMetadataBytes ?? DEFAULT_MAX_TOTAL_METADATA_BYTES, "maxTotalMetadataBytes");
70
74
  this.#maxMetadataVersions = positiveLimit(options.maxMetadataVersions ?? 100_000, "maxMetadataVersions");
71
- this.#requestDeadlineMs = positiveLimit(options.requestDeadlineMs ?? 120_000, "requestDeadlineMs");
75
+ this.#requestDeadlineMs = positiveLimit(options.requestDeadlineMs ?? DEFAULT_REQUEST_DEADLINE_MS, "requestDeadlineMs");
72
76
  positiveLimit(this.#maxTarballBytes, "maxTarballBytes");
73
77
  this.#signal = options.signal;
74
78
  const { scopedRegistries, ...baseOptions } = options;
@@ -143,6 +147,33 @@ export class RegistryClient {
143
147
  }
144
148
  return validatePackument(raw, name, this.#origin, this.#maxMetadataVersions);
145
149
  }
150
+ /** Fetch one exact version without transferring every release in a packument. */
151
+ async versionManifest(name, version, options = {}) {
152
+ const selected = this.forPackage(name);
153
+ if (selected !== this)
154
+ return selected.versionManifest(name, version, options);
155
+ const url = `${this.#registry}${escapePackageName(name)}/${encodeURIComponent(version)}`;
156
+ this.#logger?.debug("fetching exact version metadata", {
157
+ name,
158
+ version,
159
+ url,
160
+ });
161
+ let raw;
162
+ try {
163
+ raw = await this.#withRequest(url, `${INSTALL_V1_ACCEPT}, application/json`, (response) => this.#readMetadata(response), options.signal);
164
+ }
165
+ catch (error) {
166
+ throw mapFetchError(error, `fetch metadata for ${name}@${version}`);
167
+ }
168
+ const validated = validatePackument({ name, versions: { [version]: raw } }, name, this.#origin, 1);
169
+ const entry = validated.versions[version];
170
+ if (entry === undefined)
171
+ throw new ZyrkError("ZYRK_RESOLUTION_FAILED", `registry has no version ${version} of ${name}`, {
172
+ detail: { name, version },
173
+ remedy: "check the requested version exists in the configured registry",
174
+ });
175
+ return entry;
176
+ }
146
177
  /** Registry advisory request; private names may never follow an external redirect. */
147
178
  async auditAdvisories(packages, options = {}) {
148
179
  for (const [name, versions] of Object.entries(packages)) {
@@ -414,7 +445,10 @@ export class RegistryClient {
414
445
  throw resourceLimit("redirect count", 10, "registry redirect configuration");
415
446
  }
416
447
  catch (error) {
417
- active?.body.destroy();
448
+ // A bounded body consumer can stop while the decoder still has source
449
+ // bytes arriving. Abort the transport before closing the exposed stream
450
+ // so compressed responses cannot write into a closed decompressor.
451
+ cancel();
418
452
  if (timedOut || signals.some((source) => source.aborted))
419
453
  throw resourceLimit("request deadline or cancellation", this.#requestDeadlineMs, "requestDeadlineMs");
420
454
  throw error;
@@ -3,5 +3,10 @@ import type { ExtractionLimits } from "../extract/tarball.ts";
3
3
  export declare function installBudgets(env: NodeJS.ProcessEnv): {
4
4
  resolveBudget: Partial<ResolverLimits>;
5
5
  extractBudget: Partial<ExtractionLimits>;
6
+ fetchBudget: {
7
+ deadlineMs: number;
8
+ };
6
9
  };
10
+ /** Bound slow registry transfers while allowing large framework archives. */
11
+ export declare function registryRequestDeadline(env: NodeJS.ProcessEnv): number;
7
12
  //# sourceMappingURL=budgets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"budgets.d.ts","sourceRoot":"","sources":["../../src/install/budgets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAwB9D,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG;IACtD,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACvC,aAAa,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC1C,CASA"}
1
+ {"version":3,"file":"budgets.d.ts","sourceRoot":"","sources":["../../src/install/budgets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAyB9D,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG;IACtD,aAAa,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACvC,aAAa,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,WAAW,EAAE;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACrC,CAYA;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAEtE"}
@@ -1,15 +1,15 @@
1
1
  /** Public, bounded install timeout configuration. Node and byte limits stay intact. */
2
2
  import { ZyrkError } from "../errors.js";
3
- function timeout(env, key, fallback) {
3
+ function timeout(env, key, fallback, maximum = 900000) {
4
4
  const raw = env[key];
5
5
  if (raw === undefined)
6
6
  return fallback;
7
7
  if (!/^[0-9]+$/.test(raw) ||
8
8
  !Number.isSafeInteger(Number(raw)) ||
9
9
  Number(raw) < 1 ||
10
- Number(raw) > 900000)
11
- throw new ZyrkError("ZYRK_POLICY_DENIED", `Cannot configure install: ${key} must be an integer from 1 to 900000.`, {
12
- remedy: `Set ${key} to a timeout in milliseconds from 1 to 900000, or unset it to use the default.`,
10
+ Number(raw) > maximum)
11
+ throw new ZyrkError("ZYRK_POLICY_DENIED", `Cannot configure install: ${key} must be an integer from 1 to ${maximum}.`, {
12
+ remedy: `Set ${key} to a timeout in milliseconds from 1 to ${maximum}, or unset it to use the default.`,
13
13
  });
14
14
  return Number(raw);
15
15
  }
@@ -21,5 +21,12 @@ export function installBudgets(env) {
21
21
  extractBudget: {
22
22
  maxDurationMs: timeout(env, "ZYRK_EXTRACT_TIMEOUT_MS", 120000),
23
23
  },
24
+ fetchBudget: {
25
+ deadlineMs: timeout(env, "ZYRK_FETCH_TIMEOUT_MS", 900000, 3600000),
26
+ },
24
27
  };
25
28
  }
29
+ /** Bound slow registry transfers while allowing large framework archives. */
30
+ export function registryRequestDeadline(env) {
31
+ return timeout(env, "ZYRK_REQUEST_TIMEOUT_MS", 900000, 3600000);
32
+ }
@@ -1,5 +1,9 @@
1
1
  import { type RuntimePlatform, type FinalizedGraph } from "../resolve/resolver.ts";
2
2
  import type { WorkspaceGraph } from "./workspace-graph.ts";
3
+ /** Summarize expected platform filtering without emitting one warning per variant. */
4
+ export declare function platformOmissionSummary(omissions: readonly {
5
+ name: string;
6
+ }[]): string | undefined;
3
7
  export declare function selectPlatformGraph(graph: FinalizedGraph, optionalRoots: ReadonlySet<string>, platform?: RuntimePlatform): FinalizedGraph;
4
8
  export declare function selectWorkspacePlatform(graph: WorkspaceGraph, platform?: RuntimePlatform, omitDev?: boolean): WorkspaceGraph;
5
9
  //# sourceMappingURL=platform.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/install/platform.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAuG3D,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,EAClC,QAAQ,kBAAoB,GAC3B,cAAc,CAqBhB;AACD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,cAAc,EACrB,QAAQ,kBAAoB,EAC5B,OAAO,UAAQ,GACd,cAAc,CA+BhB"}
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/install/platform.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,sFAAsF;AACtF,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GACrC,MAAM,GAAG,SAAS,CAQpB;AAsGD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,EAClC,QAAQ,kBAAoB,GAC3B,cAAc,CAqBhB;AACD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,cAAc,EACrB,QAAQ,kBAAoB,EAC5B,OAAO,UAAQ,GACd,cAAc,CA+BhB"}
@@ -3,6 +3,17 @@ import { satisfies } from "semver";
3
3
  import { ZyrkError } from "../errors.js";
4
4
  import { currentPlatform, } from "../resolve/resolver.js";
5
5
  import { lockMismatch } from "./lock.js";
6
+ /** Summarize expected platform filtering without emitting one warning per variant. */
7
+ export function platformOmissionSummary(omissions) {
8
+ if (omissions.length === 0)
9
+ return undefined;
10
+ const packages = new Set(omissions.map((item) => item.name));
11
+ const entries = omissions.length;
12
+ const variants = packages.size;
13
+ const entryLabel = `${entries} platform-specific optional ${entries === 1 ? "dependency" : "dependencies"}`;
14
+ const packageLabel = `${variants} ${variants === 1 ? "package variant" : "package variants"}`;
15
+ return `Skipped ${entryLabel} across ${packageLabel}; this is expected when selecting dependencies for the current platform.`;
16
+ }
6
17
  function compatible(node, platform) {
7
18
  return ([
8
19
  [node.platform?.os, platform.os],
@@ -12,6 +12,7 @@ export type BuildCandidate = {
12
12
  command: string;
13
13
  }>;
14
14
  };
15
+ export declare const WORKSPACE_POLICY_FILE = "zyrk-workspace.json";
15
16
  export declare function candidates(nodes: readonly ResolvedOccurrence[], dirs: ReadonlyMap<string, string>): Promise<BuildCandidate[]>;
16
17
  export declare function candidateDigest(graph: GraphDoc, builds: readonly BuildCandidate[], policy: unknown, patches: string): string;
17
18
  export declare function approveCandidate(options: {
@@ -21,7 +22,15 @@ export declare function approveCandidate(options: {
21
22
  selected: readonly string[];
22
23
  env?: NodeJS.ProcessEnv;
23
24
  policy?: unknown;
25
+ portable?: boolean;
24
26
  }): Promise<void>;
27
+ export declare function approvedCandidates(options: {
28
+ project: string;
29
+ env: NodeJS.ProcessEnv;
30
+ digest: string;
31
+ builds: readonly BuildCandidate[];
32
+ policy: unknown;
33
+ }): Promise<BuildCandidate[]>;
25
34
  export declare function assertApproved(options: {
26
35
  project: string;
27
36
  env: NodeJS.ProcessEnv;
@@ -1 +1 @@
1
- {"version":3,"file":"private.d.ts","sourceRoot":"","sources":["../../src/install/private.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,OAAO,EAA2B,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAOjE,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD,CAAC;AAUF,wBAAsB,UAAU,CAC9B,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,OAAO,CAAC,cAAc,EAAE,CAAC,CA8C3B;AAyBD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,GACd,MAAM,CASR;AACD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhB;AAcD,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChB;AACD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,QAAQ,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGzB;AAID,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFhB"}
1
+ {"version":3,"file":"private.d.ts","sourceRoot":"","sources":["../../src/install/private.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,OAAO,EAA2B,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAOjE,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD,CAAC;AACF,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAe3D,wBAAsB,UAAU,CAC9B,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,OAAO,CAAC,cAAc,EAAE,CAAC,CA8C3B;AAyBD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,GACd,MAAM,CASR;AACD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChB;AA0ED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAgC5B;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,MAAM,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhB;AACD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,QAAQ,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGzB;AAID,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFhB"}
@@ -13,6 +13,7 @@ import { canonicalJson } from "../util/json.js";
13
13
  import { isErrno, pathExists, writeFileAtomic } from "../util/fsx.js";
14
14
  import { superviseChild } from "../util/proc.js";
15
15
  import { redactSecrets } from "../log.js";
16
+ export const WORKSPACE_POLICY_FILE = "zyrk-workspace.json";
16
17
  function fail(message) {
17
18
  throw new ZyrkError("ZYRK_SCRIPT_BLOCKED", message, {
18
19
  remedy: "Review the exact candidate with zyrk approve-builds, then explicitly approve its package names. Mandatory denials cannot be overridden.",
@@ -93,16 +94,62 @@ export async function approveCandidate(options) {
93
94
  const selected = new Set(options.selected);
94
95
  const all = selected.has("*");
95
96
  for (const name of selected)
96
- if (name !== "*" && !options.builds.some((b) => b.name === name))
97
+ if (name !== "*" &&
98
+ !options.builds.some((b) => b.name === name || b.sourceId === name))
97
99
  fail("An approval selector does not match a reviewed build candidate.");
98
100
  await writeFileAtomic(path.join(options.project, ".zyrk-local", "approvals.json"), JSON.stringify({
99
101
  schemaVersion: 1,
100
102
  candidate: options.digest,
101
103
  sources: options.builds
102
- .filter((b) => all || selected.has(b.name))
104
+ .filter((b) => all || selected.has(b.name) || selected.has(b.sourceId))
103
105
  .map((b) => b.sourceId)
104
106
  .sort(),
105
107
  }), { mode: 0o600 });
108
+ if (options.portable) {
109
+ const selectedBuilds = options.builds.filter((build) => all || selected.has(build.name) || selected.has(build.sourceId));
110
+ await writePortableApprovals(options.project, selectedBuilds);
111
+ }
112
+ }
113
+ async function readPortableApprovals(project) {
114
+ const file = path.join(project, WORKSPACE_POLICY_FILE);
115
+ let raw;
116
+ try {
117
+ const stat = await lstat(file);
118
+ if (!stat.isFile() || stat.isSymbolicLink())
119
+ fail("The workspace build-approval file must be a regular file.");
120
+ raw = JSON.parse(await readFile(file, "utf8"));
121
+ }
122
+ catch (cause) {
123
+ if (cause instanceof ZyrkError)
124
+ throw cause;
125
+ if (isErrno(cause, "ENOENT"))
126
+ return [];
127
+ fail("The workspace build-approval file is unreadable or malformed.");
128
+ }
129
+ if (!object(raw) ||
130
+ raw.schemaVersion !== 1 ||
131
+ !Array.isArray(raw.approvedBuilds) ||
132
+ raw.approvedBuilds.some((item) => !object(item) ||
133
+ typeof item.name !== "string" ||
134
+ typeof item.version !== "string" ||
135
+ typeof item.sourceId !== "string" ||
136
+ !/^src1_[a-f0-9]{40}$/.test(item.sourceId)))
137
+ fail("The workspace build-approval file has an unsupported format.");
138
+ return raw.approvedBuilds;
139
+ }
140
+ async function writePortableApprovals(project, selected) {
141
+ const current = await readPortableApprovals(project);
142
+ const bySource = new Map(current.map((item) => [item.sourceId, item]));
143
+ for (const build of selected)
144
+ bySource.set(build.sourceId, {
145
+ name: build.name,
146
+ version: build.version,
147
+ sourceId: build.sourceId,
148
+ });
149
+ await writeFileAtomic(path.join(project, WORKSPACE_POLICY_FILE), `${JSON.stringify({
150
+ schemaVersion: 1,
151
+ approvedBuilds: [...bySource.values()].sort((a, b) => a.name.localeCompare(b.name)),
152
+ }, null, 2)}\n`, { mode: 0o644 });
106
153
  }
107
154
  async function readUserPolicy(env) {
108
155
  let user = {};
@@ -117,9 +164,9 @@ async function readUserPolicy(env) {
117
164
  fail("Mandatory user build policy is malformed.");
118
165
  return user;
119
166
  }
120
- export async function assertApproved(options) {
167
+ export async function approvedCandidates(options) {
121
168
  if (!options.builds.length)
122
- return;
169
+ return [];
123
170
  const user = await readUserPolicy(options.env);
124
171
  let local;
125
172
  try {
@@ -133,19 +180,29 @@ export async function assertApproved(options) {
133
180
  throw cause;
134
181
  local = undefined;
135
182
  }
136
- for (const build of options.builds) {
183
+ const portable = await readPortableApprovals(options.project);
184
+ return options.builds.filter((build) => {
137
185
  if (denied(user.scripts, build) || denied(options.policy, build))
138
- fail(`Build of ${build.name}@${build.version} is denied by policy.`);
139
- const portable = object(options.policy) &&
186
+ return false;
187
+ const manifestAllowed = object(options.policy) &&
140
188
  Array.isArray(options.policy.allow) &&
141
189
  options.policy.allow.includes(build.sourceId);
142
- if (!portable &&
143
- (!object(local) ||
144
- local.schemaVersion !== 1 ||
145
- local.candidate !== options.digest ||
146
- !Array.isArray(local.sources) ||
147
- !local.sources.includes(build.sourceId)))
148
- fail(`Build of ${build.name}@${build.version} has not been approved for this exact candidate.`);
190
+ const workspaceAllowed = portable.some((approval) => approval.sourceId === build.sourceId);
191
+ const localAllowed = object(local) &&
192
+ local.schemaVersion === 1 &&
193
+ local.candidate === options.digest &&
194
+ Array.isArray(local.sources) &&
195
+ local.sources.includes(build.sourceId);
196
+ return manifestAllowed || workspaceAllowed || localAllowed;
197
+ });
198
+ }
199
+ export async function assertApproved(options) {
200
+ const approved = await approvedCandidates(options);
201
+ if (approved.length !== options.builds.length) {
202
+ const pending = options.builds.find((build) => !approved.some((item) => item.sourceId === build.sourceId));
203
+ fail(pending
204
+ ? `Build of ${pending.name}@${pending.version} has not been approved for this exact source.`
205
+ : "Not every dependency build candidate is approved.");
149
206
  }
150
207
  }
151
208
  export async function materializePrivate(options) {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/install/registry.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAoB,MAAM,sBAAsB,CAAC;AAKxE,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,cAAc,CAAC,CAuI1B"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/install/registry.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAoB,MAAM,sBAAsB,CAAC;AAMxE,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,cAAc,CAAC,CAwI1B"}
@@ -8,6 +8,7 @@ import { RegistryClient, DEFAULT_REGISTRY } from "../fetch/registry.js";
8
8
  import { isErrno } from "../util/fsx.js";
9
9
  import { findProjectBoundary } from "../config/paths.js";
10
10
  import { cleanUrl } from "./lock.js";
11
+ import { registryRequestDeadline } from "./budgets.js";
11
12
  export async function installRegistry(options) {
12
13
  let registry = DEFAULT_REGISTRY;
13
14
  const auth = {};
@@ -97,6 +98,7 @@ export async function installRegistry(options) {
97
98
  return new RegistryClient({
98
99
  registry,
99
100
  scopedRegistries,
101
+ requestDeadlineMs: registryRequestDeadline(options.env),
100
102
  userAgent: `zyrk/${options.version}`,
101
103
  fetchOptions: auth,
102
104
  });
@@ -43,6 +43,7 @@ export type InstallOptions = {
43
43
  env?: NodeJS.ProcessEnv;
44
44
  reviewBuilds?: boolean;
45
45
  approveBuilds?: readonly string[];
46
+ requestBuildApproval?: (builds: readonly BuildCandidate[]) => Promise<readonly string[]>;
46
47
  rebuild?: boolean;
47
48
  repair?: boolean;
48
49
  resolveBudget?: Partial<ResolverLimits>;
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/install/run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/install/run.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { sourceProgress, reportLinked } from "./progress.js";
2
- import { selectPlatformGraph } from "./platform.js";
2
+ import { platformOmissionSummary, selectPlatformGraph } from "./platform.js";
3
3
  import { findWorkspaceRoot } from "./workspaces.js";
4
4
  import { assertSupportedSourcePayload } from "./source-policy.js";
5
5
  import { prepareRepair } from "./repair.js";
6
6
  import { createFetchOperation } from "../fetch/package.js";
7
- import { candidates, candidateDigest, approveCandidate, assertApproved, materializePrivate, buildPrivate, } from "./private.js";
7
+ import { candidates, candidateDigest, approveCandidate, approvedCandidates, materializePrivate, buildPrivate, } from "./private.js";
8
8
  import { loadProjectPatches, applyProjectPatches } from "../commands/patch.js";
9
9
  import { registerGeneration } from "./metadata.js";
10
10
  import { randomId } from "../util/ids.js";
@@ -102,6 +102,7 @@ export async function installProject(options) {
102
102
  ...options,
103
103
  resolveBudget: { ...budgets.resolveBudget, ...options.resolveBudget },
104
104
  extractBudget: { ...budgets.extractBudget, ...options.extractBudget },
105
+ fetchBudget: { ...budgets.fetchBudget, ...options.fetchBudget },
105
106
  };
106
107
  if (await findWorkspaceRoot(options.projectDir)) {
107
108
  const { installWorkspaceProject } = await import("./workspace-install.js");
@@ -272,6 +273,9 @@ export async function installProject(options) {
272
273
  packument: (name) => options.registry.packument(name, {
273
274
  signal: operation.operation.signal,
274
275
  }),
276
+ versionManifest: (name, version) => options.registry.versionManifest(name, version, {
277
+ signal: operation.operation.signal,
278
+ }),
275
279
  },
276
280
  requirements: Object.entries(allDeps)
277
281
  .map(([name, spec]) => {
@@ -416,7 +420,10 @@ export async function installProject(options) {
416
420
  name: "package.json",
417
421
  contents: `${JSON.stringify(manifest, null, 2)}\n`,
418
422
  });
419
- const warnings = (activeResolution.omissions ?? []).map((item) => `${item.name}: omitted optional dependency incompatible with the selected platform.`);
423
+ const warnings = [];
424
+ const omissionSummary = platformOmissionSummary(activeResolution.omissions ?? []);
425
+ if (omissionSummary)
426
+ warnings.push(omissionSummary);
420
427
  for (const stage of ["preinstall", "install", "postinstall"])
421
428
  if (manifest.scripts?.[stage])
422
429
  warnings.push(`Root ${stage} hook was not run; invoke it explicitly after installation.`);
@@ -549,6 +556,7 @@ export async function installProject(options) {
549
556
  selected: options.approveBuilds,
550
557
  env: options.env ?? process.env,
551
558
  policy: scriptPolicy(manifest),
559
+ portable: true,
552
560
  });
553
561
  return {
554
562
  readiness: "build-review",
@@ -559,15 +567,37 @@ export async function installProject(options) {
559
567
  builds,
560
568
  };
561
569
  }
562
- const unbuilt = builds.length > 0 && options.ignoreScripts === true;
570
+ let approvedBuilds = [];
563
571
  if (builds.length && !options.ignoreScripts) {
564
- await assertApproved({
572
+ const approvalOptions = {
565
573
  project,
566
574
  env: options.env ?? process.env,
567
575
  digest: reviewedDigest,
568
576
  builds,
569
577
  policy: scriptPolicy(manifest),
570
- });
578
+ };
579
+ approvedBuilds = await approvedCandidates(approvalOptions);
580
+ if (approvedBuilds.length < builds.length &&
581
+ options.requestBuildApproval) {
582
+ const pending = builds.filter((build) => !approvedBuilds.some((item) => item.sourceId === build.sourceId));
583
+ const selected = await options.requestBuildApproval(pending);
584
+ if (selected.length) {
585
+ await approveCandidate({
586
+ project,
587
+ digest: reviewedDigest,
588
+ builds: pending,
589
+ selected,
590
+ env: options.env ?? process.env,
591
+ policy: scriptPolicy(manifest),
592
+ portable: true,
593
+ });
594
+ approvedBuilds = await approvedCandidates(approvalOptions);
595
+ }
596
+ }
597
+ }
598
+ const unbuilt = builds.length > 0 &&
599
+ (options.ignoreScripts === true || approvedBuilds.length < builds.length);
600
+ if (approvedBuilds.length && !options.ignoreScripts) {
571
601
  if (!privateGraph) {
572
602
  realized = await materializePrivate({
573
603
  project,
@@ -582,7 +612,7 @@ export async function installProject(options) {
582
612
  project,
583
613
  generationId,
584
614
  nodes: activeNodes,
585
- builds,
615
+ builds: approvedBuilds,
586
616
  realized,
587
617
  env: options.env ?? process.env,
588
618
  });
@@ -606,9 +636,10 @@ export async function installProject(options) {
606
636
  privateGraph = true;
607
637
  }
608
638
  }
609
- if (unbuilt)
610
- for (const build of builds)
611
- warnings.push(`${build.name}@${build.version}: skipped ${build.stages.map((s) => s.name).join(", ")}; intentionally unbuilt.`);
639
+ if (unbuilt) {
640
+ const skipped = builds.length - approvedBuilds.length;
641
+ warnings.push(`${skipped} dependency build ${skipped === 1 ? "script was" : "scripts were"} skipped because approval was not granted; readiness is intentionally unbuilt. Run zyrk approve-builds to review and approve sources.`);
642
+ }
612
643
  if (privateGraph)
613
644
  warnings.push("Using a complete project-private dependency graph; build/runtime writes do not change shared source.");
614
645
  const entries = selectedRoots.map((r) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-install.d.ts","sourceRoot":"","sources":["../../src/install/workspace-install.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuH9D,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAqrBxB"}
1
+ {"version":3,"file":"workspace-install.d.ts","sourceRoot":"","sources":["../../src/install/workspace-install.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuH9D,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAqtBxB"}
@@ -1,5 +1,5 @@
1
1
  import { sourceProgress, reportLinked } from "./progress.js";
2
- import { selectWorkspacePlatform } from "./platform.js";
2
+ import { platformOmissionSummary, selectWorkspacePlatform, } from "./platform.js";
3
3
  /** Multi-importer live workspace installation with one publication journal. */
4
4
  import { chmod, copyFile, lstat, mkdir, readFile, realpath, rename, rm, symlink, } from "node:fs/promises";
5
5
  import { constants } from "node:fs";
@@ -32,7 +32,7 @@ import { loadProjectPatches, applyProjectPatches } from "../commands/patch.js";
32
32
  import { prepareRepair } from "./repair.js";
33
33
  import { assertSupportedSourcePayload } from "./source-policy.js";
34
34
  import { registerGeneration } from "./metadata.js";
35
- import { candidates, candidateDigest, approveCandidate, assertApproved, buildPrivate, } from "./private.js";
35
+ import { candidates, candidateDigest, approveCandidate, approvedCandidates, buildPrivate, } from "./private.js";
36
36
  const object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
37
37
  const unsupported = (message) => {
38
38
  throw new ZyrkError("ZYRK_NOT_IMPLEMENTED", message, {
@@ -191,7 +191,10 @@ export async function installWorkspaceProject(options) {
191
191
  ? await readFile(path.join(root, "package-lock.json"), "utf8")
192
192
  : JSON.stringify(doc, null, 2) + "\n";
193
193
  graph = selectWorkspacePlatform(graph, options.platform, options.omitDev);
194
- const warnings = graph.omissions.map((o) => `${o.name}: omitted incompatible optional workspace dependency.`);
194
+ const warnings = [];
195
+ const omissionSummary = platformOmissionSummary(graph.omissions);
196
+ if (omissionSummary)
197
+ warnings.push(omissionSummary);
195
198
  for (const importer of project.importers)
196
199
  for (const stage of ["preinstall", "install", "postinstall"])
197
200
  if (importer.manifest.scripts?.[stage])
@@ -441,6 +444,7 @@ export async function installWorkspaceProject(options) {
441
444
  selected: options.approveBuilds,
442
445
  env: options.env ?? process.env,
443
446
  policy,
447
+ portable: true,
444
448
  });
445
449
  return {
446
450
  readiness: "build-review",
@@ -451,14 +455,38 @@ export async function installWorkspaceProject(options) {
451
455
  builds,
452
456
  };
453
457
  }
458
+ let approvedBuilds = [];
454
459
  if (builds.length && !options.ignoreScripts) {
455
- await assertApproved({
460
+ const approvalOptions = {
456
461
  project: root,
457
462
  env: options.env ?? process.env,
458
463
  digest,
459
464
  builds,
460
465
  policy,
461
- });
466
+ };
467
+ approvedBuilds = await approvedCandidates(approvalOptions);
468
+ if (approvedBuilds.length < builds.length &&
469
+ options.requestBuildApproval) {
470
+ const pending = builds.filter((build) => !approvedBuilds.some((item) => item.sourceId === build.sourceId));
471
+ const selected = await options.requestBuildApproval(pending);
472
+ if (selected.length) {
473
+ await approveCandidate({
474
+ project: root,
475
+ digest,
476
+ builds: pending,
477
+ selected,
478
+ env: options.env ?? process.env,
479
+ policy,
480
+ portable: true,
481
+ });
482
+ approvedBuilds = await approvedCandidates(approvalOptions);
483
+ }
484
+ }
485
+ }
486
+ const skippedBuilds = builds.length - approvedBuilds.length;
487
+ if (skippedBuilds > 0)
488
+ warnings.push(`${skippedBuilds} dependency build ${skippedBuilds === 1 ? "script was" : "scripts were"} skipped because approval was not granted; readiness is intentionally unbuilt. Run zyrk approve-builds to review and approve sources.`);
489
+ if (approvedBuilds.length && !options.ignoreScripts) {
462
490
  // Approved writes need a coherent fully private registry closure.
463
491
  await allocatePrivate(new Set(nodes.map((n) => n.key)));
464
492
  options.onProgress?.({ phase: "building" });
@@ -466,12 +494,12 @@ export async function installWorkspaceProject(options) {
466
494
  project: root,
467
495
  generationId,
468
496
  nodes,
469
- builds,
497
+ builds: approvedBuilds,
470
498
  realized,
471
499
  env: options.env ?? process.env,
472
500
  });
473
501
  }
474
- const readiness = builds.length && options.ignoreScripts
502
+ const readiness = builds.length && (options.ignoreScripts || skippedBuilds > 0)
475
503
  ? "intentionally-unbuilt"
476
504
  : "ready";
477
505
  registration = await registerGeneration({
@@ -1 +1 @@
1
- {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/lockfile/write.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,mBAAmB,CAAC;IAChC,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,eAAe,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CACzB,CAAC;AAEF,wBAAsB,aAAa,CACjC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,UAAU,CAAC,CAqOrB"}
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/lockfile/write.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,mBAAmB,CAAC;IAChC,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,eAAe,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CACzB,CAAC;AAEF,wBAAsB,aAAa,CACjC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,UAAU,CAAC,CAqPrB"}
@@ -94,7 +94,23 @@ export async function writeLockfile(options) {
94
94
  : `npm:${target.name}@${target.version}`;
95
95
  }
96
96
  }
97
+ const npmMetadata = {};
98
+ const existingEntry = existingPackages[location];
99
+ if (existingEntry !== undefined) {
100
+ for (const field of [
101
+ "license",
102
+ "funding",
103
+ "deprecated",
104
+ "hasInstallScript",
105
+ "inBundle",
106
+ ]) {
107
+ if (Object.hasOwn(existingEntry, field)) {
108
+ npmMetadata[field] = existingEntry[field];
109
+ }
110
+ }
111
+ }
97
112
  packages[location] = {
113
+ ...npmMetadata,
98
114
  name: node.name,
99
115
  version: node.version,
100
116
  resolved: node.tarballUrl,
@@ -1,9 +1,10 @@
1
- import { type Packument } from "../fetch/registry.ts";
1
+ import { type Packument, type PackumentVersion } from "../fetch/registry.ts";
2
2
  import { type OverridePolicy } from "./overrides.ts";
3
3
  export type MetadataProvider = {
4
4
  readonly origin: string;
5
5
  originFor?(name: string): string;
6
6
  packument(name: string): Promise<Packument>;
7
+ versionManifest?(name: string, version: string): Promise<PackumentVersion>;
7
8
  };
8
9
  export type EdgeKind = "prod" | "optional" | "peer";
9
10
  export type ResolvedEdge = {
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/resolve/resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/resolve/resolver.ts"],"names":[],"mappings":"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"}
@@ -70,6 +70,7 @@ class ResolverState {
70
70
  provider;
71
71
  overrides;
72
72
  packuments = new Map();
73
+ versionManifests = new Map();
73
74
  peerless = new Map();
74
75
  peerBearing = new Map();
75
76
  freshPeers = new Map();
@@ -142,6 +143,23 @@ class ResolverState {
142
143
  this.packuments.set(name, pending);
143
144
  return pending;
144
145
  }
146
+ versionManifestOf(name, version) {
147
+ const key = `${name}\0${version}`;
148
+ const cached = this.versionManifests.get(key);
149
+ if (cached !== undefined)
150
+ return cached;
151
+ this.checkBudget();
152
+ let timer;
153
+ const deadline = new Promise((_resolve, reject) => {
154
+ timer = setTimeout(() => reject(budgetError("resolution deadline exceeded")), Math.max(1, this.expires - Date.now()));
155
+ });
156
+ const pending = Promise.race([
157
+ this.provider.versionManifest(name, version),
158
+ deadline,
159
+ ]).finally(() => clearTimeout(timer));
160
+ this.versionManifests.set(key, pending);
161
+ return pending;
162
+ }
145
163
  /**
146
164
  * Build (or reuse) the occurrence for one dependency edge. Peerless
147
165
  * packages share one occurrence per name@version; packages declaring
@@ -442,6 +460,21 @@ class ResolverState {
442
460
  // Version selection
443
461
  // ---------------------------------------------------------------------------
444
462
  async function selectVersion(state, req) {
463
+ if (req.targetKind === "exact" && state.provider.versionManifest) {
464
+ try {
465
+ return {
466
+ version: req.targetSpec,
467
+ entry: await state.versionManifestOf(req.realName, req.targetSpec),
468
+ };
469
+ }
470
+ catch (error) {
471
+ // Registries and older test adapters may expose only packuments. Keep
472
+ // the optimization opportunistic when the exact endpoint is absent.
473
+ if (!(error instanceof ZyrkError) || error.detail?.status !== 404) {
474
+ throw error;
475
+ }
476
+ }
477
+ }
445
478
  const packument = await state.packumentOf(req.realName);
446
479
  const fail = (message, remedy) => new ZyrkError("ZYRK_RESOLUTION_FAILED", message, {
447
480
  detail: { name: req.realName, spec: req.targetSpec },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyrk",
3
- "version": "0.6.6",
3
+ "version": "0.7.1",
4
4
  "description": "Storage-efficient Node package manager: one verified store per user, shared across projects through links.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "version:bump": "node scripts/bump-version.js",
21
21
  "setup:hooks": "git config core.hooksPath .githooks",
22
22
  "build": "tsc -p tsconfig.build.json",
23
- "prepublishOnly": "npm run build"
23
+ "prepublishOnly": "node bin/zyrk.js run build"
24
24
  },
25
25
  "dependencies": {
26
26
  "make-fetch-happen": "16.0.1",