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,506 @@
1
+ /** Self-contained pure-package deployment and explicit local Docker commands. */
2
+ import { spawn } from "node:child_process";
3
+ import { copyFile, chmod, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rename, rm, symlink, writeFile, } from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ import { findProjectBoundary, resolveStoreRoot } from "../config/paths.js";
6
+ import { ZyrkError } from "../errors.js";
7
+ import { lockedGraph } from "../install/lock.js";
8
+ import { validateLockDoc } from "../lockfile/read.js";
9
+ import { manifestFingerprint, } from "../lockfile/fingerprints.js";
10
+ import { createGraphDoc, contextIdFor } from "../store/graph.js";
11
+ import { Store } from "../store/layout.js";
12
+ import { readSourceManifest } from "../store/manifests.js";
13
+ import { packageDirIn, realizationDirFor } from "../store/realize.js";
14
+ import { verifyRealization } from "../store/verify.js";
15
+ import { sha512Hex, sha512HexOfFile } from "../util/hash.js";
16
+ import { canonicalJson } from "../util/json.js";
17
+ import { acquireLock } from "../util/locks.js";
18
+ import { closeCode } from "../util/proc.js";
19
+ function object(v) {
20
+ return typeof v === "object" && v !== null && !Array.isArray(v);
21
+ }
22
+ function error(code, message) {
23
+ return new ZyrkError(code, message, {
24
+ remedy: "Select explicit runtime files and a direct Node entry, install a ready graph, and use a new output directory.",
25
+ });
26
+ }
27
+ function flags(args) {
28
+ const result = new Map();
29
+ for (let i = 0; i < args.length; i++) {
30
+ const item = args[i];
31
+ const split = item.indexOf("=");
32
+ const key = split < 0 ? item : item.slice(0, split);
33
+ const value = split < 0 ? args[++i] : item.slice(split + 1);
34
+ if (!key.startsWith("--") || !value || value.startsWith("--"))
35
+ throw error("ZYRK_USAGE", "Docker/deploy options require explicit values.");
36
+ result.set(key, [...(result.get(key) ?? []), value]);
37
+ }
38
+ return result;
39
+ }
40
+ function one(f, key) {
41
+ const values = f.get(key);
42
+ if ((values?.length ?? 0) > 1)
43
+ throw error("ZYRK_USAGE", "An option was supplied more than once.");
44
+ return values?.[0];
45
+ }
46
+ function within(root, file) {
47
+ const rel = path.relative(root, file);
48
+ return (rel === "" ||
49
+ (!rel.startsWith(".." + path.sep) && rel !== ".." && !path.isAbsolute(rel)));
50
+ }
51
+ function relativeFile(value) {
52
+ if (!value ||
53
+ path.isAbsolute(value) ||
54
+ value.split(/[\\/]/).some((p) => !p || p === "." || p === "..") ||
55
+ /[\0\r\n*?\[\]]/.test(value))
56
+ throw error("ZYRK_USAGE", "Runtime file selections must be concrete safe project-relative paths.");
57
+ return value;
58
+ }
59
+ function sensitive(name) {
60
+ return (/^(?:\.env(?:\..*)?|\.npmrc|\.netrc|\.git|\.zyrk-local|node_modules|\.ssh)$/i.test(name) || /\.(?:pem|key|p12|pfx)$/i.test(name));
61
+ }
62
+ async function readJson(file) {
63
+ try {
64
+ const doc = JSON.parse(await readFile(file, "utf8"));
65
+ if (object(doc))
66
+ return doc;
67
+ }
68
+ catch { }
69
+ throw error("ZYRK_MANIFEST_INVALID", "Cannot read deployment project metadata.");
70
+ }
71
+ function output(options, result, human) {
72
+ options.stdout.write(options.json
73
+ ? JSON.stringify({
74
+ schemaVersion: 1,
75
+ command: options.command,
76
+ ...result,
77
+ }) + "\n"
78
+ : human + "\n");
79
+ }
80
+ export async function runDeployCommand(options) {
81
+ const boundary = await findProjectBoundary(options.cwd);
82
+ if (!boundary)
83
+ throw error("ZYRK_MANIFEST_INVALID", "Cannot find a project to deploy.");
84
+ const project = await realpath(boundary);
85
+ const manifest = await readJson(path.join(project, "package.json"));
86
+ if (options.command === "docker")
87
+ return docker(options, project, manifest);
88
+ const f = flags(options.args);
89
+ for (const key of f.keys())
90
+ if (!["--out", "--omit", "--files", "--entry"].includes(key))
91
+ throw error("ZYRK_USAGE", "Unknown deploy option.");
92
+ if (one(f, "--omit") !== undefined && one(f, "--omit") !== "dev")
93
+ throw error("ZYRK_USAGE", "Deploy supports only --omit=dev.");
94
+ const destination = one(f, "--out");
95
+ if (!destination)
96
+ throw error("ZYRK_USAGE", "Deploy requires --out <new-directory>.");
97
+ const out = path.resolve(options.cwd, destination);
98
+ if (out === project || within(out, project))
99
+ throw error("ZYRK_DEPLOY_ESCAPE", "Deployment output overlaps the project input.");
100
+ const parent = await realpath(path.dirname(out)).catch(() => {
101
+ throw error("ZYRK_USAGE", "Deployment output parent must already exist.");
102
+ });
103
+ const actualOut = path.join(parent, path.basename(out));
104
+ if (actualOut !== out || actualOut === project || within(actualOut, project))
105
+ throw error("ZYRK_DEPLOY_ESCAPE", "Deployment output resolves through a link or overlaps project input.");
106
+ if (await lstat(out).catch(() => null))
107
+ throw error("ZYRK_USAGE", "Deployment output already exists; choose a new directory.");
108
+ const files = runtimeFiles(f, manifest);
109
+ const entry = entryFile(f, manifest);
110
+ if (!files.some((file) => within(file, entry)))
111
+ throw error("ZYRK_DEPLOY_NONRELOCATABLE", "The Node entry is absent from the selected runtime files.");
112
+ for (const file of files)
113
+ if (within(path.join(project, file), out) ||
114
+ within(out, path.join(project, file)))
115
+ throw error("ZYRK_DEPLOY_ESCAPE", "Deployment output overlaps selected runtime files.");
116
+ const control = await lstat(path.join(project, ".zyrk-local")).catch(() => null);
117
+ if (control && (!control.isDirectory() || control.isSymbolicLink()))
118
+ throw error("ZYRK_STORE_CORRUPT", "Project control directory is unsafe.");
119
+ const projectLock = await acquireLock(path.join(project, ".zyrk-local/project.lock"), { name: "portable deployment" });
120
+ try {
121
+ const state = await readJson(path.join(project, "node_modules/.zyrk/install-state.json"));
122
+ const doc = validateLockDoc(await readJson(path.join(project, "package-lock.json")), "project package-lock.json");
123
+ const resolved = lockedGraph(doc);
124
+ if (state.readiness !== "ready")
125
+ throw error("ZYRK_SCRIPT_BLOCKED", "Cannot export an unready or intentionally unbuilt generation.");
126
+ if (state.lockDigest !== sha512Hex(canonicalJson(doc)) ||
127
+ doc.zyrk?.manifest !==
128
+ manifestFingerprint(manifest))
129
+ throw error("ZYRK_LOCK_MISMATCH", "Deployment manifest, lock, and installed generation disagree.");
130
+ if (state.platform !== process.platform || state.arch !== process.arch)
131
+ throw error("ZYRK_PLATFORM_UNSUPPORTED", "Deployment requires artifacts for the current platform and architecture.");
132
+ const store = await Store.open({
133
+ root: resolveStoreRoot({ env: options.env }).root,
134
+ });
135
+ store.requireInitialized();
136
+ const barrier = await acquireLock(path.join(store.paths.locksDir, "liveness.lock"), { name: "portable deployment retention" });
137
+ try {
138
+ const installedKeys = new Set(Array.isArray(state.contexts)
139
+ ? state.contexts.filter((v) => typeof v === "string")
140
+ : []);
141
+ const installedNodes = resolved.nodes.filter((n) => installedKeys.has(n.key));
142
+ const graph = createGraphDoc(installedNodes.map((n) => ({ ...n, deps: n.edges })));
143
+ const selectedRoots = {};
144
+ const declarations = {
145
+ ...(object(manifest.dependencies) ? manifest.dependencies : {}),
146
+ ...(object(manifest.optionalDependencies)
147
+ ? manifest.optionalDependencies
148
+ : {}),
149
+ };
150
+ for (const name of Object.keys(declarations)) {
151
+ const key = doc.zyrk?.roots["."]?.[name];
152
+ if (key && installedKeys.has(key))
153
+ selectedRoots[name] = key;
154
+ else if (!object(manifest.optionalDependencies) ||
155
+ manifest.optionalDependencies[name] === undefined)
156
+ throw error("ZYRK_OFFLINE_MISSING", "A required runtime dependency is not installed.");
157
+ }
158
+ const selected = new Set();
159
+ const byKey = new Map(graph.nodes.map((n) => [n.key, n]));
160
+ const visit = (key) => {
161
+ if (selected.has(key))
162
+ return;
163
+ selected.add(key);
164
+ const node = byKey.get(key);
165
+ if (!node)
166
+ throw error("ZYRK_OFFLINE_MISSING", "A runtime dependency edge is missing.");
167
+ for (const edge of node.edges)
168
+ visit(edge.target);
169
+ };
170
+ Object.values(selectedRoots).forEach(visit);
171
+ for (const key of selected)
172
+ await verifyRealization({
173
+ store,
174
+ graph,
175
+ rootKey: key,
176
+ contextId: contextIdFor(graph, key),
177
+ });
178
+ const stage = await mkdtemp(path.join(parent, ".zyrk-deploy-"));
179
+ try {
180
+ for (const file of files)
181
+ await copyApplication(path.join(project, file), path.join(stage, file), project);
182
+ const deployedManifest = { ...manifest };
183
+ delete deployedManifest.devDependencies;
184
+ delete deployedManifest.zyrk;
185
+ delete deployedManifest.packageManager;
186
+ await writeFile(path.join(stage, "package.json"), JSON.stringify(deployedManifest, null, 2) + "\n");
187
+ const packageDirs = new Map();
188
+ for (const key of selected) {
189
+ const node = byKey.get(key);
190
+ const context = contextIdFor(graph, key);
191
+ const sourceDir = packageDirIn(realizationDirFor(store, context), node.name);
192
+ const targetDir = packageDirIn(path.join(stage, "node_modules/.zyrk/portable", context), node.name);
193
+ packageDirs.set(key, targetDir);
194
+ await mkdir(targetDir, { recursive: true });
195
+ const source = await readSourceManifest(store, node.sourceId);
196
+ for (const e of source.manifest.entries) {
197
+ const target = path.join(targetDir, e.path);
198
+ if (!within(targetDir, target))
199
+ throw error("ZYRK_DEPLOY_ESCAPE", "A dependency source path escapes its portable package.");
200
+ await mkdir(path.dirname(target), { recursive: true });
201
+ if (e.type === "dir")
202
+ await mkdir(target, { recursive: true });
203
+ else if (e.type === "symlink") {
204
+ if (path.isAbsolute(e.target) ||
205
+ !within(targetDir, path.resolve(path.dirname(target), e.target)))
206
+ throw error("ZYRK_DEPLOY_ESCAPE", "A package symlink escapes its portable package.");
207
+ await symlink(e.target, target);
208
+ }
209
+ else {
210
+ if (e.path.endsWith(".node"))
211
+ throw error("ZYRK_DEPLOY_NONRELOCATABLE", "Native addon runtime compatibility requires a validated deployment adapter.");
212
+ await copyFile(path.join(sourceDir, e.path), target);
213
+ await chmod(target, e.mode === "exec" ? 0o755 : 0o644);
214
+ }
215
+ }
216
+ }
217
+ for (const key of selected) {
218
+ const node = byKey.get(key), pkg = packageDirs.get(key);
219
+ for (const edge of node.edges) {
220
+ const modules = edge.name === node.name
221
+ ? path.join(pkg, "node_modules")
222
+ : path.dirname(node.name.startsWith("@") ? path.dirname(pkg) : pkg);
223
+ await relativeLink(path.join(modules, edge.name), packageDirs.get(edge.target));
224
+ }
225
+ }
226
+ for (const [name, key] of Object.entries(selectedRoots))
227
+ await relativeLink(path.join(stage, "node_modules", name), packageDirs.get(key));
228
+ // Local launchers are recomputed from selected roots; their targets stay portable.
229
+ for (const [name, key] of Object.entries(selectedRoots)) {
230
+ const node = doc.zyrk.nodes[key];
231
+ for (const [bin, target] of Object.entries(node.bin ?? {})) {
232
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(bin))
233
+ throw error("ZYRK_DEPLOY_ESCAPE", "Unsafe executable name in portable graph.");
234
+ const file = path.resolve(packageDirs.get(key), target);
235
+ if (!within(packageDirs.get(key), file))
236
+ throw error("ZYRK_DEPLOY_ESCAPE", "Portable executable escapes its package.");
237
+ const stat = await lstat(file).catch(() => null);
238
+ if (!stat?.isFile())
239
+ throw error("ZYRK_DEPLOY_NONRELOCATABLE", "A required package executable is missing.");
240
+ if ((stat.mode & 0o111) !== 0)
241
+ await relativeLink(path.join(stage, "node_modules/.bin", bin), file);
242
+ else {
243
+ await mkdir(path.join(stage, "node_modules/.bin"), {
244
+ recursive: true,
245
+ });
246
+ const rel = path
247
+ .relative(path.join(stage, "node_modules/.bin"), file)
248
+ .split(path.sep)
249
+ .join("/");
250
+ await writeFile(path.join(stage, "node_modules/.bin", bin), '#!/bin/sh\nexec node "$(dirname "$0")"/' +
251
+ shellQuote(rel) +
252
+ ' "$@"\n', { mode: 0o755 });
253
+ }
254
+ }
255
+ void name;
256
+ }
257
+ const entryStat = await lstat(path.join(stage, entry)).catch(() => null);
258
+ if (!entryStat?.isFile())
259
+ throw error("ZYRK_DEPLOY_NONRELOCATABLE", "The selected runtime Node entry is missing.");
260
+ await validateLinks(stage, stage);
261
+ await writeFile(path.join(stage, "zyrk-deploy.json"), JSON.stringify({
262
+ schemaVersion: 1,
263
+ platform: process.platform,
264
+ arch: process.arch,
265
+ node: process.version,
266
+ entry: ["node", entry],
267
+ requirements: ["Node.js " + process.versions.node.split(".")[0]],
268
+ packages: selected.size,
269
+ files,
270
+ validation: "structural; application smoke test not run",
271
+ }, null, 2) + "\n");
272
+ await rename(stage, out);
273
+ output(options, {
274
+ packages: selected.size,
275
+ entry: ["node", entry],
276
+ validation: "structural",
277
+ }, `Exported ${selected.size} runtime dependencies. Start with: node ${entry}. Structural validation passed; no application smoke test ran.`);
278
+ return 0;
279
+ }
280
+ finally {
281
+ await rm(stage, { recursive: true, force: true });
282
+ }
283
+ }
284
+ finally {
285
+ await barrier.release();
286
+ }
287
+ }
288
+ finally {
289
+ await projectLock.release();
290
+ }
291
+ }
292
+ function runtimeFiles(f, manifest) {
293
+ const raw = f.get("--files") ?? manifest.files;
294
+ if (!Array.isArray(raw) ||
295
+ !raw.length ||
296
+ raw.some((v) => typeof v !== "string"))
297
+ throw error("ZYRK_USAGE", "Declare runtime files with repeated --files paths or a package.json files array.");
298
+ return [...new Set(raw.map((v) => relativeFile(String(v))))];
299
+ }
300
+ function entryFile(f, manifest) {
301
+ let entry = one(f, "--entry");
302
+ if (!entry &&
303
+ object(manifest.scripts) &&
304
+ typeof manifest.scripts.start === "string")
305
+ entry = /^node\s+([a-zA-Z0-9_./-]+)$/.exec(manifest.scripts.start)?.[1];
306
+ if (!entry)
307
+ throw error("ZYRK_USAGE", "Choose a direct Node runtime entry with --entry <project-relative-file>.");
308
+ return relativeFile(entry);
309
+ }
310
+ async function copyApplication(source, target, project) {
311
+ if (path.relative(project, source).split(path.sep).some(sensitive))
312
+ throw error("ZYRK_POLICY_DENIED", "A runtime file selection includes credentials, control state, or development dependencies.");
313
+ const stat = await lstat(source).catch(() => null);
314
+ if (!stat)
315
+ throw error("ZYRK_DEPLOY_NONRELOCATABLE", "A selected runtime file is missing.");
316
+ if (stat.isSymbolicLink())
317
+ throw error("ZYRK_DEPLOY_ESCAPE", "Application runtime selections must contain real files and directories.");
318
+ if (stat.isDirectory()) {
319
+ await mkdir(target, { recursive: true });
320
+ for (const name of await readdir(source))
321
+ await copyApplication(path.join(source, name), path.join(target, name), project);
322
+ }
323
+ else if (stat.isFile()) {
324
+ await mkdir(path.dirname(target), { recursive: true });
325
+ await copyFile(source, target);
326
+ await chmod(target, stat.mode & 0o111 ? 0o755 : 0o644);
327
+ }
328
+ else
329
+ throw error("ZYRK_DEPLOY_ESCAPE", "Special files cannot be exported as application files.");
330
+ }
331
+ async function relativeLink(link, target) {
332
+ await mkdir(path.dirname(link), { recursive: true });
333
+ const existing = await lstat(link).catch(() => null);
334
+ if (existing) {
335
+ if (existing.isSymbolicLink() &&
336
+ (await readlink(link)) === path.relative(path.dirname(link), target))
337
+ return;
338
+ throw error("ZYRK_DEPLOY_NONRELOCATABLE", "Conflicting dependency or executable slots exist in the runtime graph.");
339
+ }
340
+ await symlink(path.relative(path.dirname(link), target), link);
341
+ }
342
+ async function validateLinks(root, dir) {
343
+ for (const name of await readdir(dir)) {
344
+ const file = path.join(dir, name), stat = await lstat(file);
345
+ if (stat.isSymbolicLink()) {
346
+ const target = await readlink(file);
347
+ if (path.isAbsolute(target) ||
348
+ !within(root, path.resolve(path.dirname(file), target)) ||
349
+ !within(root, await realpath(file)))
350
+ throw error("ZYRK_DEPLOY_ESCAPE", "A portable link escapes the deployment boundary.");
351
+ }
352
+ else if (stat.isDirectory())
353
+ await validateLinks(root, file);
354
+ }
355
+ }
356
+ function shellQuote(value) {
357
+ return "'" + value.replaceAll("'", "'\\''") + "'";
358
+ }
359
+ async function docker(options, project, manifest) {
360
+ const operation = options.args[0];
361
+ if (!["init", "build", "run"].includes(operation ?? ""))
362
+ throw error("ZYRK_USAGE", "Select docker init, build, or run.");
363
+ const f = flags(options.args.slice(1));
364
+ if (operation === "init") {
365
+ for (const key of f.keys())
366
+ if (![
367
+ "--node-image",
368
+ "--cli-artifact",
369
+ "--cli-sha512",
370
+ "--build-script",
371
+ "--entry",
372
+ "--files",
373
+ "--port",
374
+ ].includes(key))
375
+ throw error("ZYRK_USAGE", "Unknown docker init option.");
376
+ const image = one(f, "--node-image");
377
+ if (!image ||
378
+ !/^node:(?:24|26)[a-zA-Z0-9._-]*@sha256:[0-9a-f]{64}$/.test(image))
379
+ throw error("ZYRK_USAGE", "Docker init requires a supported Node image pinned by sha256 digest.");
380
+ const artifact = relativeFile(one(f, "--cli-artifact") ?? "");
381
+ const digest = one(f, "--cli-sha512");
382
+ if (!digest || !/^[0-9a-f]{128}$/.test(digest))
383
+ throw error("ZYRK_USAGE", "Docker init requires the pinned CLI artifact sha512 digest.");
384
+ const actual = await realpath(path.join(project, artifact)).catch(() => {
385
+ throw error("ZYRK_USAGE", "The pinned CLI artifact is missing.");
386
+ });
387
+ if (!within(project, actual) || (await sha512HexOfFile(actual)) !== digest)
388
+ throw error("ZYRK_INTEGRITY_FAILED", "The pinned CLI artifact failed its project boundary or digest check.");
389
+ const entry = entryFile(f, manifest), files = runtimeFiles(f, manifest);
390
+ if (!files.some((file) => within(file, entry)))
391
+ throw error("ZYRK_DEPLOY_NONRELOCATABLE", "The runtime entry must belong to the selected production file set.");
392
+ const build = one(f, "--build-script");
393
+ if (build &&
394
+ (!object(manifest.scripts) || typeof manifest.scripts[build] !== "string"))
395
+ throw error("ZYRK_USAGE", "The selected Docker build script is not declared.");
396
+ const portText = one(f, "--port") ?? "3000";
397
+ if (!/^\d+$/.test(portText) ||
398
+ Number(portText) < 1 ||
399
+ Number(portText) > 65535)
400
+ throw error("ZYRK_USAGE", "Choose a runtime port between 1 and 65535.");
401
+ const artifactJson = JSON.stringify([artifact, "/tmp/zyrk-cli.tgz"]);
402
+ const deployArgs = files.map((file) => " --files " + shellQuote(file)).join("") +
403
+ " --entry " +
404
+ shellQuote(entry);
405
+ const dockerfile = `# syntax=docker/dockerfile:1\n# Generated inputs are recorded in zyrk-docker.json; review before building.\nFROM ${image} AS build\nWORKDIR /app\nCOPY ${artifactJson}\nRUN echo '${digest} /tmp/zyrk-cli.tgz' | sha512sum --check - && npm install --global /tmp/zyrk-cli.tgz && rm /tmp/zyrk-cli.tgz\nENV ZYRK_STORE_DIR=/zyrk-store\nCOPY . .\nRUN --mount=type=secret,id=npmrc,target=/root/.npmrc zyrk fetch --frozen-lockfile\nRUN --network=none zyrk install --frozen-lockfile --offline${build ? " && zyrk run " + shellQuote(build) : ""} && zyrk deploy --out /runtime-output --omit=dev${deployArgs}\nFROM ${image} AS runtime\nWORKDIR /app\nENV NODE_ENV=production\nCOPY --from=build --chown=node:node /runtime-output/ /app/\nUSER node\nEXPOSE ${portText}\nCMD ${JSON.stringify(["node", entry])}\n`;
406
+ const contents = {
407
+ Dockerfile: dockerfile,
408
+ ".dockerignore": "node_modules\n.git\n.env\n.env.*\n**/.env\n**/.env.*\n.npmrc\n**/.npmrc\n.netrc\n**/.netrc\n**/*.pem\n**/*.key\n.zyrk-local\ndist-runtime\nnpm-debug.log*\n",
409
+ "zyrk-docker.json": JSON.stringify({
410
+ schemaVersion: 1,
411
+ nodeImage: image,
412
+ cliArtifact: artifact,
413
+ cliSha512: digest,
414
+ entry,
415
+ files,
416
+ buildScript: build ?? null,
417
+ port: Number(portText),
418
+ }, null, 2) + "\n",
419
+ "ZYRK_DOCKER.md": `Review Dockerfile and .dockerignore before building. Runtime files: ${files.join(", ")}. Runtime entry: node ${entry}. Build script: ${build ?? "none"}. Container port: ${portText}.\n\nThe pinned CLI tarball must remain at its declared path. Dependencies are fetched with an optional BuildKit npmrc secret, then installed, built, and exported without networking or registry secrets. No host node_modules or store enters the runtime image. Adapt build inputs and credential exclusions to your project; generated files do not prove application health. Native/private build adapters and arbitrary shell start commands are not supported by this generic exporter.\n\nBuild: zyrk docker build --tag your-app:local\nRun: zyrk docker run --tag your-app:local --port ${portText}\nSupply private registry credentials to Docker BuildKit directly with docker build --secret id=npmrc,src=<credential-file> when required; zyrk never embeds them.\n`,
420
+ };
421
+ for (const name of Object.keys(contents))
422
+ if (await lstat(path.join(project, name)).catch(() => null))
423
+ throw error("ZYRK_USAGE", "Docker setup files already exist; review or move them before generating new files.");
424
+ const created = [];
425
+ try {
426
+ for (const [name, content] of Object.entries(contents)) {
427
+ await writeFile(path.join(project, name), content, {
428
+ flag: "wx",
429
+ mode: 0o644,
430
+ });
431
+ created.push(name);
432
+ }
433
+ }
434
+ catch (cause) {
435
+ for (const name of created)
436
+ await rm(path.join(project, name));
437
+ throw cause;
438
+ }
439
+ output(options, {
440
+ files: Object.keys(contents),
441
+ entry: ["node", entry],
442
+ port: Number(portText),
443
+ nodeImage: image,
444
+ }, `Created reviewable Docker setup files. Runtime: node ${entry}; port ${portText}; image ${image}. Build script: ${build ?? "none"}.`);
445
+ return 0;
446
+ }
447
+ for (const key of f.keys())
448
+ if (!["--tag", "--port"].includes(key) ||
449
+ (operation === "build" && key === "--port"))
450
+ throw error("ZYRK_USAGE", "Unknown Docker build/run option.");
451
+ const tag = one(f, "--tag");
452
+ if (!tag ||
453
+ !/^([a-zA-Z0-9][a-zA-Z0-9._/-]*)(?::[a-zA-Z0-9_][a-zA-Z0-9_.-]*)?$/.test(tag))
454
+ throw error("ZYRK_USAGE", "Docker build/run requires an explicit local image --tag.");
455
+ const config = await readJson(path.join(project, "zyrk-docker.json"));
456
+ if (config.schemaVersion !== 1 ||
457
+ typeof config.port !== "number" ||
458
+ !Number.isInteger(config.port) ||
459
+ config.port < 1 ||
460
+ config.port > 65535)
461
+ throw error("ZYRK_MANIFEST_INVALID", "Docker runtime declaration is invalid.");
462
+ const args = operation === "build"
463
+ ? ["build", "--tag", tag, "."]
464
+ : [
465
+ "run",
466
+ "--rm",
467
+ "--publish",
468
+ `${one(f, "--port") ?? String(config.port)}:${config.port}`,
469
+ tag,
470
+ ];
471
+ if (operation === "run" &&
472
+ !/^\d{1,5}$/.test(one(f, "--port") ?? String(config.port)))
473
+ throw error("ZYRK_USAGE", "Choose an explicit numeric host port.");
474
+ const code = await subprocess(options, "docker", args, project, {
475
+ ...options.env,
476
+ DOCKER_BUILDKIT: "1",
477
+ });
478
+ if (code === 0)
479
+ output(options, { operation, tag, exitCode: code }, `Docker ${operation} completed for ${tag}. Application health was not tested.`);
480
+ return code;
481
+ }
482
+ async function subprocess(options, command, args, cwd, env) {
483
+ return new Promise((resolve, reject) => {
484
+ const child = spawn(command, args, {
485
+ cwd,
486
+ env,
487
+ stdio: ["inherit", "pipe", "pipe"],
488
+ });
489
+ child.stdout?.on("data", (c) => options.stdout.write(c.toString()));
490
+ child.stderr?.on("data", (c) => options.stderr.write(c.toString()));
491
+ const signals = ["SIGTERM", "SIGINT", "SIGHUP"];
492
+ const listeners = signals.map((sig) => {
493
+ const listener = () => {
494
+ child.kill(sig);
495
+ };
496
+ process.on(sig, listener);
497
+ return [sig, listener];
498
+ });
499
+ child.once("error", () => reject(error("ZYRK_BUILD_FAILED", "Cannot launch Docker; install the Docker CLI and enable a reachable BuildKit-capable daemon.")));
500
+ child.once("close", (code, signal) => {
501
+ for (const [sig, listener] of listeners)
502
+ process.off(sig, listener);
503
+ resolve(closeCode(code, signal));
504
+ });
505
+ });
506
+ }
@@ -0,0 +1,27 @@
1
+ import type { RegistryClient } from "../fetch/registry.ts";
2
+ import type { Store } from "../store/layout.ts";
3
+ import { type InstallResult } from "../install/run.ts";
4
+ declare const severities: readonly ["info", "low", "moderate", "high", "critical"];
5
+ type Severity = (typeof severities)[number];
6
+ export type AuditResult = {
7
+ kind: "audit";
8
+ status: "complete" | "disabled" | "unavailable";
9
+ advisories: Array<{
10
+ name: string;
11
+ version: string;
12
+ severity: Severity;
13
+ title: string;
14
+ url: string;
15
+ }>;
16
+ thresholdExceeded: boolean;
17
+ warnings: string[];
18
+ };
19
+ export declare function runMaintenanceCommand(options: {
20
+ command: "audit" | "fetch" | "repair";
21
+ args: readonly string[];
22
+ projectDir: string;
23
+ store: Store;
24
+ registry: RegistryClient;
25
+ }): Promise<InstallResult | AuditResult>;
26
+ export {};
27
+ //# sourceMappingURL=maintenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maintenance.d.ts","sourceRoot":"","sources":["../../src/commands/maintenance.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AASvE,QAAA,MAAM,UAAU,YAAI,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,CAAU,CAAC;AAC5E,KAAK,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAC5C,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,aAAa,CAAC;IAChD,UAAU,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,QAAQ,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC,CAAC;IACH,iBAAiB,EAAE,OAAO,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAUF,wBAAsB,qBAAqB,CAAC,OAAO,EAAE;IACnD,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;IACtC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;CAC1B,GAAG,OAAO,CAAC,aAAa,GAAG,WAAW,CAAC,CAoNvC"}