zyrk 0.8.4 → 0.8.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
@@ -191,6 +191,12 @@ paths. Older platform-pruned locks need an intentional non-frozen refresh; incom
191
191
  npm locks must be freshly resolved before portable adoption. macOS, Windows, and
192
192
  broad framework compatibility have not passed their release gates.
193
193
 
194
+ Zyrk writes the npm-compatible lock layout alongside its portable graph. Peer
195
+ dependencies are placed beside or above their consumers, as npm requires. If
196
+ an older Zyrk lock makes `npm ci` report missing peer packages, run
197
+ `zyrk install --lockfile-only` with the updated CLI to refresh that layout
198
+ without installing dependencies or running lifecycle scripts.
199
+
194
200
  ### Workspaces
195
201
 
196
202
  Standard `package.json` workspaces use one root lock and a recoverable transaction
@@ -528,3 +534,12 @@ and runs real pinned React, Vite, Vitest, and Tailwind builds and HTTP asset
528
534
  checks. It uses native zyrk installation with default deny policy. Recorded
529
535
  results describe the exact tested versions and platform, without a universal
530
536
  framework or cold-install performance claim.
537
+
538
+ ### Inspection output
539
+
540
+ Interactive `doctor`, store status/verification, and collection commands use
541
+ command-specific summaries. A failed doctor result is marked as a failure;
542
+ an unavailable audit result is attention, never a clean audit. Store collection
543
+ previews distinguish eligible, retained, removed, and grace-period entries and
544
+ keep physical reclamation explicitly unavailable when it cannot be measured.
545
+ Redirected output and `--json` preserve their existing data contracts.
package/VERSION CHANGED
@@ -1 +1 @@
1
- v0.8.4
1
+ v0.8.6
@@ -2,6 +2,15 @@ export type TerminalStyle = {
2
2
  isTTY?: boolean;
3
3
  color?: boolean;
4
4
  };
5
+ export type HumanSummary = {
6
+ tone: "success" | "attention" | "failure" | "status";
7
+ heading: string;
8
+ facts: string[];
9
+ next?: string;
10
+ };
11
+ export declare function renderSummary(summary: HumanSummary, style: TerminalStyle): string;
12
+ /** Command facts stay separate from their terminal presentation and JSON data. */
13
+ export declare function commandSummary(command: string, value: Record<string, unknown>): HumanSummary | undefined;
5
14
  /** Render command summaries for people while preserving redirected output. */
6
15
  export declare function renderCommandOutput(command: string, output: string, style?: TerminalStyle): string;
7
16
  /** Make stable CLI diagnostics easier to scan without changing their content. */
@@ -1 +1 @@
1
- {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/cli/terminal.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAQjE,8EAA8E;AAC9E,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,aAAkB,GACxB,MAAM,CA0BR;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,aAAkB,GACxB,MAAM,CAUR"}
1
+ {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/cli/terminal.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,aAAa,CAC3B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,aAAa,GACnB,MAAM,CAQR;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,YAAY,GAAG,SAAS,CAgG1B;AAQD,8EAA8E;AAC9E,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,aAAkB,GACxB,MAAM,CA4BR;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,aAAkB,GACxB,MAAM,CAUR"}
@@ -1,3 +1,94 @@
1
+ export function renderSummary(summary, style) {
2
+ const marker = { success: "✓", attention: "!", failure: "✖", status: "›" }[summary.tone];
3
+ const color = { success: 32, attention: 33, failure: 31, status: 2 }[summary.tone];
4
+ return `${paint(marker, color, style)} ${summary.heading}\n${summary.facts.map((fact) => ` ${fact}\n`).join("")}${summary.next ? ` Next: ${summary.next}\n` : ""}`;
5
+ }
6
+ /** Command facts stay separate from their terminal presentation and JSON data. */
7
+ export function commandSummary(command, value) {
8
+ if (command === "doctor" &&
9
+ typeof value.ok === "boolean" &&
10
+ Array.isArray(value.diagnostics)) {
11
+ const diagnostics = value.diagnostics;
12
+ return {
13
+ tone: !value.ok
14
+ ? "failure"
15
+ : diagnostics.length
16
+ ? "attention"
17
+ : "success",
18
+ heading: !value.ok
19
+ ? "Project needs attention"
20
+ : diagnostics.length
21
+ ? "Project checks passed with warnings"
22
+ : "Project checks passed",
23
+ facts: diagnostics.length
24
+ ? diagnostics.map((d) => `${d.severity}: ${d.code}: ${d.message}`)
25
+ : ["Lock, links, build readiness, and store safety checked."],
26
+ ...(!value.ok
27
+ ? {
28
+ next: "Review these diagnostics before choosing install or repair.",
29
+ }
30
+ : {}),
31
+ };
32
+ }
33
+ if (command.startsWith("store") && typeof value.initialized === "boolean") {
34
+ return {
35
+ tone: "status",
36
+ heading: value.initialized
37
+ ? "Shared store status"
38
+ : "Shared store is not initialized",
39
+ facts: value.initialized
40
+ ? [
41
+ `Retention roots: ${value.retentionRoots ?? "unavailable"}`,
42
+ `Logical bytes: ${value.logicalBytes ?? "unavailable"}`,
43
+ `Allocated bytes estimate: ${value.allocatedBytesEstimate ?? "unavailable"}`,
44
+ String(value.allocationMethod ??
45
+ "Physical allocation measurement unavailable."),
46
+ "Private builds, fetch caches, and exclusive reclaimable bytes are not measured here.",
47
+ ]
48
+ : ["No store content has been inspected."],
49
+ };
50
+ }
51
+ if (command.startsWith("store") && value.verified === true)
52
+ return {
53
+ tone: "success",
54
+ heading: "Shared store verification passed",
55
+ facts: [
56
+ `Sources: ${value.sources}; objects: ${value.objects}; realizations: ${value.realizations}`,
57
+ ],
58
+ };
59
+ if (command.startsWith("store") &&
60
+ typeof value.dryRun === "boolean" &&
61
+ Array.isArray(value.candidates))
62
+ return {
63
+ tone: "status",
64
+ heading: value.dryRun
65
+ ? "Store collection preview"
66
+ : "Store collection complete",
67
+ facts: [
68
+ `Eligible entries: ${value.candidates.length}; removed: ${value.removed}; retained: ${value.retained}`,
69
+ `Within grace period: ${value.graceRetained}`,
70
+ `Selected logical bytes: ${value.logicalBytesSelected}; physical bytes reclaimed: ${value.physicalBytesReclaimed}`,
71
+ ...(Array.isArray(value.retentionReasons)
72
+ ? value.retentionReasons.map(String)
73
+ : []),
74
+ ],
75
+ ...(value.dryRun
76
+ ? { next: "Review eligibility; run zyrk store gc to apply collection." }
77
+ : {}),
78
+ };
79
+ if (value.status === "unavailable")
80
+ return {
81
+ tone: "attention",
82
+ heading: `${command} is unavailable`,
83
+ facts: [
84
+ "No clean result can be established.",
85
+ ...Object.entries(value)
86
+ .filter(([key]) => !["schemaVersion", "status", "command"].includes(key))
87
+ .map(([key, item]) => `${key}: ${JSON.stringify(item)}`),
88
+ ],
89
+ };
90
+ return undefined;
91
+ }
1
92
  function paint(value, code, style) {
2
93
  return style.isTTY && style.color !== false
3
94
  ? `\u001b[${code}m${value}\u001b[0m`
@@ -15,6 +106,9 @@ export function renderCommandOutput(command, output, style = {}) {
15
106
  value = undefined;
16
107
  }
17
108
  if (value && typeof value === "object" && !Array.isArray(value)) {
109
+ const semantic = commandSummary(command, value);
110
+ if (semantic)
111
+ return renderSummary(semantic, style);
18
112
  const summary = { ...value };
19
113
  delete summary.schemaVersion;
20
114
  delete summary.status;
@@ -1 +1 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/commands/project.ts"],"names":[],"mappings":"AA6DA,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAqMF,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CAgRjB"}
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/commands/project.ts"],"names":[],"mappings":"AA6DA,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA2MF,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CAgRjB"}
@@ -138,7 +138,9 @@ function emit(options, result, human) {
138
138
  command: options.command,
139
139
  ...result,
140
140
  }) + "\n"
141
- : renderCommandOutput(options.command, `${human}\n`, {
141
+ : renderCommandOutput(options.command, options.command === "doctor" && options.stdout.isTTY
142
+ ? JSON.stringify(result)
143
+ : `${human}\n`, {
142
144
  ...(options.stdout.isTTY === undefined
143
145
  ? {}
144
146
  : { isTTY: options.stdout.isTTY }),
@@ -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,CAqPrB"}
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,CAiQrB"}
@@ -123,13 +123,19 @@ export async function writeLockfile(options) {
123
123
  ...node.platform,
124
124
  ...(node.engines === undefined ? {} : { engines: node.engines }),
125
125
  };
126
- for (const edge of node.edges) {
127
- const found = lookup(location, edge.name);
126
+ // Establish peers before descending into ordinary children. A peer must be
127
+ // visible beside or above its consumer; its own node_modules is invalid to
128
+ // npm even if that directory already contains the requested version.
129
+ const placementEdges = [...node.edges].sort((a, b) => Number(b.kind === "peer") - Number(a.kind === "peer"));
130
+ for (const edge of placementEdges) {
131
+ const base = edge.kind === "peer"
132
+ ? location.slice(0, Math.max(0, location.lastIndexOf("/node_modules/")))
133
+ : location;
134
+ const found = lookup(base, edge.name);
128
135
  if (found !== undefined && placements.get(found) === edge.target)
129
136
  continue;
130
137
  if (edge.kind === "peer") {
131
- const parent = location.slice(0, Math.max(0, location.lastIndexOf("node_modules/")));
132
- populate(`${parent}node_modules/${edge.name}`, edge.target);
138
+ populate(`${base ? `${base}/` : ""}node_modules/${edge.name}`, edge.target);
133
139
  }
134
140
  else {
135
141
  populate(`${location}/node_modules/${edge.name}`, edge.target);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyrk",
3
- "version": "0.8.4",
3
+ "version": "0.8.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",