zyrk 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/VERSION +1 -1
  2. package/bin/zyrk.js +1 -1
  3. package/dist/cli/main.d.ts +54 -0
  4. package/dist/cli/main.d.ts.map +1 -0
  5. package/dist/cli/main.js +473 -0
  6. package/dist/cli/npm-delegate.d.ts +38 -0
  7. package/dist/cli/npm-delegate.d.ts.map +1 -0
  8. package/{src/cli/npm-delegate.ts → dist/cli/npm-delegate.js} +26 -55
  9. package/dist/cli/parse.d.ts +40 -0
  10. package/dist/cli/parse.d.ts.map +1 -0
  11. package/dist/cli/parse.js +78 -0
  12. package/dist/commands/dependencies.d.ts +12 -0
  13. package/dist/commands/dependencies.d.ts.map +1 -0
  14. package/dist/commands/dependencies.js +118 -0
  15. package/dist/commands/deploy.d.ts +15 -0
  16. package/dist/commands/deploy.d.ts.map +1 -0
  17. package/dist/commands/deploy.js +506 -0
  18. package/dist/commands/maintenance.d.ts +27 -0
  19. package/dist/commands/maintenance.d.ts.map +1 -0
  20. package/dist/commands/maintenance.js +198 -0
  21. package/dist/commands/patch.d.ts +38 -0
  22. package/dist/commands/patch.d.ts.map +1 -0
  23. package/dist/commands/patch.js +336 -0
  24. package/dist/commands/project.d.ts +15 -0
  25. package/dist/commands/project.d.ts.map +1 -0
  26. package/dist/commands/project.js +396 -0
  27. package/dist/commands/store.d.ts +10 -0
  28. package/dist/commands/store.d.ts.map +1 -0
  29. package/dist/commands/store.js +551 -0
  30. package/dist/config/paths.d.ts +37 -0
  31. package/dist/config/paths.d.ts.map +1 -0
  32. package/dist/config/paths.js +66 -0
  33. package/dist/dlx/create.d.ts +23 -0
  34. package/dist/dlx/create.d.ts.map +1 -0
  35. package/dist/dlx/create.js +59 -0
  36. package/dist/dlx/run.d.ts +54 -0
  37. package/dist/dlx/run.d.ts.map +1 -0
  38. package/dist/dlx/run.js +274 -0
  39. package/dist/errors.d.ts +68 -0
  40. package/dist/errors.d.ts.map +1 -0
  41. package/dist/errors.js +111 -0
  42. package/dist/extract/tarball.d.ts +82 -0
  43. package/dist/extract/tarball.d.ts.map +1 -0
  44. package/dist/extract/tarball.js +645 -0
  45. package/dist/fetch/all.d.ts +48 -0
  46. package/dist/fetch/all.d.ts.map +1 -0
  47. package/dist/fetch/all.js +127 -0
  48. package/dist/fetch/package.d.ts +45 -0
  49. package/dist/fetch/package.d.ts.map +1 -0
  50. package/dist/fetch/package.js +124 -0
  51. package/dist/fetch/registry.d.ts +109 -0
  52. package/dist/fetch/registry.d.ts.map +1 -0
  53. package/dist/fetch/registry.js +418 -0
  54. package/dist/install/import-npm.d.ts +4 -0
  55. package/dist/install/import-npm.d.ts.map +1 -0
  56. package/dist/install/import-npm.js +147 -0
  57. package/dist/install/lock.d.ts +8 -0
  58. package/dist/install/lock.d.ts.map +1 -0
  59. package/dist/install/lock.js +146 -0
  60. package/dist/install/metadata.d.ts +19 -0
  61. package/dist/install/metadata.d.ts.map +1 -0
  62. package/dist/install/metadata.js +79 -0
  63. package/dist/install/registry.d.ts +8 -0
  64. package/dist/install/registry.d.ts.map +1 -0
  65. package/dist/install/registry.js +59 -0
  66. package/dist/install/run.d.ts +32 -0
  67. package/dist/install/run.d.ts.map +1 -0
  68. package/dist/install/run.js +545 -0
  69. package/dist/lockfile/fingerprints.d.ts +35 -0
  70. package/dist/lockfile/fingerprints.d.ts.map +1 -0
  71. package/dist/lockfile/fingerprints.js +56 -0
  72. package/dist/lockfile/read.d.ts +27 -0
  73. package/dist/lockfile/read.d.ts.map +1 -0
  74. package/dist/lockfile/read.js +126 -0
  75. package/dist/lockfile/types.d.ts +74 -0
  76. package/dist/lockfile/types.d.ts.map +1 -0
  77. package/dist/lockfile/types.js +17 -0
  78. package/dist/lockfile/write.d.ts +39 -0
  79. package/dist/lockfile/write.d.ts.map +1 -0
  80. package/dist/lockfile/write.js +180 -0
  81. package/dist/log.d.ts +21 -0
  82. package/dist/log.d.ts.map +1 -0
  83. package/dist/log.js +79 -0
  84. package/dist/project/projection.d.ts +50 -0
  85. package/dist/project/projection.d.ts.map +1 -0
  86. package/dist/project/projection.js +276 -0
  87. package/dist/resolve/resolver.d.ts +75 -0
  88. package/dist/resolve/resolver.d.ts.map +1 -0
  89. package/dist/resolve/resolver.js +533 -0
  90. package/dist/resolve/spec.d.ts +26 -0
  91. package/dist/resolve/spec.d.ts.map +1 -0
  92. package/dist/resolve/spec.js +87 -0
  93. package/dist/store/db.d.ts +49 -0
  94. package/dist/store/db.d.ts.map +1 -0
  95. package/{src/store/db.ts → dist/store/db.js} +106 -206
  96. package/dist/store/graph.d.ts +60 -0
  97. package/dist/store/graph.d.ts.map +1 -0
  98. package/dist/store/graph.js +115 -0
  99. package/dist/store/layout.d.ts +71 -0
  100. package/dist/store/layout.d.ts.map +1 -0
  101. package/dist/store/layout.js +282 -0
  102. package/dist/store/manifests.d.ts +78 -0
  103. package/dist/store/manifests.d.ts.map +1 -0
  104. package/dist/store/manifests.js +238 -0
  105. package/dist/store/objects.d.ts +56 -0
  106. package/dist/store/objects.d.ts.map +1 -0
  107. package/dist/store/objects.js +157 -0
  108. package/dist/store/realize.d.ts +38 -0
  109. package/dist/store/realize.d.ts.map +1 -0
  110. package/dist/store/realize.js +227 -0
  111. package/dist/store/source.d.ts +42 -0
  112. package/dist/store/source.d.ts.map +1 -0
  113. package/dist/store/source.js +169 -0
  114. package/dist/store/txn.d.ts +37 -0
  115. package/dist/store/txn.d.ts.map +1 -0
  116. package/dist/store/txn.js +97 -0
  117. package/dist/store/verify.d.ts +40 -0
  118. package/dist/store/verify.d.ts.map +1 -0
  119. package/dist/store/verify.js +396 -0
  120. package/dist/util/fsx.d.ts +54 -0
  121. package/dist/util/fsx.d.ts.map +1 -0
  122. package/dist/util/fsx.js +259 -0
  123. package/dist/util/hash.d.ts +48 -0
  124. package/dist/util/hash.d.ts.map +1 -0
  125. package/dist/util/hash.js +80 -0
  126. package/dist/util/ids.d.ts +19 -0
  127. package/dist/util/ids.d.ts.map +1 -0
  128. package/dist/util/ids.js +38 -0
  129. package/dist/util/integrity.d.ts +97 -0
  130. package/dist/util/integrity.d.ts.map +1 -0
  131. package/dist/util/integrity.js +192 -0
  132. package/dist/util/json.d.ts +14 -0
  133. package/dist/util/json.d.ts.map +1 -0
  134. package/dist/util/json.js +78 -0
  135. package/dist/util/locks.d.ts +40 -0
  136. package/dist/util/locks.d.ts.map +1 -0
  137. package/dist/util/locks.js +186 -0
  138. package/dist/util/proc.d.ts +32 -0
  139. package/dist/util/proc.d.ts.map +1 -0
  140. package/dist/util/proc.js +84 -0
  141. package/dist/version.d.ts +27 -0
  142. package/dist/version.d.ts.map +1 -0
  143. package/{src/version.ts → dist/version.js} +0 -8
  144. package/package.json +5 -3
  145. package/src/cli/main.ts +0 -468
  146. package/src/cli/parse.ts +0 -106
  147. package/src/commands/dependencies.ts +0 -154
  148. package/src/commands/deploy.ts +0 -777
  149. package/src/commands/maintenance.ts +0 -260
  150. package/src/commands/patch.ts +0 -408
  151. package/src/commands/project.ts +0 -598
  152. package/src/commands/store.ts +0 -689
  153. package/src/config/paths.ts +0 -119
  154. package/src/dlx/create.ts +0 -69
  155. package/src/dlx/run.ts +0 -389
  156. package/src/errors.ts +0 -142
  157. package/src/extract/tarball.ts +0 -868
  158. package/src/fetch/all.ts +0 -206
  159. package/src/fetch/package.ts +0 -181
  160. package/src/fetch/registry.ts +0 -615
  161. package/src/install/import-npm.ts +0 -177
  162. package/src/install/lock.ts +0 -176
  163. package/src/install/metadata.ts +0 -122
  164. package/src/install/registry.ts +0 -88
  165. package/src/install/run.ts +0 -760
  166. package/src/lockfile/fingerprints.ts +0 -74
  167. package/src/lockfile/read.ts +0 -198
  168. package/src/lockfile/types.ts +0 -75
  169. package/src/lockfile/write.ts +0 -257
  170. package/src/log.ts +0 -100
  171. package/src/project/projection.ts +0 -390
  172. package/src/resolve/resolver.ts +0 -757
  173. package/src/resolve/spec.ts +0 -131
  174. package/src/store/graph.ts +0 -175
  175. package/src/store/layout.ts +0 -431
  176. package/src/store/manifests.ts +0 -347
  177. package/src/store/objects.ts +0 -229
  178. package/src/store/realize.ts +0 -322
  179. package/src/store/source.ts +0 -249
  180. package/src/store/txn.ts +0 -118
  181. package/src/store/verify.ts +0 -433
  182. package/src/types/vendor.d.ts +0 -120
  183. package/src/util/fsx.ts +0 -289
  184. package/src/util/hash.ts +0 -117
  185. package/src/util/ids.ts +0 -42
  186. package/src/util/integrity.ts +0 -269
  187. package/src/util/json.ts +0 -89
  188. package/src/util/locks.ts +0 -266
  189. package/src/util/proc.ts +0 -108
@@ -0,0 +1,396 @@
1
+ /** Project inspection and explicit execution (SPEC 12.2, 18.3.1, D-1). */
2
+ import { spawn } from "node:child_process";
3
+ import { access, lstat, readFile, readdir, realpath } from "node:fs/promises";
4
+ import { constants } from "node:fs";
5
+ import * as path from "node:path";
6
+ import { hostname } from "node:os";
7
+ import { findProjectBoundary, resolveConfigFile, resolveStoreRoot, } from "../config/paths.js";
8
+ import { ZyrkError } from "../errors.js";
9
+ import { lockedGraph, packageName } from "../install/lock.js";
10
+ import { validateLockDoc } from "../lockfile/read.js";
11
+ import { manifestFingerprint, } from "../lockfile/fingerprints.js";
12
+ import {} from "../lockfile/types.js";
13
+ import { PROJECT_STATE_DIR } from "../project/projection.js";
14
+ import { openStoreDb } from "../store/db.js";
15
+ import { Store } from "../store/layout.js";
16
+ import { acquireLock } from "../util/locks.js";
17
+ import { closeCode, currentProcessIdentity } from "../util/proc.js";
18
+ import { randomId } from "../util/ids.js";
19
+ import { canonicalJson } from "../util/json.js";
20
+ import { sha512Hex } from "../util/hash.js";
21
+ import { CLI_VERSION } from "../version.js";
22
+ function object(value) {
23
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24
+ }
25
+ function fail(code, message) {
26
+ return new ZyrkError(code, message, {
27
+ remedy: "Review the project manifest, run zyrk install to restore its selected graph, and retry.",
28
+ });
29
+ }
30
+ async function json(file) {
31
+ try {
32
+ return JSON.parse(await readFile(file, "utf8"));
33
+ }
34
+ catch {
35
+ throw fail("ZYRK_MANIFEST_INVALID", "Cannot read project metadata as JSON.");
36
+ }
37
+ }
38
+ function validateState(raw) {
39
+ if (!object(raw) ||
40
+ raw.schemaVersion !== 1 ||
41
+ typeof raw.generationId !== "string" ||
42
+ !/^gen1_[\w-]+$/.test(raw.generationId) ||
43
+ typeof raw.projectId !== "string" ||
44
+ typeof raw.graphDigest !== "string" ||
45
+ typeof raw.lockDigest !== "string" ||
46
+ typeof raw.readiness !== "string" ||
47
+ !Array.isArray(raw.direct) ||
48
+ raw.direct.some((e) => !object(e) ||
49
+ typeof e.name !== "string" ||
50
+ !packageName(e.name) ||
51
+ typeof e.target !== "string"))
52
+ throw fail("ZYRK_STORE_CORRUPT", "Project install state is missing or malformed.");
53
+ return raw;
54
+ }
55
+ async function load(project) {
56
+ const state = validateState(await json(path.join(project, "node_modules/.zyrk/install-state.json")));
57
+ let doc;
58
+ try {
59
+ doc = validateLockDoc(await json(path.join(project, "package-lock.json")), "project package-lock.json");
60
+ lockedGraph(doc);
61
+ }
62
+ catch {
63
+ throw fail("ZYRK_LOCK_MISMATCH", "Cannot inspect the installed graph: package-lock.json has no compatible zyrk extension.");
64
+ }
65
+ if (sha512Hex(canonicalJson(doc)) !== state.lockDigest ||
66
+ doc.zyrk === undefined)
67
+ throw fail("ZYRK_LOCK_MISMATCH", "The installed generation and package-lock.json disagree.");
68
+ return { state, extension: doc.zyrk };
69
+ }
70
+ function emit(options, result, human) {
71
+ options.stdout.write(options.json
72
+ ? JSON.stringify({
73
+ schemaVersion: 1,
74
+ command: options.command,
75
+ ...result,
76
+ }) + "\n"
77
+ : human + "\n");
78
+ }
79
+ export async function runProjectCommand(options) {
80
+ const boundary = await findProjectBoundary(options.cwd);
81
+ if (!boundary)
82
+ throw fail("ZYRK_MANIFEST_INVALID", "Cannot find a project package.json.");
83
+ const project = await realpath(boundary);
84
+ const manifest = await json(path.join(project, "package.json"));
85
+ if (!object(manifest))
86
+ throw fail("ZYRK_MANIFEST_INVALID", "Project package.json must be an object.");
87
+ const executing = options.command === "run" || options.command === "exec";
88
+ if ((!executing && options.command !== "why" && options.args.length !== 0) ||
89
+ (options.command === "why" &&
90
+ (options.args.length !== 1 || !packageName(options.args[0] ?? ""))))
91
+ throw fail("ZYRK_USAGE", "Invalid project command arguments.");
92
+ if (executing && !options.args[0])
93
+ throw fail("ZYRK_USAGE", "Select a project script or installed executable.");
94
+ // Never follow a replaced control directory when acquiring the project lock.
95
+ const control = await lstat(path.join(project, PROJECT_STATE_DIR)).catch(() => null);
96
+ if (control && (!control.isDirectory() || control.isSymbolicLink()))
97
+ throw fail("ZYRK_STORE_CORRUPT", "Project control directory is not a real directory.");
98
+ const held = await acquireLock(path.join(project, PROJECT_STATE_DIR, "project.lock"), { name: "project execution or inspection" });
99
+ try {
100
+ if (options.command === "doctor")
101
+ return await doctor(options, project, manifest);
102
+ const { state, extension } = await load(project);
103
+ if (options.command === "list" || options.command === "why") {
104
+ const activeKeys = new Set();
105
+ const visit = (key) => {
106
+ if (activeKeys.has(key))
107
+ return;
108
+ activeKeys.add(key);
109
+ for (const target of Object.values(extension.nodes[key]?.edges ?? {}))
110
+ visit(target);
111
+ };
112
+ for (const entry of state.direct) {
113
+ const key = extension.roots["."]?.[entry.name];
114
+ if (key !== undefined)
115
+ visit(key);
116
+ }
117
+ const installed = Object.entries(extension.nodes).filter(([key]) => activeKeys.has(key));
118
+ const details = await retentionDetails(options.env, state);
119
+ const selected = options.command === "why"
120
+ ? installed.filter(([, n]) => n.name === options.args[0])
121
+ : installed;
122
+ const nodes = selected.map(([key, n]) => ({
123
+ key,
124
+ name: n.name,
125
+ version: n.version,
126
+ source: n.source,
127
+ peers: n.peers,
128
+ contexts: details
129
+ .filter((d) => d.source === n.source)
130
+ .map((d) => d.context),
131
+ retainingProjects: [
132
+ ...new Set(details
133
+ .filter((d) => d.source === n.source)
134
+ .flatMap((d) => d.projects)),
135
+ ],
136
+ dependencies: n.edges,
137
+ direct: Object.entries(extension.roots["."] ?? {})
138
+ .filter(([, target]) => target === key)
139
+ .map(([name]) => name),
140
+ requiredBy: installed.flatMap(([parent, node]) => Object.entries(node.edges)
141
+ .filter(([, target]) => target === key)
142
+ .map(([name]) => ({ parent, name }))),
143
+ realization: "shared",
144
+ }));
145
+ if (options.command === "why" && !nodes.length)
146
+ throw fail("ZYRK_RESOLUTION_FAILED", "The requested package is absent from the installed graph.");
147
+ emit(options, {
148
+ generationId: state.generationId,
149
+ graphDigest: state.graphDigest,
150
+ readiness: state.readiness,
151
+ nodes,
152
+ }, nodes
153
+ .map((n) => `${n.name}@${n.version}${n.direct.length ? " (direct: " + n.direct.join(", ") + ")" : ""}${n.requiredBy.length ? " <- " + n.requiredBy.map((p) => extension.nodes[p.parent]?.name).join(", ") : ""}`)
154
+ .join("\n") || "No installed dependencies.");
155
+ return 0;
156
+ }
157
+ if (!executing)
158
+ throw fail("ZYRK_USAGE", "Unknown project command.");
159
+ if (state.readiness !== "ready")
160
+ throw fail("ZYRK_SCRIPT_BLOCKED", "Cannot execute an intentionally unbuilt or unready dependency generation.");
161
+ let command, args;
162
+ const selected = options.args[0];
163
+ if (options.command === "run") {
164
+ await authorizeScript(options.env);
165
+ const scripts = manifest.scripts;
166
+ if (!object(scripts) ||
167
+ !Object.hasOwn(scripts, selected) ||
168
+ typeof scripts[selected] !== "string")
169
+ throw fail("ZYRK_USAGE", "The selected project script is not declared.");
170
+ const forwarded = options.args.slice(options.args[1] === "--" ? 2 : 1);
171
+ command = "/bin/sh";
172
+ args = [
173
+ "-c",
174
+ scripts[selected] +
175
+ (forwarded.length ? " " + forwarded.map(quote).join(" ") : ""),
176
+ ];
177
+ }
178
+ else {
179
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(selected))
180
+ throw fail("ZYRK_USAGE", "Select an installed executable by its local bin name.");
181
+ command = path.join(project, "node_modules/.bin", selected);
182
+ try {
183
+ await access(command, constants.X_OK);
184
+ if (!(await lstat(await realpath(command))).isFile())
185
+ throw new Error();
186
+ }
187
+ catch {
188
+ throw fail("ZYRK_RESOLUTION_FAILED", "The selected executable is not installed locally; exec never fetches packages.");
189
+ }
190
+ args = options.args.slice(1);
191
+ }
192
+ const env = {
193
+ ...options.env,
194
+ PATH: path.join(project, "node_modules/.bin") +
195
+ path.delimiter +
196
+ (options.env.PATH ?? ""),
197
+ INIT_CWD: options.cwd,
198
+ npm_package_json: path.join(project, "package.json"),
199
+ npm_execpath: path.resolve(import.meta.dirname, "../../bin/zyrk.js"),
200
+ npm_config_user_agent: `zyrk/${CLI_VERSION} node/${process.version} ${process.platform} ${process.arch}`,
201
+ };
202
+ if (options.command === "run")
203
+ Object.assign(env, {
204
+ npm_lifecycle_event: selected,
205
+ npm_lifecycle_script: manifest.scripts[selected],
206
+ });
207
+ return await leasedChild(options, state, command, args, project, env);
208
+ }
209
+ finally {
210
+ await held.release();
211
+ }
212
+ }
213
+ function quote(value) {
214
+ return "'" + value.replaceAll("'", "'\\''") + "'";
215
+ }
216
+ async function authorizeScript(env) {
217
+ let raw;
218
+ try {
219
+ raw = JSON.parse(await readFile(resolveConfigFile({ env }), "utf8"));
220
+ }
221
+ catch (error) {
222
+ if (error.code === "ENOENT")
223
+ return;
224
+ throw fail("ZYRK_POLICY_DENIED", "Cannot read user execution policy; project script execution is denied.");
225
+ }
226
+ // No permissive interpretation of security settings not implemented by this runner.
227
+ if (!object(raw) ||
228
+ Object.keys(raw).some((key) => /script|policy|security/i.test(key)))
229
+ throw fail("ZYRK_POLICY_DENIED", "Configured user script policy cannot be enforced by this runner; project script execution is denied.");
230
+ }
231
+ async function leasedChild(options, state, command, args, cwd, env) {
232
+ let store;
233
+ try {
234
+ store = await Store.open({
235
+ root: resolveStoreRoot({ env: options.env }).root,
236
+ });
237
+ store.requireInitialized();
238
+ }
239
+ catch {
240
+ throw fail("ZYRK_STORE_UNSAFE", "Cannot lease the installed graph in the selected user store.");
241
+ }
242
+ const barrier = await acquireLock(path.join(store.paths.locksDir, "liveness.lock"), { name: "generation lease registration" });
243
+ const db = openStoreDb(store.paths.dbFile);
244
+ const lease = "lease1_" + randomId("", 16);
245
+ try {
246
+ const generation = db
247
+ .prepare("SELECT generation_id FROM generations WHERE generation_id = ? AND project_id = ? AND graph_digest = ? AND state = 'active'")
248
+ .get(state.generationId, state.projectId, state.graphDigest);
249
+ if (!generation)
250
+ throw fail("ZYRK_STORE_CORRUPT", "Cannot lease the installed generation: its store registration is absent or inconsistent.");
251
+ const instances = db
252
+ .prepare("SELECT instance_id FROM generation_instances WHERE generation_id = ? ORDER BY instance_id")
253
+ .all(state.generationId)
254
+ .map((row) => row.instance_id);
255
+ const identity = currentProcessIdentity(), now = new Date().toISOString();
256
+ db.prepare("INSERT INTO leases VALUES (?, ?, ?, ?, ?, 'active', ?, ?)").run(lease, identity.pid, identity.startId, hostname(), JSON.stringify({
257
+ generationId: state.generationId,
258
+ graphDigest: state.graphDigest,
259
+ instances,
260
+ }), now, now);
261
+ }
262
+ catch (error) {
263
+ db.close();
264
+ throw error;
265
+ }
266
+ finally {
267
+ await barrier.release();
268
+ }
269
+ try {
270
+ return await new Promise((resolve, reject) => {
271
+ const child = spawn(command, args, {
272
+ cwd,
273
+ env,
274
+ stdio: ["inherit", "pipe", "pipe"],
275
+ detached: process.platform !== "win32",
276
+ });
277
+ // Once spawned, the child owns liveness even if the wrapper is killed.
278
+ // A null start discriminator intentionally retains a recycled PID conservatively.
279
+ if (child.pid !== undefined)
280
+ db.prepare("UPDATE leases SET holder_pid=?, holder_start_id=NULL WHERE lease_id=?").run(child.pid, lease);
281
+ const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
282
+ const listeners = signals.map((signal) => {
283
+ const listener = () => {
284
+ try {
285
+ if (child.pid)
286
+ process.kill(process.platform === "win32" ? child.pid : -child.pid, signal);
287
+ }
288
+ catch {
289
+ /* A finished child needs no forwarding. */
290
+ }
291
+ };
292
+ process.on(signal, listener);
293
+ return [signal, listener];
294
+ });
295
+ child.stdout?.on("data", (chunk) => options.stdout.write(chunk.toString()));
296
+ child.stderr?.on("data", (chunk) => options.stderr.write(chunk.toString()));
297
+ child.once("error", () => {
298
+ reject(fail("ZYRK_BUILD_FAILED", "Cannot launch the selected project command."));
299
+ });
300
+ child.once("close", (code, signal) => {
301
+ for (const [sig, listener] of listeners)
302
+ process.off(sig, listener);
303
+ resolve(closeCode(code, signal));
304
+ });
305
+ });
306
+ }
307
+ finally {
308
+ db.prepare("UPDATE leases SET state='released', heartbeat_at=? WHERE lease_id=?").run(new Date().toISOString(), lease);
309
+ db.close();
310
+ }
311
+ }
312
+ async function doctor(options, project, manifest) {
313
+ const diagnostics = [];
314
+ const add = (code, message, severity = "error") => diagnostics.push({ code, severity, message });
315
+ try {
316
+ const { state, extension } = await load(project);
317
+ if (manifestFingerprint(manifest) !==
318
+ extension.manifest)
319
+ add("ZYRK_LOCK_MISMATCH", "Dependency declarations differ from the installed lock.");
320
+ if (state.readiness !== "ready")
321
+ add("ZYRK_SCRIPT_BLOCKED", "Installed dependencies are intentionally unbuilt or unready.");
322
+ const expected = new Set(state.direct.map((entry) => entry.name));
323
+ for (const item of await readdir(path.join(project, "node_modules"))) {
324
+ if (item.startsWith("."))
325
+ continue;
326
+ const names = item.startsWith("@")
327
+ ? (await readdir(path.join(project, "node_modules", item))).map((name) => `${item}/${name}`)
328
+ : [item];
329
+ for (const name of names)
330
+ if (!expected.has(name))
331
+ add("ZYRK_STORE_CORRUPT", "Unexpected package entry exists outside the installed projection.");
332
+ }
333
+ for (const entry of state.direct) {
334
+ try {
335
+ const link = path.join(project, "node_modules", entry.name);
336
+ if (!(await lstat(link)).isSymbolicLink() ||
337
+ (await realpath(link)) !== (await realpath(entry.target)))
338
+ throw new Error();
339
+ }
340
+ catch {
341
+ add("ZYRK_STORE_CORRUPT", `Installed dependency link for ${entry.name} is missing or changed.`);
342
+ }
343
+ }
344
+ }
345
+ catch (error) {
346
+ add(error instanceof ZyrkError ? error.code : "ZYRK_STORE_CORRUPT", "Project install state or its lock is missing, invalid, or inconsistent.");
347
+ }
348
+ try {
349
+ const store = await Store.open({
350
+ root: resolveStoreRoot({ env: options.env }).root,
351
+ });
352
+ if (!store.initialized)
353
+ add("ZYRK_STORE_UNSAFE", "The selected user store is not initialized.");
354
+ }
355
+ catch {
356
+ add("ZYRK_STORE_UNSAFE", "The selected user store failed ownership, format, or filesystem safety checks.");
357
+ }
358
+ if (options.env.NODE_PATH)
359
+ add("ZYRK_POLICY_DENIED", "NODE_PATH enables ambient package lookup outside the selected graph.", "warning");
360
+ if (/--preserve-symlinks(?:-main)?(?:\s|=|$)/.test(options.env.NODE_OPTIONS ?? ""))
361
+ add("ZYRK_POLICY_DENIED", "Symlink preservation in NODE_OPTIONS can change dependency and singleton resolution.", "warning");
362
+ const ok = !diagnostics.some((d) => d.severity === "error");
363
+ emit(options, { ok, diagnostics }, diagnostics.length
364
+ ? diagnostics
365
+ .map((d) => `${d.severity}: ${d.code}: ${d.message}`)
366
+ .join("\n")
367
+ : "Project lock, direct dependency links, readiness, and store safety checks passed.");
368
+ return ok ? 0 : 1;
369
+ }
370
+ async function retentionDetails(env, state) {
371
+ try {
372
+ const store = await Store.open({ root: resolveStoreRoot({ env }).root });
373
+ if (!store.initialized)
374
+ return [];
375
+ const db = openStoreDb(store.paths.dbFile);
376
+ try {
377
+ const rows = db
378
+ .prepare(`SELECT r.source_id, gi.context_id FROM generation_instances gi JOIN realizations r ON r.context_id=gi.context_id WHERE gi.generation_id=?`)
379
+ .all(state.generationId);
380
+ return rows.map((row) => ({
381
+ source: String(row.source_id),
382
+ context: String(row.context_id),
383
+ projects: db
384
+ .prepare(`SELECT DISTINCT g.project_id FROM generations g JOIN generation_instances gi ON gi.generation_id=g.generation_id JOIN roots root ON root.generation_id=g.generation_id WHERE gi.context_id=? AND root.state <> 'removed' ORDER BY g.project_id`)
385
+ .all(row.context_id)
386
+ .map((p) => String(p.project_id)),
387
+ }));
388
+ }
389
+ finally {
390
+ db.close();
391
+ }
392
+ }
393
+ catch {
394
+ throw fail("ZYRK_STORE_CORRUPT", "Cannot inspect the installed generation retention metadata.");
395
+ }
396
+ }
@@ -0,0 +1,10 @@
1
+ import { Store } from "../store/layout.ts";
2
+ /** All publication and root/lease mutation uses this same store barrier. */
3
+ export declare function runStoreCommand(options: {
4
+ command: "store" | "projects";
5
+ args: readonly string[];
6
+ store: Store;
7
+ cwd: string;
8
+ json?: boolean;
9
+ }): Promise<unknown>;
10
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/commands/store.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AA4H3C,4EAA4E;AAC5E,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC7C,OAAO,EAAE,OAAO,GAAG,UAAU,CAAC;IAC9B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,OAAO,CAAC,CA+CnB"}