zyrk 0.6.2 → 0.6.6

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
@@ -178,7 +178,14 @@ For `create next-app`, the native adapter accepts resolved create-next-app
178
178
  scaffold check covers 16.4.2. Other versions fail before execution. zyrk passes the official `--skip-install` flag to
179
179
  create-next-app, validates the generated project manifest, then installs its
180
180
  dependencies through zyrk using the same store and registry configuration. An
181
- explicit `--skip-install` leaves the generated project uninstalled. The official
181
+ explicit `--skip-install` leaves the generated project uninstalled.
182
+ The private initializer displays `Using zyrk.` for native installation, or
183
+ `Scaffolding only; dependency installation skipped.` for an explicit skip. This
184
+ adapts verified upstream display statements without changing shared source
185
+ files, installer execution, generated metadata, or unrelated npm text. An
186
+ unrecognized display contract fails before the initializer runs. Conflicting
187
+ `--use-npm`, `--use-pnpm`, `--use-yarn`, and `--use-bun` selections require an
188
+ explicit `--skip-install`; remove them to install with zyrk. The official
182
189
  flag can select create-next-app's configuration defaults instead of prompting
183
190
  for each choice; pass its explicit configuration flags to customize them. Its
184
191
  interactive project-name prompt remains available. Supply an explicit target
@@ -187,9 +194,13 @@ limited to one newly created direct child directory. If generation exits without
187
194
  a valid manifest, zyrk reports failure and retains any generated files for review.
188
195
 
189
196
  For Next.js 16.4.2 with dependencies in the external shared store, use
190
- `zyrk run dev -- --webpack` and `zyrk run build -- --webpack`. Strict-root
191
- Turbopack rejects that layout. The [Next compatibility report](docs/next-compatibility.md)
192
- records the tested scaffold, native install, build and HTTP runtime.
197
+ `zyrk run dev -- --webpack` and `zyrk run build -- --webpack`, or explicitly opt
198
+ into Turbopack with `zyrk next --turbopack` before normal `zyrk run dev` / build.
199
+ The [Turbopack guide](docs/next-turbopack.md) explains the experimental additional
200
+ roots, preserved configuration, portable local-state lookup, and deployment
201
+ boundary. The [Next compatibility report](docs/next-compatibility.md) records
202
+ the tested scaffold, native install, build and HTTP runtime. These checks cover
203
+ Linux x64; they do not establish universal framework or device support.
193
204
 
194
205
  Other initializers control their own dependency-install step and may invoke
195
206
  another package manager. Use their skip-install option, when available, then run
@@ -394,7 +405,12 @@ recorded in the manifest fingerprint; frozen installs reject policy drift.
394
405
  `zyrk help` and `zyrk --help` both show help successfully. Installs report
395
406
  preparation, resolution, verified source counts, realization, approved builds,
396
407
  and project linking on stderr. Interactive terminals show an activity spinner;
397
- redirected output contains plain phase messages. `--json` disables progress.
408
+ redirected output contains plain progress messages. Both `zyrk add prettier` and
409
+ `zyrk install prettier` show these details. Source counters distinguish verified
410
+ reuse from newly downloaded sources; downloads show received bytes. A checkmark
411
+ lists each unique package/version linked after the project transaction commits.
412
+ Source counts differ from dependency contexts when peers split a package.
413
+ `--json` disables progress.
398
414
 
399
415
  Install resolution has a five-minute deadline; each archive extraction has a
400
416
  two-minute deadline. Set `ZYRK_RESOLVE_TIMEOUT_MS` or `ZYRK_EXTRACT_TIMEOUT_MS`
package/VERSION CHANGED
@@ -1 +1 @@
1
- v0.6.2
1
+ v0.6.6
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AA2BA,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;AA2DzC,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,CA4ZpE"}
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"}
package/dist/cli/main.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { runNextCommand } from "../commands/next.js";
1
2
  import { createInstallProgress } from "./progress.js";
2
3
  import { runMaintenanceCommand } from "../commands/maintenance.js";
3
4
  import { runDeployCommand } from "../commands/deploy.js";
@@ -37,6 +38,7 @@ const NATIVE_COMMANDS = [
37
38
  ["exec", "Run an installed executable (never fetches)."],
38
39
  ["dlx", "Explicitly fetch and run a temporary tool environment."],
39
40
  ["create", "Scaffold from a create-* starter package."],
41
+ ["next", "Explicitly configure Next Turbopack: next --turbopack."],
40
42
  ["list, why", "Show the installed graph / why a dependency is selected."],
41
43
  ["doctor", "Diagnose filesystem, links, policy, install-state issues."],
42
44
  ["approve-builds, rebuild", "Review script permissions / rebuild."],
@@ -58,6 +60,7 @@ for (const [names] of NATIVE_COMMANDS) {
58
60
  const IMPLEMENTED_COMMANDS = new Set([
59
61
  "dlx",
60
62
  "create",
63
+ "next",
61
64
  "install",
62
65
  "i",
63
66
  "add",
@@ -375,6 +378,30 @@ export async function runCli(options) {
375
378
  return emit(fail(toZyrkError(error, `zyrk ${parsed.command} failed.`), parsed));
376
379
  }
377
380
  }
381
+ if (!parsed.help && !parsed.version && parsed.command === "next") {
382
+ try {
383
+ const result = await runNextCommand({
384
+ args: parsed.commandArgs,
385
+ cwd: options.cwd ?? process.cwd(),
386
+ env: options.env,
387
+ });
388
+ return emit({
389
+ exitCode: 0,
390
+ stdout: parsed.json
391
+ ? JSON.stringify({
392
+ schemaVersion: CLI_JSON_SCHEMA_VERSION,
393
+ command: "next",
394
+ status: "ok",
395
+ ...result,
396
+ }) + "\n"
397
+ : `${result.changed ? "Configured" : "Already configured"} Next Turbopack with the project's strict root and the installed external shared realizations. Experimental additional roots require Next.js 16.4+. Backup: ${result.backup}.\n`,
398
+ stderr: "",
399
+ });
400
+ }
401
+ catch (error) {
402
+ return emit(fail(toZyrkError(error, "Next Turbopack configuration failed."), parsed));
403
+ }
404
+ }
378
405
  if (!parsed.help &&
379
406
  !parsed.version &&
380
407
  (parsed.command === "install" || parsed.command === "i")) {
@@ -480,11 +507,13 @@ export async function runCli(options) {
480
507
  parsed.command === "remove" ||
481
508
  parsed.command === "update" ||
482
509
  parsed.command === "import")) {
510
+ const progress = createInstallProgress(options.stderr, parsed.json);
483
511
  try {
484
512
  const tools = options.tools === undefined
485
513
  ? await defaultTools(options.env, version, options.cwd ?? process.cwd())
486
514
  : await options.tools();
487
515
  const result = await runDependencyCommand({
516
+ onProgress: progress.update,
488
517
  command: parsed.command,
489
518
  env: options.env,
490
519
  args: parsed.commandArgs,
@@ -492,6 +521,7 @@ export async function runCli(options) {
492
521
  store: tools.store,
493
522
  registry: tools.registry,
494
523
  });
524
+ progress.close();
495
525
  if (parsed.json) {
496
526
  return emit({
497
527
  exitCode: 0,
@@ -501,13 +531,17 @@ export async function runCli(options) {
501
531
  }
502
532
  return emit({
503
533
  exitCode: 0,
504
- stdout: `Dependency command ${parsed.command} completed.\n`,
505
- stderr: "",
534
+ stdout: `Dependency command ${parsed.command} completed: ${result.packages} packages, ${result.reused} realizations reused; ${result.readiness}.\n`,
535
+ stderr: result.warnings.map((w) => `zyrk: ${w}\n`).join(""),
506
536
  });
507
537
  }
508
538
  catch (error) {
539
+ progress.close();
509
540
  return emit(fail(toZyrkError(error, `zyrk ${parsed.command} failed.`), parsed));
510
541
  }
542
+ finally {
543
+ progress.close();
544
+ }
511
545
  }
512
546
  // Project commands: run, exec, list, why, doctor, repair
513
547
  if (!parsed.help &&
@@ -614,7 +648,7 @@ async function runToolCommand(command, parsed, options, version) {
614
648
  options.stderr.write(isNext
615
649
  ? tail.includes("--skip-install")
616
650
  ? "zyrk: Next scaffolding only; dependency installation was explicitly skipped.\n"
617
- : "zyrk: Next scaffolder runs with --skip-install; zyrk will install dependencies.\n"
651
+ : "zyrk: Creating Next.js application; dependencies will install with zyrk.\n"
618
652
  : "zyrk: This initializer controls its own dependency installation.\n");
619
653
  const progress = createInstallProgress(options.stderr, false);
620
654
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/cli/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAWzD,kFAAkF;AAClF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAC1D,KAAK,EAAE,OAAO,GACb;IAAE,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CAuCzD"}
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/cli/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAYzD,kFAAkF;AAClF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE;IAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,EAC1D,KAAK,EAAE,OAAO,GACb;IAAE,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAE,CA2DzD"}
@@ -5,11 +5,13 @@ const labels = {
5
5
  realizing: "Preparing shared realizations",
6
6
  building: "Preparing approved private builds",
7
7
  linking: "Linking project dependencies",
8
+ complete: "Installation complete",
8
9
  };
9
10
  /** Presentation only: never writes to stdout or exposes package URLs or paths. */
10
11
  export function createInstallProgress(stream, quiet) {
11
12
  let timer;
12
13
  let current = "", last = "", frame = 0, closed = false;
14
+ let lastDownload = 0;
13
15
  const write = (text) => {
14
16
  try {
15
17
  stream.write(text);
@@ -23,11 +25,33 @@ export function createInstallProgress(stream, quiet) {
23
25
  update(event) {
24
26
  if (quiet || closed)
25
27
  return;
28
+ const pkg = event.package;
29
+ // Registry names are validated elsewhere; strip control characters here
30
+ // as presentation defense for direct API callers and hostile metadata.
31
+ const label = pkg
32
+ ? `${pkg.name}@${pkg.version}`.replace(/[\x00-\x1f\x7f-\x9f]/g, "")
33
+ : "";
34
+ if (pkg?.status === "linked") {
35
+ if (stream.isTTY)
36
+ write("\r\u001b[2K");
37
+ write(`✓ ${label} linked\n`);
38
+ return;
39
+ }
40
+ const downloading = pkg?.status === "downloading";
41
+ if (downloading && pkg.bytes && Date.now() - lastDownload < 100)
42
+ return;
43
+ if (downloading)
44
+ lastDownload = Date.now();
26
45
  current =
27
- labels[event.phase] +
46
+ (downloading
47
+ ? `Downloading ${label}: ${((pkg.bytes ?? 0) / 1024).toFixed(1)} KiB`
48
+ : labels[event.phase]) +
28
49
  (event.total === undefined
29
50
  ? ""
30
- : ` (${event.completed ?? 0}/${event.total})`);
51
+ : ` (${event.completed ?? 0}/${event.total})`) +
52
+ (event.reused === undefined
53
+ ? ""
54
+ : ` — reused ${event.reused}, downloaded ${event.downloaded ?? 0}`);
31
55
  if (stream.isTTY) {
32
56
  render();
33
57
  if (!timer) {
@@ -1,6 +1,6 @@
1
1
  import type { RegistryClient } from "../fetch/registry.ts";
2
2
  import type { Store } from "../store/layout.ts";
3
- import { type InstallResult } from "../install/run.ts";
3
+ import { type InstallOptions, type InstallResult } from "../install/run.ts";
4
4
  export type DependencyCommand = "add" | "remove" | "update" | "import";
5
5
  export declare function runDependencyCommand(options: {
6
6
  command: DependencyCommand;
@@ -9,5 +9,6 @@ export declare function runDependencyCommand(options: {
9
9
  store: Store;
10
10
  registry: RegistryClient;
11
11
  env?: NodeJS.ProcessEnv;
12
+ onProgress?: InstallOptions["onProgress"];
12
13
  }): Promise<InstallResult>;
13
14
  //# 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,EAGL,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;CACzB,GAAG,OAAO,CAAC,aAAa,CAAC,CA2HzB"}
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"}
@@ -15,6 +15,7 @@ export async function runDependencyCommand(options) {
15
15
  store: options.store,
16
16
  registry: options.registry,
17
17
  ...(options.env ? { env: options.env } : {}),
18
+ ...(options.onProgress ? { onProgress: options.onProgress } : {}),
18
19
  };
19
20
  const names = [];
20
21
  let from;
@@ -0,0 +1,10 @@
1
+ export declare function runNextCommand(options: {
2
+ args: readonly string[];
3
+ cwd: string;
4
+ env: NodeJS.ProcessEnv;
5
+ }): Promise<{
6
+ changed: boolean;
7
+ config: string;
8
+ backup: string;
9
+ }>;
10
+ //# sourceMappingURL=next.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/commands/next.ts"],"names":[],"mappings":"AAqFA,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA0LhE"}
@@ -0,0 +1,185 @@
1
+ /** Explicit Next.js configuration editing; ordinary installs never call this. */
2
+ import { constants } from "node:fs";
3
+ import { chmod, copyFile, lstat, readFile, realpath, readdir, rm, writeFile, } from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ import { satisfies } from "semver";
6
+ import { findProjectBoundary, resolveStoreRoot } from "../config/paths.js";
7
+ import { ZyrkError } from "../errors.js";
8
+ import { lockedGraph } from "../install/lock.js";
9
+ import { selectPlatformGraph } from "../install/platform.js";
10
+ import { validateLockDoc } from "../lockfile/read.js";
11
+ import { manifestFingerprint } from "../lockfile/fingerprints.js";
12
+ import { Store } from "../store/layout.js";
13
+ import { createGraphDoc, contextIdFor } from "../store/graph.js";
14
+ import { packageDirIn, selectRealizationDirs } from "../store/realize.js";
15
+ import { verifyRealization } from "../store/verify.js";
16
+ import { acquireLock } from "../util/locks.js";
17
+ import { isErrno, writeFileAtomic } from "../util/fsx.js";
18
+ import { sha512Hex } from "../util/hash.js";
19
+ import { canonicalJson } from "../util/json.js";
20
+ const HELPER = "next.config.zyrk-turbopack.cjs";
21
+ const DECLARATION = "next.config.zyrk-turbopack.d.cts";
22
+ const MARKER = "// Managed by explicit zyrk next --turbopack; original configuration is preserved.\n";
23
+ const TYPES = "declare function withZyrkTurbopack<T>(configuration: T, projectDir: string): T;\nexport = withZyrkTurbopack;\n";
24
+ const RUNTIME = `// Local paths are discovered at Next startup, never committed into configuration.
25
+ const fs = require("node:fs");
26
+ const path = require("node:path");
27
+ const crypto = require("node:crypto");
28
+ const canonical = value => JSON.stringify(value && typeof value === "object" ? Array.isArray(value) ? value.map(v => JSON.parse(canonical(v))) : Object.fromEntries(Object.keys(value).sort().map(k => [k, JSON.parse(canonical(value[k]))])) : value);
29
+ const inside = (root, file) => { const relative = path.relative(root, file); return relative === "" || (!relative.startsWith(".." + path.sep) && relative !== ".." && !path.isAbsolute(relative)); };
30
+ module.exports = (configuration, directory) => async (phase, context) => {
31
+ if (process.platform === "win32") throw new Error("zyrk Turbopack Windows support has not passed its compatibility gate.");
32
+ const project = fs.realpathSync(directory);
33
+ const state = JSON.parse(fs.readFileSync(path.join(project, "node_modules/.zyrk/install-state.json"), "utf8"));
34
+ if (state.schemaVersion !== 1 || state.readiness !== "ready" || state.linker !== "shared" || typeof state.storeRoot !== "string" || !path.isAbsolute(state.storeRoot)) throw new Error("zyrk Turbopack requires a ready shared installation; run zyrk install.");
35
+ const lock = JSON.parse(fs.readFileSync(path.join(project, "package-lock.json"), "utf8"));
36
+ if (crypto.createHash("sha512").update(canonical(lock)).digest("hex") !== state.lockDigest) throw new Error("zyrk Turbopack installation and lock disagree; run zyrk install.");
37
+ const next = fs.realpathSync(path.join(project, "node_modules/next"));
38
+ const version = JSON.parse(fs.readFileSync(path.join(next, "package.json"), "utf8")).version;
39
+ if (typeof version !== "string" || !/^16\\.(?:[4-9]|[1-9][0-9]+)\\.[0-9]+$/.test(version)) throw new Error("zyrk Turbopack additional roots require stable Next.js 16.4.x through 16.x.");
40
+ const store = fs.realpathSync(path.join(state.storeRoot, "realizations"));
41
+ const direct = Array.isArray(state.direct) && state.direct.find(edge => edge.name === "next");
42
+ if (!direct || fs.realpathSync(direct.target) !== next || !inside(store, next) || inside(project, store) || inside(store, project)) throw new Error("zyrk Turbopack requires the recorded external shared dependency projection.");
43
+ const resolved = await (typeof configuration === "function" ? configuration(phase, context) : configuration);
44
+ if (!resolved || typeof resolved !== "object" || Array.isArray(resolved)) throw new Error("The original Next configuration must return an object.");
45
+ const roots = resolved.experimental?.turbopackAdditionalRoots || {};
46
+ if (roots.zyrkStore && fs.realpathSync(roots.zyrkStore.path) !== store) throw new Error("The existing zyrkStore Turbopack root conflicts with the installed store.");
47
+ return { ...resolved, turbopack: { ...resolved.turbopack, root: project }, experimental: { ...resolved.experimental, turbopackAdditionalRoots: { ...roots, zyrkStore: { path: store } } } };
48
+ };
49
+ `;
50
+ function denied(message) {
51
+ return new ZyrkError("ZYRK_USAGE", message, {
52
+ remedy: "Run zyrk next --turbopack in a ready shared Next.js 16.4+ project; restore or review existing configuration files before retrying.",
53
+ });
54
+ }
55
+ async function regular(file) {
56
+ const info = await lstat(file);
57
+ if (!info.isFile() || info.isSymbolicLink())
58
+ throw denied("Next configuration editing requires regular files.");
59
+ }
60
+ function wrapper(name, esm) {
61
+ const original = name.replace(/\.(ts|mjs|js)$/, ".zyrk-original.$1");
62
+ if (!esm)
63
+ return `${MARKER}module.exports = require("./${HELPER}")(require("./${original}"), __dirname);\n`;
64
+ const importedOriginal = name.endsWith(".ts")
65
+ ? original.slice(0, -3)
66
+ : original;
67
+ const directory = name.endsWith(".mjs") || name.endsWith(".js")
68
+ ? 'fileURLToPath(new URL(".", import.meta.url))'
69
+ : "__dirname";
70
+ return `${MARKER}${directory === "__dirname" ? "" : 'import { fileURLToPath } from "node:url";\n'}import original from "./${importedOriginal}";\nimport withZyrkTurbopack from "./${HELPER}";\n\nexport default withZyrkTurbopack(original, ${directory});\n`;
71
+ }
72
+ export async function runNextCommand(options) {
73
+ if (process.platform === "win32")
74
+ throw new ZyrkError("ZYRK_PLATFORM_UNSUPPORTED", "Next Turbopack configuration is not verified on Windows.", {
75
+ remedy: "Use a platform with a verified zyrk store; Windows support remains gated.",
76
+ });
77
+ if (options.args.length !== 1 || options.args[0] !== "--turbopack")
78
+ throw denied("Next configuration editing requires the explicit --turbopack option.");
79
+ const boundary = await findProjectBoundary(options.cwd);
80
+ if (!boundary)
81
+ throw denied("Cannot find a Next project manifest.");
82
+ const project = await realpath(boundary);
83
+ const control = await lstat(path.join(project, ".zyrk-local"));
84
+ if (!control.isDirectory() || control.isSymbolicLink())
85
+ throw denied("Next project control directory is unsafe.");
86
+ const held = await acquireLock(path.join(project, ".zyrk-local/project.lock"), { name: "Next Turbopack configuration" });
87
+ try {
88
+ const state = JSON.parse(await readFile(path.join(project, "node_modules/.zyrk/install-state.json"), "utf8"));
89
+ const doc = validateLockDoc(JSON.parse(await readFile(path.join(project, "package-lock.json"), "utf8")), "project package-lock.json");
90
+ if (state.schemaVersion !== 1 ||
91
+ state.readiness !== "ready" ||
92
+ state.linker !== "shared" ||
93
+ state.lockDigest !== sha512Hex(canonicalJson(doc)))
94
+ throw denied("Next configuration requires a ready shared installation matching its lock.");
95
+ const manifest = JSON.parse(await readFile(path.join(project, "package.json"), "utf8"));
96
+ if (doc.zyrk?.manifest !== manifestFingerprint(manifest))
97
+ throw denied("The Next project manifest differs from its installed lock.");
98
+ const resolved = lockedGraph(doc);
99
+ const selected = selectPlatformGraph({
100
+ ...resolved,
101
+ roots: resolved.roots.filter((root) => Array.isArray(state.direct) &&
102
+ state.direct.some((edge) => edge.name === root.name)),
103
+ }, new Set(Object.keys(doc.packages?.[""]?.optionalDependencies ?? {})));
104
+ const next = selected.nodes.find((node) => node.name === "next" &&
105
+ selected.roots.some((root) => root.name === "next" && root.target === node.key));
106
+ if (!next || !satisfies(next.version, ">=16.4.0 <17.0.0"))
107
+ throw denied("The additional-root adapter requires stable Next.js 16.4.x through 16.x.");
108
+ const store = await Store.open({
109
+ root: resolveStoreRoot({ env: options.env }).root,
110
+ });
111
+ store.requireInitialized();
112
+ if (state.storeRoot !== store.root)
113
+ throw denied("The installed Next store differs from the selected user store.");
114
+ const storeRealizations = await realpath(store.paths.realizationsDir);
115
+ const contains = (root, file) => {
116
+ const relative = path.relative(root, file);
117
+ return (relative === "" ||
118
+ (!relative.startsWith(".." + path.sep) &&
119
+ relative !== ".." &&
120
+ !path.isAbsolute(relative)));
121
+ };
122
+ if (contains(project, storeRealizations) ||
123
+ contains(storeRealizations, project))
124
+ throw denied("The Turbopack adapter requires shared realizations outside the project root.");
125
+ const graph = createGraphDoc(selected.nodes.map((node) => ({ ...node, deps: node.edges })));
126
+ const directories = selectRealizationDirs(store, graph);
127
+ await verifyRealization({
128
+ store,
129
+ graph,
130
+ rootKey: next.key,
131
+ contextId: contextIdFor(graph, next.key),
132
+ realizationDirs: directories,
133
+ });
134
+ if ((await realpath(path.join(project, "node_modules/next"))) !==
135
+ (await realpath(packageDirIn(directories.get(next.key), "next"))))
136
+ throw denied("The installed Next link differs from its verified context.");
137
+ const names = (await readdir(project)).filter((name) => ["next.config.ts", "next.config.js", "next.config.mjs"].includes(name));
138
+ if (names.length !== 1)
139
+ throw denied("Select a project with exactly one supported Next configuration file.");
140
+ const name = names[0];
141
+ const original = name.replace(/\.(ts|mjs|js)$/, ".zyrk-original.$1");
142
+ const target = path.join(project, name);
143
+ await regular(target);
144
+ const contents = await readFile(target, "utf8");
145
+ const generated = wrapper(name, name !== "next.config.js" || manifest.type === "module");
146
+ if (contents === generated) {
147
+ for (const file of [original, HELPER, DECLARATION])
148
+ await regular(path.join(project, file));
149
+ if ((await readFile(path.join(project, HELPER), "utf8")) !== RUNTIME ||
150
+ (await readFile(path.join(project, DECLARATION), "utf8")) !== TYPES)
151
+ throw denied("The existing Turbopack adapter was changed; review it before retrying.");
152
+ return { changed: false, config: name, backup: original };
153
+ }
154
+ const created = [];
155
+ let configWriteAttempted = false;
156
+ try {
157
+ await copyFile(target, path.join(project, original), constants.COPYFILE_EXCL);
158
+ created.push(original);
159
+ await chmod(path.join(project, original), 0o444);
160
+ for (const [file, text] of [
161
+ [HELPER, RUNTIME],
162
+ [DECLARATION, TYPES],
163
+ ]) {
164
+ await writeFile(path.join(project, file), text, { flag: "wx" });
165
+ created.push(file);
166
+ }
167
+ configWriteAttempted = true;
168
+ await writeFileAtomic(target, generated);
169
+ }
170
+ catch (cause) {
171
+ // The atomic write may have renamed successfully before a durability
172
+ // error. Retain its dependencies and original backup for recovery.
173
+ if (!configWriteAttempted)
174
+ for (const file of created.reverse())
175
+ await rm(path.join(project, file)).catch(() => { });
176
+ if (isErrno(cause, "EEXIST"))
177
+ throw denied("A Next configuration backup or adapter already exists; no existing files were overwritten.");
178
+ throw cause;
179
+ }
180
+ return { changed: true, config: name, backup: original };
181
+ }
182
+ finally {
183
+ await held.release();
184
+ }
185
+ }
@@ -0,0 +1,2 @@
1
+ export declare function adaptNextDisplay(script: string, version: string, skipInstall: boolean): Promise<void>;
2
+ //# sourceMappingURL=next-display.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next-display.d.ts","sourceRoot":"","sources":["../../src/dlx/next-display.ts"],"names":[],"mappings":"AAQA,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,OAAO,GACnB,OAAO,CAAC,IAAI,CAAC,CA8Cf"}
@@ -0,0 +1,36 @@
1
+ /** A display-only adaptation of verified create-next-app statement shapes.
2
+ * Official source: v16.4.2 create-app.ts and templates/index.ts. Installer
3
+ * selection and executable logic stay upstream; --skip-install keeps the
4
+ * native zyrk installation separate. Only the private initializer is edited.
5
+ */
6
+ import { lstat, readFile, writeFile } from "node:fs/promises";
7
+ import { ZyrkError } from "../errors.js";
8
+ export async function adaptNextDisplay(script, version, skipInstall) {
9
+ const file = await lstat(script);
10
+ if (!file.isFile() || file.nlink !== 1 || (file.mode & 0o200) === 0)
11
+ throw new ZyrkError("ZYRK_INTERNAL", "Cannot adapt the Next initializer without an independent writable script.", {
12
+ remedy: "Retry with a private initializer generation; never make shared store files writable.",
13
+ });
14
+ const original = await readFile(script, "utf8");
15
+ // Match complete neighboring statements, not arbitrary output strings. Minifier
16
+ // variable names can vary; the verified statement structure must match once.
17
+ const header = /console\.log\(`Creating a new Next\.js app in \$\{\(0,([A-Za-z_$][\w$]*)\.green\)\(([A-Za-z_$][\w$]*)\)\}\.`\);console\.log\(\);process\.chdir\(\2\);/g;
18
+ const label = /console\.log\(\(0,([A-Za-z_$][\w$]*)\.bold\)\(`Using \$\{[A-Za-z_$][\w$]*\}\.`\)\);(?=console\.log\("\\nInitializing project with template:",[A-Za-z_$][\w$]*,"\\n"\);)/g;
19
+ const headers = [...original.matchAll(header)];
20
+ const labels = [...original.matchAll(label)];
21
+ if (headers.length !== 1 ||
22
+ labels.length !== 1 ||
23
+ headers[0][1] !== labels[0][1])
24
+ throw new ZyrkError("ZYRK_RESOLUTION_FAILED", "Cannot adapt the selected Next initializer: its display contract is unsupported.", {
25
+ detail: { package: "create-next-app", version },
26
+ remedy: "Select create-next-app@16.4.2 or report this version so its upstream display contract can be verified.",
27
+ });
28
+ const message = skipInstall
29
+ ? "Scaffolding only; dependency installation skipped."
30
+ : "Using zyrk.";
31
+ const adapted = original
32
+ .replace(label, "")
33
+ .replace(header, (statement, color, target) => statement.slice(0, -`process.chdir(${target});`.length) +
34
+ `console.log((0,${color}.bold)(${JSON.stringify(message)}));console.log();process.chdir(${target});`);
35
+ await writeFile(script, adapted);
36
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/dlx/next.ts"],"names":[],"mappings":"AAQA,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExE,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAuC3E,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAEtE;AA4CD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,UAAU,GAAG;IAAE,iBAAiB,CAAC,EAAE,cAAc,CAAC,YAAY,CAAC,CAAA;CAAE,GACzE,OAAO,CAAC,SAAS,CAAC,CAkGpB"}
1
+ {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/dlx/next.ts"],"names":[],"mappings":"AASA,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExE,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAuC3E,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAEtE;AA4CD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,UAAU,GAAG;IAAE,iBAAiB,CAAC,EAAE,cAAc,CAAC,YAAY,CAAC,CAAA;CAAE,GACzE,OAAO,CAAC,SAAS,CAAC,CAoHpB"}
package/dist/dlx/next.js CHANGED
@@ -5,6 +5,7 @@
5
5
  import { lstat, readFile, readdir } from "node:fs/promises";
6
6
  import * as path from "node:path";
7
7
  import { satisfies } from "semver";
8
+ import { adaptNextDisplay } from "./next-display.js";
8
9
  import { ZyrkError } from "../errors.js";
9
10
  import { installProject } from "../install/run.js";
10
11
  import { isErrno } from "../util/fsx.js";
@@ -106,6 +107,11 @@ export async function runNextInitializer(options) {
106
107
  throw failed("Next initializer target already has a project manifest.");
107
108
  const entries = explicit === undefined ? new Set(await readdir(cwd)) : undefined;
108
109
  const skipInstall = flags.has("--skip-install");
110
+ if (!skipInstall &&
111
+ [...flags].some((flag) => ["--use-npm", "--use-pnpm", "--use-yarn", "--use-bun"].includes(flag)))
112
+ throw new ZyrkError("ZYRK_USAGE", "Cannot select another dependency installer for zyrk create next-app.", {
113
+ remedy: "Remove --use-npm/--use-pnpm/--use-yarn/--use-bun to install with zyrk, or pass --skip-install to scaffold only.",
114
+ });
109
115
  // Insert before -- so it remains an option when the user supplies a literal target.
110
116
  const forwarded = [...args];
111
117
  if (!skipInstall) {
@@ -123,7 +129,7 @@ export async function runNextInitializer(options) {
123
129
  remedy: "Select create-next-app 15.x or 16.x; the native create adapter requires the verified --skip-install contract.",
124
130
  });
125
131
  },
126
- });
132
+ }, async ({ script, version }) => adaptNextDisplay(script, version, skipInstall));
127
133
  if (result.exitCode !== 0)
128
134
  return result;
129
135
  let directory = explicit;
package/dist/dlx/run.d.ts CHANGED
@@ -57,5 +57,9 @@ export declare function splitDlxSpec(spec: string): {
57
57
  export declare function runDlx(options: DlxOptions): Promise<DlxResult>;
58
58
  /** Initializers copy and rewrite their shipped templates, so their full
59
59
  * closure must use independent writable files (I-001, I-002, F-017). */
60
- export declare function runInitializer(options: DlxOptions): Promise<DlxResult>;
60
+ export declare function runInitializer(options: DlxOptions, prepare?: (tool: {
61
+ script: string;
62
+ name: string;
63
+ version: string;
64
+ }) => Promise<void>): Promise<DlxResult>;
61
65
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/dlx/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAgBhD,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,CAqBA;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAEpE;AAED;wEACwE;AACxE,wBAAsB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAE5E"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/dlx/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAgBhD,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,CAqBA;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAEpE;AAED;wEACwE;AACxE,wBAAsB,cAAc,CAClC,OAAO,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,KAAK,OAAO,CAAC,IAAI,CAAC,GAClB,OAAO,CAAC,SAAS,CAAC,CAEpB"}
package/dist/dlx/run.js CHANGED
@@ -59,10 +59,10 @@ export async function runDlx(options) {
59
59
  }
60
60
  /** Initializers copy and rewrite their shipped templates, so their full
61
61
  * closure must use independent writable files (I-001, I-002, F-017). */
62
- export async function runInitializer(options) {
63
- return runTemporaryTool(options, true);
62
+ export async function runInitializer(options, prepare) {
63
+ return runTemporaryTool(options, true, prepare);
64
64
  }
65
- async function runTemporaryTool(options, writable) {
65
+ async function runTemporaryTool(options, writable, prepare) {
66
66
  const { store, registry } = options;
67
67
  const args = options.args ?? [];
68
68
  const cwd = options.cwd ?? process.cwd();
@@ -148,6 +148,7 @@ async function runTemporaryTool(options, writable) {
148
148
  if (binTarget === undefined)
149
149
  throw internal(`selected bin ${binKey} vanished`);
150
150
  const script = await resolveBinScript(toolPkgDir, tool, binKey, binTarget);
151
+ await prepare?.({ script, name: tool.name, version: tool.version });
151
152
  const bins = {};
152
153
  for (const [name, target] of Object.entries(tool.bin)) {
153
154
  bins[name] = await resolveBinScript(toolPkgDir, tool, name, target);
@@ -0,0 +1,14 @@
1
+ import type { InstallOptions } from "./run.ts";
2
+ type Package = {
3
+ name: string;
4
+ version: string;
5
+ };
6
+ /** Counts verified source identities, independent of peer-split realizations. */
7
+ export declare function sourceProgress(onProgress: InstallOptions["onProgress"], total: number): {
8
+ downloading(pkg: Package): (chunk: number) => void;
9
+ verified(pkg: Package, status: "reused" | "downloaded"): void;
10
+ };
11
+ /** Only called after transactional projection publication succeeds. */
12
+ export declare function reportLinked(onProgress: InstallOptions["onProgress"], packages: readonly Package[]): void;
13
+ export {};
14
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/install/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,UAAU,CAAC;AAEhE,KAAK,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AACjD,iFAAiF;AACjF,wBAAgB,cAAc,CAC5B,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,EACxC,KAAK,EAAE,MAAM;IAcX,WAAW,MAAM,OAAO,WAQP,MAAM;IAUvB,QAAQ,MAAM,OAAO,UAAU,QAAQ,GAAG,YAAY;EAMzD;AACD,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,EACxC,QAAQ,EAAE,SAAS,OAAO,EAAE,QAU7B"}
@@ -0,0 +1,48 @@
1
+ /** Counts verified source identities, independent of peer-split realizations. */
2
+ export function sourceProgress(onProgress, total) {
3
+ let reused = 0, downloaded = 0;
4
+ const emit = (pkg) => onProgress?.({
5
+ phase: "fetching",
6
+ total,
7
+ completed: reused + downloaded,
8
+ reused,
9
+ downloaded,
10
+ ...(pkg ? { package: pkg } : {}),
11
+ });
12
+ return {
13
+ downloading(pkg) {
14
+ let bytes = 0;
15
+ emit({
16
+ name: pkg.name,
17
+ version: pkg.version,
18
+ status: "downloading",
19
+ bytes,
20
+ });
21
+ return (chunk) => {
22
+ bytes += chunk;
23
+ emit({
24
+ name: pkg.name,
25
+ version: pkg.version,
26
+ status: "downloading",
27
+ bytes,
28
+ });
29
+ };
30
+ },
31
+ verified(pkg, status) {
32
+ if (status === "reused")
33
+ reused++;
34
+ else
35
+ downloaded++;
36
+ emit({ name: pkg.name, version: pkg.version, status });
37
+ },
38
+ };
39
+ }
40
+ /** Only called after transactional projection publication succeeds. */
41
+ export function reportLinked(onProgress, packages) {
42
+ const unique = new Map(packages.map((pkg) => [JSON.stringify([pkg.name, pkg.version]), pkg]));
43
+ for (const pkg of unique.values())
44
+ onProgress?.({
45
+ phase: "complete",
46
+ package: { name: pkg.name, version: pkg.version, status: "linked" },
47
+ });
48
+ }
@@ -5,9 +5,17 @@ import type { ResolverLimits } from "../resolve/resolver.ts";
5
5
  import type { ExtractionLimits } from "../extract/tarball.ts";
6
6
  import { Store } from "../store/layout.ts";
7
7
  export type InstallProgress = {
8
- phase: "planning" | "resolving" | "fetching" | "realizing" | "building" | "linking";
8
+ phase: "planning" | "resolving" | "fetching" | "realizing" | "building" | "linking" | "complete";
9
9
  completed?: number;
10
10
  total?: number;
11
+ reused?: number;
12
+ downloaded?: number;
13
+ package?: {
14
+ name: string;
15
+ version: string;
16
+ status: "downloading" | "downloaded" | "reused" | "linked";
17
+ bytes?: number;
18
+ };
11
19
  };
12
20
  export type InstallOptions = {
13
21
  onProgress?: (event: InstallProgress) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/install/run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAK9D,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAYtB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AA+B3C,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EACD,UAAU,GACV,WAAW,GACX,UAAU,GACV,WAAW,GACX,UAAU,GACV,SAAS,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AACF,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC1C,WAAW,CAAC,EAAE;QACZ,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH,CAAC;AACF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EACL,OAAO,GACP,uBAAuB,GACvB,eAAe,GACf,SAAS,GACT,cAAc,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;CAC3B,CAAC;AA4GF,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAoyBxB"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/install/run.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { sourceProgress, reportLinked } from "./progress.js";
1
2
  import { selectPlatformGraph } from "./platform.js";
2
3
  import { findWorkspaceRoot } from "./workspaces.js";
3
4
  import { assertSupportedSourcePayload } from "./source-policy.js";
@@ -441,7 +442,7 @@ export async function installProject(options) {
441
442
  })
442
443
  : undefined;
443
444
  const unique = new Map(activeNodes.map((n) => [n.sourceId, n]));
444
- let completedSources = 0;
445
+ const progress = sourceProgress(options.onProgress, unique.size);
445
446
  options.onProgress?.({
446
447
  phase: "fetching",
447
448
  completed: 0,
@@ -451,21 +452,21 @@ export async function installProject(options) {
451
452
  operation.operation.checkpoint();
452
453
  if (await pathExists(sourceManifestPath(options.store, node.sourceId))) {
453
454
  await readSourceManifest(options.store, node.sourceId);
454
- options.onProgress?.({
455
- phase: "fetching",
456
- completed: ++completedSources,
457
- total: unique.size,
458
- });
455
+ progress.verified(node, "reused");
459
456
  continue;
460
457
  }
461
458
  if (options.offline)
462
459
  throw error("ZYRK_OFFLINE_MISSING", `Verified source for ${node.name}@${node.version} is missing from the store.`);
463
460
  const tarball = path.join(txn.dir, `${node.sourceId}.tgz`);
461
+ const downloadProgress = progress.downloading(node);
464
462
  const download = await options.registry
465
463
  .forPackage(node.name)
466
464
  .downloadTarball({
467
465
  signal: operation.operation.signal,
468
- onBytes: operation.operation.consumeArchiveBytes,
466
+ onBytes(bytes) {
467
+ operation.operation.consumeArchiveBytes(bytes);
468
+ downloadProgress(bytes);
469
+ },
469
470
  url: node.tarballUrl,
470
471
  expectedIntegrity: node.integrity,
471
472
  destPath: tarball,
@@ -490,11 +491,7 @@ export async function installProject(options) {
490
491
  if (source.sourceId !== node.sourceId)
491
492
  throw lockMismatch("download identity disagrees with the locked source");
492
493
  await rm(tarball, { force: true });
493
- options.onProgress?.({
494
- phase: "fetching",
495
- completed: ++completedSources,
496
- total: unique.size,
497
- });
494
+ progress.verified(node, "downloaded");
498
495
  }
499
496
  for (const node of unique.values())
500
497
  assertSupportedSourcePayload(node, await readSourceManifest(options.store, node.sourceId));
@@ -707,6 +704,7 @@ export async function installProject(options) {
707
704
  });
708
705
  registration.activate();
709
706
  await txn.setState("committed");
707
+ reportLinked(options.onProgress, activeNodes);
710
708
  return {
711
709
  readiness,
712
710
  packages: activeNodes.length,
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-install.d.ts","sourceRoot":"","sources":["../../src/install/workspace-install.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuH9D,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAmrBxB"}
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,3 +1,4 @@
1
+ import { sourceProgress, reportLinked } from "./progress.js";
1
2
  import { selectWorkspacePlatform } from "./platform.js";
2
3
  /** Multi-importer live workspace installation with one publication journal. */
3
4
  import { chmod, copyFile, lstat, mkdir, readFile, realpath, rename, rm, symlink, } from "node:fs/promises";
@@ -264,7 +265,7 @@ export async function installWorkspaceProject(options) {
264
265
  })
265
266
  : undefined;
266
267
  const uniqueSources = new Map(nodes.map((n) => [n.sourceId, n]));
267
- let completedSources = 0;
268
+ const progress = sourceProgress(options.onProgress, uniqueSources.size);
268
269
  options.onProgress?.({
269
270
  phase: "fetching",
270
271
  completed: 0,
@@ -272,11 +273,13 @@ export async function installWorkspaceProject(options) {
272
273
  });
273
274
  for (const node of uniqueSources.values()) {
274
275
  operation.operation.checkpoint();
275
- if (!(await pathExists(sourceManifestPath(options.store, node.sourceId)))) {
276
+ const cached = await pathExists(sourceManifestPath(options.store, node.sourceId));
277
+ if (!cached) {
276
278
  if (options.offline)
277
279
  throw new ZyrkError("ZYRK_OFFLINE_MISSING", `Verified workspace dependency ${node.name}@${node.version} is missing.`, {
278
280
  remedy: "Fetch the exact locked sources online before retrying offline.",
279
281
  });
282
+ const downloadProgress = progress.downloading(node);
280
283
  const archive = path.join(txn.dir, `${node.sourceId}.tgz`), download = await options.registry
281
284
  .forPackage(node.name)
282
285
  .downloadTarball({
@@ -285,7 +288,10 @@ export async function installWorkspaceProject(options) {
285
288
  destPath: archive,
286
289
  label: `${node.name}@${node.version}`,
287
290
  signal: operation.operation.signal,
288
- onBytes: operation.operation.consumeArchiveBytes,
291
+ onBytes(bytes) {
292
+ operation.operation.consumeArchiveBytes(bytes);
293
+ downloadProgress(bytes);
294
+ },
289
295
  });
290
296
  const extraction = await extractTarball({
291
297
  limits: options.extractBudget,
@@ -308,11 +314,7 @@ export async function installWorkspaceProject(options) {
308
314
  await rm(archive, { force: true });
309
315
  }
310
316
  assertSupportedSourcePayload(node, await readSourceManifest(options.store, node.sourceId));
311
- options.onProgress?.({
312
- phase: "fetching",
313
- completed: ++completedSources,
314
- total: uniqueSources.size,
315
- });
317
+ progress.verified(node, cached ? "reused" : "downloaded");
316
318
  }
317
319
  if (options.fetchOnly)
318
320
  return {
@@ -562,6 +564,7 @@ export async function installWorkspaceProject(options) {
562
564
  throw lockMismatch("workspace manifest changed during install");
563
565
  await publishWorkspaceProjection(root, generationId, project.importers.map((i) => i.id), lockText, manifestChanges);
564
566
  registration.activate();
567
+ reportLinked(options.onProgress, nodes);
565
568
  return {
566
569
  readiness,
567
570
  packages: nodes.length,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyrk",
3
- "version": "0.6.2",
3
+ "version": "0.6.6",
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",