@venn-lang/contracts 0.1.0

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 (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +254 -0
  3. package/dist/index.d.ts +620 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +1342 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/node.d.mts +287 -0
  8. package/dist/node.d.mts.map +1 -0
  9. package/dist/node.mjs +387 -0
  10. package/dist/node.mjs.map +1 -0
  11. package/dist/signal-source.types-BI3TYaCi.d.ts +317 -0
  12. package/dist/signal-source.types-BI3TYaCi.d.ts.map +1 -0
  13. package/dist/testing.d.ts +84 -0
  14. package/dist/testing.d.ts.map +1 -0
  15. package/dist/testing.js +276 -0
  16. package/dist/testing.js.map +1 -0
  17. package/package.json +77 -0
  18. package/src/capabilities/host-capability.ts +18 -0
  19. package/src/capabilities/index.ts +2 -0
  20. package/src/conformance/expect-venn-error.ts +18 -0
  21. package/src/conformance/harness.types.ts +14 -0
  22. package/src/conformance/index.ts +2 -0
  23. package/src/dotenv/dotenv-files.ts +30 -0
  24. package/src/dotenv/index.ts +2 -0
  25. package/src/dotenv/parse-dotenv.ts +34 -0
  26. package/src/errors/contract-codes.ts +37 -0
  27. package/src/errors/index.ts +7 -0
  28. package/src/errors/venn-error.ts +20 -0
  29. package/src/host/create-host.ts +16 -0
  30. package/src/host/create-node-host.ts +29 -0
  31. package/src/host/create-test-host.ts +28 -0
  32. package/src/host/create-worker-host.ts +32 -0
  33. package/src/host/host.types.ts +26 -0
  34. package/src/host/index.ts +5 -0
  35. package/src/host/unavailable.ts +23 -0
  36. package/src/index.ts +10 -0
  37. package/src/logger/console-logger.ts +12 -0
  38. package/src/logger/index.ts +3 -0
  39. package/src/logger/logger.types.ts +18 -0
  40. package/src/logger/memory-logger.ts +12 -0
  41. package/src/node.ts +8 -0
  42. package/src/port/assert-capabilities.ts +18 -0
  43. package/src/port/assert-port-shape.ts +18 -0
  44. package/src/port/bind-port.ts +28 -0
  45. package/src/port/index.ts +5 -0
  46. package/src/port/missing-capabilities.ts +14 -0
  47. package/src/port/port.types.ts +36 -0
  48. package/src/ports/clock/clock.port.ts +15 -0
  49. package/src/ports/clock/clock.types.ts +17 -0
  50. package/src/ports/clock/index.ts +4 -0
  51. package/src/ports/clock/system-clock.ts +9 -0
  52. package/src/ports/clock/virtual-clock.ts +22 -0
  53. package/src/ports/console/console.port.ts +13 -0
  54. package/src/ports/console/console.types.ts +17 -0
  55. package/src/ports/console/index.ts +3 -0
  56. package/src/ports/console/memory-console.ts +36 -0
  57. package/src/ports/console/node-console.ts +80 -0
  58. package/src/ports/file-system/file-system.errors.ts +10 -0
  59. package/src/ports/file-system/file-system.port.ts +12 -0
  60. package/src/ports/file-system/file-system.types.ts +27 -0
  61. package/src/ports/file-system/index.ts +6 -0
  62. package/src/ports/file-system/memory-fs.ts +46 -0
  63. package/src/ports/file-system/node-fs.ts +93 -0
  64. package/src/ports/index.ts +9 -0
  65. package/src/ports/lock-provider/fake-lock.ts +6 -0
  66. package/src/ports/lock-provider/in-process-lock.ts +26 -0
  67. package/src/ports/lock-provider/index.ts +4 -0
  68. package/src/ports/lock-provider/lock-provider.port.ts +16 -0
  69. package/src/ports/lock-provider/lock-provider.types.ts +8 -0
  70. package/src/ports/manifest-provider/default-manifest.ts +31 -0
  71. package/src/ports/manifest-provider/edit/edit-dependency.ts +82 -0
  72. package/src/ports/manifest-provider/edit/index.ts +7 -0
  73. package/src/ports/manifest-provider/edit/table-span.ts +39 -0
  74. package/src/ports/manifest-provider/index.ts +31 -0
  75. package/src/ports/manifest-provider/manifest.port.ts +16 -0
  76. package/src/ports/manifest-provider/manifest.types.ts +54 -0
  77. package/src/ports/manifest-provider/memory-manifest.ts +14 -0
  78. package/src/ports/manifest-provider/project.types.ts +94 -0
  79. package/src/ports/manifest-provider/read/index.ts +15 -0
  80. package/src/ports/manifest-provider/read/read-dependencies.ts +27 -0
  81. package/src/ports/manifest-provider/read/read-package.ts +32 -0
  82. package/src/ports/manifest-provider/read/read-profiles.ts +32 -0
  83. package/src/ports/manifest-provider/read/read-targets.ts +37 -0
  84. package/src/ports/manifest-provider/read/read-tooling.ts +15 -0
  85. package/src/ports/manifest-provider/read/read-workspace.ts +22 -0
  86. package/src/ports/manifest-provider/read/scalars.ts +35 -0
  87. package/src/ports/manifest-provider/read-run-settings.ts +38 -0
  88. package/src/ports/manifest-provider/resolve-alias.ts +22 -0
  89. package/src/ports/manifest-provider/toml/index.ts +3 -0
  90. package/src/ports/manifest-provider/toml/parse-toml.ts +52 -0
  91. package/src/ports/manifest-provider/toml/read-value.ts +104 -0
  92. package/src/ports/manifest-provider/toml/sections.ts +39 -0
  93. package/src/ports/manifest-provider/toml-docs.ts +27 -0
  94. package/src/ports/manifest-provider/toml-manifest.ts +41 -0
  95. package/src/ports/process-provider/fake-process.ts +32 -0
  96. package/src/ports/process-provider/index.ts +10 -0
  97. package/src/ports/process-provider/node-spawn.ts +52 -0
  98. package/src/ports/process-provider/process-provider.port.ts +13 -0
  99. package/src/ports/process-provider/process-provider.types.ts +47 -0
  100. package/src/ports/random/fixed-random.ts +10 -0
  101. package/src/ports/random/index.ts +4 -0
  102. package/src/ports/random/random.port.ts +13 -0
  103. package/src/ports/random/random.types.ts +10 -0
  104. package/src/ports/random/seeded-random.ts +19 -0
  105. package/src/ports/secret-provider/env-secrets.ts +19 -0
  106. package/src/ports/secret-provider/index.ts +6 -0
  107. package/src/ports/secret-provider/memory-secrets.ts +15 -0
  108. package/src/ports/secret-provider/secret-provider.port.ts +13 -0
  109. package/src/ports/secret-provider/secret-provider.types.ts +17 -0
  110. package/src/ports/secret-provider/secret.ts +17 -0
  111. package/src/ports/signal-source/fake-signals.ts +44 -0
  112. package/src/ports/signal-source/index.ts +11 -0
  113. package/src/ports/signal-source/node-signals.ts +33 -0
  114. package/src/ports/signal-source/signal-source.port.ts +13 -0
  115. package/src/ports/signal-source/signal-source.types.ts +24 -0
  116. package/src/testing.ts +16 -0
package/dist/node.mjs ADDED
@@ -0,0 +1,387 @@
1
+ import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
2
+ import { dirname, isAbsolute, join } from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ import { createInterface } from "node:readline";
5
+ import { constants } from "node:os";
6
+ import process$1 from "node:process";
7
+ //#region src/capabilities/host-capability.ts
8
+ /**
9
+ * Every capability a Host may expose. A port or plugin declares which it
10
+ * requires, a host advertises which it provides, and negotiation compares the
11
+ * two before anything binds.
12
+ */
13
+ const ALL_CAPABILITIES = [
14
+ "fs",
15
+ "process",
16
+ "net",
17
+ "clock",
18
+ "random",
19
+ "secrets",
20
+ "log",
21
+ "io"
22
+ ];
23
+ //#endregion
24
+ //#region src/logger/console-logger.ts
25
+ /** Logger backed by the global console, so it works in a Worker and in Node. */
26
+ function createConsoleLogger() {
27
+ return { log(entry) {
28
+ const line = `[${entry.level}] ${entry.message}`;
29
+ if (entry.level === "error") console.error(line);
30
+ else console.log(line);
31
+ } };
32
+ }
33
+ //#endregion
34
+ //#region src/ports/clock/system-clock.ts
35
+ /** Wall-clock time, on the `Date` and `setTimeout` globals. */
36
+ function createSystemClock() {
37
+ return {
38
+ now: () => Date.now(),
39
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
40
+ };
41
+ }
42
+ //#endregion
43
+ //#region src/errors/venn-error.ts
44
+ /**
45
+ * The single error type that crosses a contracts boundary.
46
+ *
47
+ * Every failure carries a stable `VNxxxx` code, so conformance suites assert on
48
+ * `.code` and never on prose. Messages stay free to improve.
49
+ */
50
+ var VennError = class extends Error {
51
+ code;
52
+ detail;
53
+ constructor(args) {
54
+ super(args.message);
55
+ this.name = "VennError";
56
+ this.code = args.code;
57
+ this.detail = args.detail;
58
+ }
59
+ };
60
+ //#endregion
61
+ //#region src/ports/file-system/file-system.errors.ts
62
+ /** VN8010: a read or a remove targeted a path that does not exist. */
63
+ function fsNotFound(args) {
64
+ return new VennError({
65
+ code: "VN8010",
66
+ message: `File not found: "${args.path}".`,
67
+ detail: { path: args.path }
68
+ });
69
+ }
70
+ //#endregion
71
+ //#region src/ports/file-system/node-fs.ts
72
+ /**
73
+ * The real file system, rooted at `root`.
74
+ *
75
+ * @param args.root - where relative paths resolve. Defaults to ".".
76
+ * @returns a {@link FileSystem} that reports a missing path as VN8010 and any
77
+ * other failure as VN8019.
78
+ */
79
+ function createNodeFs(args = {}) {
80
+ const root = args.root ?? ".";
81
+ const at = (path) => isAbsolute(path) ? path : join(root, path);
82
+ return {
83
+ read: (path) => readBytes(at(path)),
84
+ write: (path, bytes) => writeBytes(at(path), bytes),
85
+ exists: (path) => pathExists(at(path)),
86
+ remove: (path) => removePath(at(path)),
87
+ list: (path) => listPath(at(path))
88
+ };
89
+ }
90
+ async function listPath(directory) {
91
+ const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
92
+ const found = [];
93
+ for (const entry of entries) found.push({
94
+ name: entry.name,
95
+ directory: await holdsMore(directory, entry)
96
+ });
97
+ return found;
98
+ }
99
+ /**
100
+ * Whether an entry holds more, following a link if it is one.
101
+ *
102
+ * `readdir` reports a symlink as a symlink and not as what it points at, and
103
+ * whole directory trees are built out of them: pnpm links every installed
104
+ * package into place, so `node_modules/zod` is a link. Trusting the first
105
+ * answer finds no packages at all in a full `node_modules`.
106
+ */
107
+ async function holdsMore(directory, entry) {
108
+ if (entry.isDirectory()) return true;
109
+ if (!entry.isSymbolicLink()) return false;
110
+ return (await stat(join(directory, entry.name)).catch(() => void 0))?.isDirectory() ?? false;
111
+ }
112
+ async function readBytes(file) {
113
+ try {
114
+ return new Uint8Array(await readFile(file));
115
+ } catch (err) {
116
+ throw mapMissing(err, file);
117
+ }
118
+ }
119
+ async function writeBytes(file, bytes) {
120
+ await mkdir(dirname(file), { recursive: true });
121
+ await writeFile(file, bytes);
122
+ }
123
+ async function pathExists(file) {
124
+ try {
125
+ await stat(file);
126
+ return true;
127
+ } catch {
128
+ return false;
129
+ }
130
+ }
131
+ async function removePath(file) {
132
+ try {
133
+ await rm(file);
134
+ } catch (err) {
135
+ throw mapMissing(err, file);
136
+ }
137
+ }
138
+ function mapMissing(err, file) {
139
+ if (isEnoent(err)) return fsNotFound({ path: file });
140
+ return new VennError({
141
+ code: "VN8019",
142
+ message: err instanceof Error ? err.message : String(err)
143
+ });
144
+ }
145
+ function isEnoent(err) {
146
+ return typeof err === "object" && err !== null && err.code === "ENOENT";
147
+ }
148
+ //#endregion
149
+ //#region src/ports/lock-provider/in-process-lock.ts
150
+ /**
151
+ * The real one: a chained-promise mutex per name. Waiters are served in the
152
+ * order they asked, and the chain lives only in this process.
153
+ */
154
+ function createInProcessLock() {
155
+ const tails = /* @__PURE__ */ new Map();
156
+ return { acquire: (name) => acquire(tails, name) };
157
+ }
158
+ async function acquire(tails, name) {
159
+ const previous = tails.get(name) ?? Promise.resolve();
160
+ let release = () => {};
161
+ const held = new Promise((resolve) => {
162
+ release = resolve;
163
+ });
164
+ tails.set(name, previous.then(() => held));
165
+ await previous;
166
+ return release;
167
+ }
168
+ //#endregion
169
+ //#region src/ports/process-provider/node-spawn.ts
170
+ /**
171
+ * The real one, on `node:child_process`.
172
+ *
173
+ * Output is streamed to whoever asked and kept at the same time: a package
174
+ * manager needs watching while it works and reading once it is done, and only
175
+ * one of the two would leave either a command that looks hung or a result
176
+ * nobody can inspect.
177
+ */
178
+ function createNodeSpawn() {
179
+ return { spawn: (args) => start(args) };
180
+ }
181
+ function start(args) {
182
+ const child = spawn(args.command, [...args.args ?? []], {
183
+ cwd: args.cwd,
184
+ env: args.env ? {
185
+ ...process.env,
186
+ ...args.env
187
+ } : process.env,
188
+ shell: args.shell ?? false
189
+ });
190
+ const chunks = [];
191
+ listen(child, chunks, args.onOutput);
192
+ return {
193
+ pid: child.pid ?? 0,
194
+ wait: () => ended(child, chunks),
195
+ kill: () => void child.kill()
196
+ };
197
+ }
198
+ function listen(child, into, onOutput) {
199
+ for (const stream of [child.stdout, child.stderr]) stream?.on("data", (chunk) => {
200
+ const text = chunk.toString("utf8");
201
+ into.push(text);
202
+ onOutput?.(text);
203
+ });
204
+ }
205
+ /**
206
+ * A command that could never start reads as one that ran and failed, with the
207
+ * reason as its output, because that is what a caller can act on. `127` is what
208
+ * a shell says for "no such command", and it means the same here.
209
+ */
210
+ function ended(child, chunks) {
211
+ return new Promise((resolve) => {
212
+ child.on("error", (err) => resolve({
213
+ code: 127,
214
+ output: `${chunks.join("")}${err.message}`
215
+ }));
216
+ child.on("close", (code) => resolve({
217
+ code: code ?? 0,
218
+ output: chunks.join("")
219
+ }));
220
+ });
221
+ }
222
+ //#endregion
223
+ //#region src/ports/random/seeded-random.ts
224
+ /**
225
+ * The real one: a mulberry32 PRNG, seeded once per worker. Same seed, same
226
+ * sequence, so a run reproduces.
227
+ */
228
+ function createSeededRandom(args) {
229
+ let state = args.seed >>> 0;
230
+ const next = () => {
231
+ state = state + 1831565813 | 0;
232
+ let t = Math.imul(state ^ state >>> 15, 1 | state);
233
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
234
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
235
+ };
236
+ return {
237
+ next,
238
+ int: (min, max) => min + Math.floor(next() * (max - min + 1))
239
+ };
240
+ }
241
+ //#endregion
242
+ //#region src/ports/secret-provider/secret.ts
243
+ /** What every serialised secret collapses to. */
244
+ const REDACTED = "‹redigido›";
245
+ /**
246
+ * Wrap a raw value so it cannot leak through `toString` or `toJSON`.
247
+ *
248
+ * @param args.reveal - the raw value, reachable only via `Secret.reveal()`.
249
+ */
250
+ function makeSecret(args) {
251
+ return {
252
+ reveal: () => args.reveal,
253
+ toString: () => REDACTED,
254
+ toJSON: () => REDACTED
255
+ };
256
+ }
257
+ //#endregion
258
+ //#region src/ports/secret-provider/env-secrets.ts
259
+ /**
260
+ * The real one: secrets from `process.env`.
261
+ *
262
+ * The `process` global is probed rather than imported, so this file stays
263
+ * neutral and reports every name as absent in a Worker instead of throwing.
264
+ */
265
+ function createEnvSecrets() {
266
+ const env = typeof process === "undefined" ? {} : process.env;
267
+ return {
268
+ get: (name) => {
269
+ const value = env[name];
270
+ return value === void 0 ? void 0 : makeSecret({ reveal: value });
271
+ },
272
+ has: (name) => env[name] !== void 0
273
+ };
274
+ }
275
+ //#endregion
276
+ //#region src/host/create-node-host.ts
277
+ /**
278
+ * Host for the CLI: the real file system, secrets from the environment, and
279
+ * every capability.
280
+ *
281
+ * @param args.root - where relative paths resolve. Defaults to the process's
282
+ * current directory.
283
+ */
284
+ function createNodeHost(args = {}) {
285
+ return {
286
+ fs: createNodeFs({ root: args.root }),
287
+ proc: createNodeSpawn(),
288
+ clock: createSystemClock(),
289
+ random: createSeededRandom({ seed: 1 }),
290
+ secrets: createEnvSecrets(),
291
+ log: createConsoleLogger(),
292
+ lock: createInProcessLock(),
293
+ caps: ALL_CAPABILITIES
294
+ };
295
+ }
296
+ //#endregion
297
+ //#region src/ports/console/node-console.ts
298
+ /**
299
+ * The real console: Node's own streams.
300
+ *
301
+ * stdin is opened lazily, on the first `readLine`. A script that never reads
302
+ * input must not hold the process open waiting for a line that never comes.
303
+ *
304
+ * @param streams - overrides for stdin, stdout, stderr and argv. Each defaults
305
+ * to the process's own.
306
+ */
307
+ function createNodeConsole(streams = {}) {
308
+ const lines = createLineReader(streams.stdin ?? process.stdin);
309
+ const out = streams.stdout ?? process.stdout;
310
+ const err = streams.stderr ?? process.stderr;
311
+ return {
312
+ write: (text) => void out.write(text),
313
+ writeError: (text) => void err.write(text),
314
+ readLine: () => lines.next(),
315
+ args: () => streams.argv ?? []
316
+ };
317
+ }
318
+ function createLineReader(input) {
319
+ const state = {
320
+ buffered: [],
321
+ waiting: [],
322
+ done: false,
323
+ input
324
+ };
325
+ return { next: () => next(state) };
326
+ }
327
+ function next(state) {
328
+ open(state);
329
+ if (state.buffered.length > 0) return Promise.resolve(state.buffered.shift() ?? null);
330
+ if (state.done) return Promise.resolve(null);
331
+ return new Promise((resolve) => state.waiting.push(resolve));
332
+ }
333
+ /** Attach to stdin on the first read; a TTY with no piped input ends at once. */
334
+ function open(state) {
335
+ if (state.reader || state.done) return;
336
+ if (state.input.isTTY) {
337
+ state.done = true;
338
+ return;
339
+ }
340
+ state.reader = createInterface({
341
+ input: state.input,
342
+ crlfDelay: Number.POSITIVE_INFINITY
343
+ });
344
+ state.reader.on("line", (line) => deliver(state, line));
345
+ state.reader.on("close", () => finish(state));
346
+ }
347
+ function deliver(state, line) {
348
+ const resolve = state.waiting.shift();
349
+ if (resolve) resolve(line);
350
+ else state.buffered.push(line);
351
+ }
352
+ function finish(state) {
353
+ state.done = true;
354
+ for (const resolve of state.waiting.splice(0)) resolve(null);
355
+ }
356
+ //#endregion
357
+ //#region src/ports/signal-source/node-signals.ts
358
+ /**
359
+ * Whether this platform knows the signal.
360
+ *
361
+ * Not every name exists everywhere: `SIGBREAK` is Windows only, and subscribing
362
+ * to an unknown one throws. Asking first turns a crash at start up into a
363
+ * subscription that never fires.
364
+ */
365
+ function isKnownSignal(signal) {
366
+ return signal in constants.signals;
367
+ }
368
+ /**
369
+ * The real one: the signals this process receives.
370
+ *
371
+ * Lives behind `@venn-lang/contracts/node` because it touches `node:process`, so the
372
+ * main entry stays neutral and still runs in the editor's worker.
373
+ */
374
+ function createNodeSignals() {
375
+ return { on: (signal, handler) => {
376
+ if (!isKnownSignal(signal)) return () => {};
377
+ const listener = () => handler(signal);
378
+ process$1.on(signal, listener);
379
+ return () => {
380
+ process$1.off(signal, listener);
381
+ };
382
+ } };
383
+ }
384
+ //#endregion
385
+ export { createNodeConsole, createNodeFs, createNodeHost, createNodeSignals, createNodeSpawn, isKnownSignal };
386
+
387
+ //# sourceMappingURL=node.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.mjs","names":[],"sources":["../src/capabilities/host-capability.ts","../src/logger/console-logger.ts","../src/ports/clock/system-clock.ts","../src/errors/venn-error.ts","../src/ports/file-system/file-system.errors.ts","../src/ports/file-system/node-fs.ts","../src/ports/lock-provider/in-process-lock.ts","../src/ports/process-provider/node-spawn.ts","../src/ports/random/seeded-random.ts","../src/ports/secret-provider/secret.ts","../src/ports/secret-provider/env-secrets.ts","../src/host/create-node-host.ts","../src/ports/console/node-console.ts","../src/ports/signal-source/node-signals.ts"],"sourcesContent":["/**\n * Every capability a Host may expose. A port or plugin declares which it\n * requires, a host advertises which it provides, and negotiation compares the\n * two before anything binds.\n */\nexport const ALL_CAPABILITIES = [\n \"fs\",\n \"process\",\n \"net\",\n \"clock\",\n \"random\",\n \"secrets\",\n \"log\",\n \"io\",\n] as const;\n\n/** One capability name, drawn from {@link ALL_CAPABILITIES}. */\nexport type HostCapability = (typeof ALL_CAPABILITIES)[number];\n","import type { Logger } from \"./logger.types.js\";\n\n/** Logger backed by the global console, so it works in a Worker and in Node. */\nexport function createConsoleLogger(): Logger {\n return {\n log(entry) {\n const line = `[${entry.level}] ${entry.message}`;\n if (entry.level === \"error\") console.error(line);\n else console.log(line);\n },\n };\n}\n","import type { Clock } from \"./clock.types.js\";\n\n/** Wall-clock time, on the `Date` and `setTimeout` globals. */\nexport function createSystemClock(): Clock {\n return {\n now: () => Date.now(),\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n };\n}\n","/** Structured, serializable, redaction-safe detail attached to a {@link VennError}. */\nexport type VennErrorDetail = Readonly<Record<string, unknown>>;\n\n/**\n * The single error type that crosses a contracts boundary.\n *\n * Every failure carries a stable `VNxxxx` code, so conformance suites assert on\n * `.code` and never on prose. Messages stay free to improve.\n */\nexport class VennError extends Error {\n readonly code: string;\n readonly detail: VennErrorDetail | undefined;\n\n constructor(args: { code: string; message: string; detail?: VennErrorDetail }) {\n super(args.message);\n this.name = \"VennError\";\n this.code = args.code;\n this.detail = args.detail;\n }\n}\n","import { VennError } from \"../../errors/index.js\";\n\n/** VN8010: a read or a remove targeted a path that does not exist. */\nexport function fsNotFound(args: { path: string }): VennError {\n return new VennError({\n code: \"VN8010\",\n message: `File not found: \"${args.path}\".`,\n detail: { path: args.path },\n });\n}\n","import { mkdir, readdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join } from \"node:path\";\nimport { VennError } from \"../../errors/index.js\";\nimport { fsNotFound } from \"./file-system.errors.js\";\nimport type { DirEntry, FileSystem } from \"./file-system.types.js\";\n\n/**\n * The real file system, rooted at `root`.\n *\n * @param args.root - where relative paths resolve. Defaults to \".\".\n * @returns a {@link FileSystem} that reports a missing path as VN8010 and any\n * other failure as VN8019.\n */\nexport function createNodeFs(args: { root?: string } = {}): FileSystem {\n const root = args.root ?? \".\";\n // A root is where *relative* paths resolve. Joining it onto an absolute path\n // corrupts the path rather than relocating it: `join(\".\", \"C:/x\")` is\n // `.\\C:\\x`, which cannot exist, and the failure surfaces far from here.\n const at = (path: string): string => (isAbsolute(path) ? path : join(root, path));\n return {\n read: (path) => readBytes(at(path)),\n write: (path, bytes) => writeBytes(at(path), bytes),\n exists: (path) => pathExists(at(path)),\n remove: (path) => removePath(at(path)),\n list: (path) => listPath(at(path)),\n };\n}\n\nasync function listPath(directory: string): Promise<DirEntry[]> {\n const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);\n const found: DirEntry[] = [];\n for (const entry of entries) {\n found.push({ name: entry.name, directory: await holdsMore(directory, entry) });\n }\n return found;\n}\n\ntype Entry = { name: string; isDirectory(): boolean; isSymbolicLink(): boolean };\n\n/**\n * Whether an entry holds more, following a link if it is one.\n *\n * `readdir` reports a symlink as a symlink and not as what it points at, and\n * whole directory trees are built out of them: pnpm links every installed\n * package into place, so `node_modules/zod` is a link. Trusting the first\n * answer finds no packages at all in a full `node_modules`.\n */\nasync function holdsMore(directory: string, entry: Entry): Promise<boolean> {\n if (entry.isDirectory()) return true;\n if (!entry.isSymbolicLink()) return false;\n const target = await stat(join(directory, entry.name)).catch(() => undefined);\n return target?.isDirectory() ?? false;\n}\n\nasync function readBytes(file: string): Promise<Uint8Array> {\n try {\n return new Uint8Array(await readFile(file));\n } catch (err) {\n throw mapMissing(err, file);\n }\n}\n\nasync function writeBytes(file: string, bytes: Uint8Array): Promise<void> {\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, bytes);\n}\n\nasync function pathExists(file: string): Promise<boolean> {\n try {\n await stat(file);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function removePath(file: string): Promise<void> {\n try {\n await rm(file);\n } catch (err) {\n throw mapMissing(err, file);\n }\n}\n\nfunction mapMissing(err: unknown, file: string): VennError {\n if (isEnoent(err)) return fsNotFound({ path: file });\n const message = err instanceof Error ? err.message : String(err);\n return new VennError({ code: \"VN8019\", message });\n}\n\nfunction isEnoent(err: unknown): boolean {\n return typeof err === \"object\" && err !== null && (err as { code?: string }).code === \"ENOENT\";\n}\n","import type { LockProvider, Release } from \"./lock-provider.types.js\";\n\n/**\n * The real one: a chained-promise mutex per name. Waiters are served in the\n * order they asked, and the chain lives only in this process.\n */\nexport function createInProcessLock(): LockProvider {\n const tails = new Map<string, Promise<void>>();\n return {\n acquire: (name) => acquire(tails, name),\n };\n}\n\nasync function acquire(tails: Map<string, Promise<void>>, name: string): Promise<Release> {\n const previous = tails.get(name) ?? Promise.resolve();\n let release: Release = () => {};\n const held = new Promise<void>((resolve) => {\n release = resolve;\n });\n tails.set(\n name,\n previous.then(() => held),\n );\n await previous;\n return release;\n}\n","import { type ChildProcess, spawn } from \"node:child_process\";\nimport type {\n ProcessHandle,\n ProcessProvider,\n ProcessResult,\n SpawnArgs,\n} from \"./process-provider.types.js\";\n\n/**\n * The real one, on `node:child_process`.\n *\n * Output is streamed to whoever asked and kept at the same time: a package\n * manager needs watching while it works and reading once it is done, and only\n * one of the two would leave either a command that looks hung or a result\n * nobody can inspect.\n */\nexport function createNodeSpawn(): ProcessProvider {\n return { spawn: (args) => start(args) };\n}\n\nfunction start(args: SpawnArgs): ProcessHandle {\n const child = spawn(args.command, [...(args.args ?? [])], {\n cwd: args.cwd,\n env: args.env ? { ...process.env, ...args.env } : process.env,\n shell: args.shell ?? false,\n });\n const chunks: string[] = [];\n listen(child, chunks, args.onOutput);\n return { pid: child.pid ?? 0, wait: () => ended(child, chunks), kill: () => void child.kill() };\n}\n\nfunction listen(child: ChildProcess, into: string[], onOutput?: (chunk: string) => void): void {\n for (const stream of [child.stdout, child.stderr]) {\n stream?.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString(\"utf8\");\n into.push(text);\n onOutput?.(text);\n });\n }\n}\n\n/**\n * A command that could never start reads as one that ran and failed, with the\n * reason as its output, because that is what a caller can act on. `127` is what\n * a shell says for \"no such command\", and it means the same here.\n */\nfunction ended(child: ChildProcess, chunks: string[]): Promise<ProcessResult> {\n return new Promise((resolve) => {\n child.on(\"error\", (err) => resolve({ code: 127, output: `${chunks.join(\"\")}${err.message}` }));\n child.on(\"close\", (code) => resolve({ code: code ?? 0, output: chunks.join(\"\") }));\n });\n}\n","import type { Random } from \"./random.types.js\";\n\n/**\n * The real one: a mulberry32 PRNG, seeded once per worker. Same seed, same\n * sequence, so a run reproduces.\n */\nexport function createSeededRandom(args: { seed: number }): Random {\n let state = args.seed >>> 0;\n const next = (): number => {\n state = (state + 0x6d2b79f5) | 0;\n let t = Math.imul(state ^ (state >>> 15), 1 | state);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n return {\n next,\n int: (min, max) => min + Math.floor(next() * (max - min + 1)),\n };\n}\n","import type { Secret } from \"./secret-provider.types.js\";\n\n/** What every serialised secret collapses to. */\nexport const REDACTED = \"‹redigido›\";\n\n/**\n * Wrap a raw value so it cannot leak through `toString` or `toJSON`.\n *\n * @param args.reveal - the raw value, reachable only via `Secret.reveal()`.\n */\nexport function makeSecret(args: { reveal: string }): Secret {\n return {\n reveal: () => args.reveal,\n toString: () => REDACTED,\n toJSON: () => REDACTED,\n };\n}\n","import { makeSecret } from \"./secret.js\";\nimport type { SecretProvider } from \"./secret-provider.types.js\";\n\n/**\n * The real one: secrets from `process.env`.\n *\n * The `process` global is probed rather than imported, so this file stays\n * neutral and reports every name as absent in a Worker instead of throwing.\n */\nexport function createEnvSecrets(): SecretProvider {\n const env: Record<string, string | undefined> = typeof process === \"undefined\" ? {} : process.env;\n return {\n get: (name) => {\n const value = env[name];\n return value === undefined ? undefined : makeSecret({ reveal: value });\n },\n has: (name) => env[name] !== undefined,\n };\n}\n","import { ALL_CAPABILITIES } from \"../capabilities/index.js\";\nimport { createConsoleLogger } from \"../logger/index.js\";\nimport { createSystemClock } from \"../ports/clock/index.js\";\nimport { createNodeFs } from \"../ports/file-system/node-fs.js\";\nimport { createInProcessLock } from \"../ports/lock-provider/index.js\";\nimport { createNodeSpawn } from \"../ports/process-provider/node-spawn.js\";\nimport { createSeededRandom } from \"../ports/random/index.js\";\nimport { createEnvSecrets } from \"../ports/secret-provider/index.js\";\nimport type { Host } from \"./host.types.js\";\n\n/**\n * Host for the CLI: the real file system, secrets from the environment, and\n * every capability.\n *\n * @param args.root - where relative paths resolve. Defaults to the process's\n * current directory.\n */\nexport function createNodeHost(args: { root?: string } = {}): Host {\n return {\n fs: createNodeFs({ root: args.root }),\n proc: createNodeSpawn(),\n clock: createSystemClock(),\n random: createSeededRandom({ seed: 1 }),\n secrets: createEnvSecrets(),\n log: createConsoleLogger(),\n lock: createInProcessLock(),\n caps: ALL_CAPABILITIES,\n };\n}\n","import { createInterface, type Interface } from \"node:readline\";\nimport type { Console } from \"./console.types.js\";\n\n/**\n * Where a node console reads and writes. Injectable, so a test can drive the\n * real implementation and read back what a script printed.\n */\nexport interface ConsoleStreams {\n argv?: readonly string[];\n stdout?: { write(text: string): unknown };\n stderr?: { write(text: string): unknown };\n /** Where lines are read from. Defaults to the process's own standard input. */\n stdin?: NodeJS.ReadableStream & { isTTY?: boolean };\n}\n\n/**\n * The real console: Node's own streams.\n *\n * stdin is opened lazily, on the first `readLine`. A script that never reads\n * input must not hold the process open waiting for a line that never comes.\n *\n * @param streams - overrides for stdin, stdout, stderr and argv. Each defaults\n * to the process's own.\n */\nexport function createNodeConsole(streams: ConsoleStreams = {}): Console {\n const lines = createLineReader(streams.stdin ?? process.stdin);\n const out = streams.stdout ?? process.stdout;\n const err = streams.stderr ?? process.stderr;\n return {\n write: (text) => void out.write(text),\n writeError: (text) => void err.write(text),\n readLine: () => lines.next(),\n args: () => streams.argv ?? [],\n };\n}\n\ninterface LineReaderState {\n buffered: string[];\n waiting: ((line: string | null) => void)[];\n done: boolean;\n input: NodeJS.ReadableStream & { isTTY?: boolean };\n reader?: Interface;\n}\n\nfunction createLineReader(input: NodeJS.ReadableStream & { isTTY?: boolean }): {\n next(): Promise<string | null>;\n} {\n const state: LineReaderState = { buffered: [], waiting: [], done: false, input };\n return { next: () => next(state) };\n}\n\nfunction next(state: LineReaderState): Promise<string | null> {\n open(state);\n if (state.buffered.length > 0) return Promise.resolve(state.buffered.shift() ?? null);\n if (state.done) return Promise.resolve(null);\n return new Promise((resolve) => state.waiting.push(resolve));\n}\n\n/** Attach to stdin on the first read; a TTY with no piped input ends at once. */\nfunction open(state: LineReaderState): void {\n if (state.reader || state.done) return;\n if (state.input.isTTY) {\n state.done = true;\n return;\n }\n state.reader = createInterface({ input: state.input, crlfDelay: Number.POSITIVE_INFINITY });\n state.reader.on(\"line\", (line) => deliver(state, line));\n state.reader.on(\"close\", () => finish(state));\n}\n\nfunction deliver(state: LineReaderState, line: string): void {\n const resolve = state.waiting.shift();\n if (resolve) resolve(line);\n else state.buffered.push(line);\n}\n\nfunction finish(state: LineReaderState): void {\n state.done = true;\n for (const resolve of state.waiting.splice(0)) resolve(null);\n}\n","import { constants } from \"node:os\";\nimport process from \"node:process\";\nimport type { SignalSource, SystemSignal, Unsubscribe } from \"./signal-source.types.js\";\n\n/**\n * Whether this platform knows the signal.\n *\n * Not every name exists everywhere: `SIGBREAK` is Windows only, and subscribing\n * to an unknown one throws. Asking first turns a crash at start up into a\n * subscription that never fires.\n */\nexport function isKnownSignal(signal: SystemSignal): boolean {\n return signal in constants.signals;\n}\n\n/**\n * The real one: the signals this process receives.\n *\n * Lives behind `@venn-lang/contracts/node` because it touches `node:process`, so the\n * main entry stays neutral and still runs in the editor's worker.\n */\nexport function createNodeSignals(): SignalSource {\n return {\n on: (signal, handler): Unsubscribe => {\n if (!isKnownSignal(signal)) return () => {};\n const listener = () => handler(signal);\n process.on(signal, listener);\n return () => {\n process.off(signal, listener);\n };\n },\n };\n}\n"],"mappings":"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"}