stitchkit 0.103.8 → 0.103.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 (64) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/agent-runtime/sandbox-process.d.ts +2 -2
  3. package/dist/agent-runtime/sandbox-process.d.ts.map +1 -1
  4. package/dist/durability/contract.d.ts +3 -2
  5. package/dist/durability/contract.d.ts.map +1 -1
  6. package/dist/durability/effect.d.ts.map +1 -1
  7. package/dist/durability/engine.d.ts.map +1 -1
  8. package/dist/durability/json.d.ts.map +1 -1
  9. package/dist/entrypoints/agent-runtime/harness.js +2 -2
  10. package/dist/entrypoints/agent-runtime/sandbox.js +29 -81
  11. package/dist/entrypoints/agent-runtime.js +2 -2
  12. package/dist/entrypoints/cli.js +1 -1
  13. package/dist/entrypoints/files.d.ts +2 -0
  14. package/dist/entrypoints/files.d.ts.map +1 -1
  15. package/dist/entrypoints/files.js +6 -2
  16. package/dist/entrypoints/primitives.js +77 -57
  17. package/dist/entrypoints/process.d.ts +4 -0
  18. package/dist/entrypoints/process.d.ts.map +1 -0
  19. package/dist/entrypoints/process.js +13 -0
  20. package/dist/entrypoints/tools.js +12 -3
  21. package/dist/files/boundary.d.ts +19 -1
  22. package/dist/files/boundary.d.ts.map +1 -1
  23. package/dist/files/file-io.d.ts +5 -1
  24. package/dist/files/file-io.d.ts.map +1 -1
  25. package/dist/files/read.d.ts +13 -0
  26. package/dist/files/read.d.ts.map +1 -0
  27. package/dist/index-12m2v2a9.js +98 -0
  28. package/dist/index-8a9ksm91.js +95 -0
  29. package/dist/{index-90ppdx3q.js → index-9jb61j5a.js} +1 -1
  30. package/dist/{index-5vyd0fkb.js → index-ary5s7k0.js} +40 -61
  31. package/dist/index-c1jmtxa0.js +74 -0
  32. package/dist/index-eeqmg7qm.js +202 -0
  33. package/dist/index-mbnnw3f2.js +25 -0
  34. package/dist/{index-v4a2fjtk.js → index-sybedc8b.js} +124 -55
  35. package/dist/internal/abort-race.d.ts +3 -0
  36. package/dist/internal/abort-race.d.ts.map +1 -0
  37. package/dist/internal/atomic-file.d.ts +20 -2
  38. package/dist/internal/atomic-file.d.ts.map +1 -1
  39. package/dist/internal/atomic-publication.d.ts +19 -0
  40. package/dist/internal/atomic-publication.d.ts.map +1 -0
  41. package/dist/internal/json-data.d.ts +8 -0
  42. package/dist/internal/json-data.d.ts.map +1 -0
  43. package/dist/primitives/canonical-json.d.ts +11 -0
  44. package/dist/primitives/canonical-json.d.ts.map +1 -0
  45. package/dist/primitives/index.d.ts +1 -0
  46. package/dist/primitives/index.d.ts.map +1 -1
  47. package/dist/process/command-owner.d.ts +13 -0
  48. package/dist/process/command-owner.d.ts.map +1 -0
  49. package/dist/process/command.d.ts +3 -0
  50. package/dist/process/command.d.ts.map +1 -0
  51. package/dist/process/contract.d.ts +35 -0
  52. package/dist/process/contract.d.ts.map +1 -0
  53. package/dist/process/group.d.ts +6 -0
  54. package/dist/process/group.d.ts.map +1 -0
  55. package/dist/tools/transfer/managed-file-error.d.ts.map +1 -1
  56. package/llms/files.txt +8 -0
  57. package/llms/primitives.txt +31 -1
  58. package/llms/process.txt +167 -0
  59. package/llms/stitchkit.txt +1 -0
  60. package/llms/tools.txt +18 -0
  61. package/llms-full.txt +216 -1
  62. package/llms.txt +6 -4
  63. package/package.json +5 -1
  64. package/dist/index-aan6hqb9.js +0 -65
@@ -0,0 +1,202 @@
1
+ import {
2
+ raceAbort
3
+ } from "./index-mbnnw3f2.js";
4
+
5
+ // src/process/contract.ts
6
+ import { z } from "zod";
7
+ var NativeCommandOptionsSchema = z.strictObject({
8
+ executable: z.string().min(1),
9
+ args: z.array(z.string()).default([]),
10
+ cwd: z.string().optional(),
11
+ env: z.record(z.string(), z.string()).optional(),
12
+ envPolicy: z.enum(["declared-only", "ambient"]).default("declared-only"),
13
+ signal: z.custom((v) => v instanceof AbortSignal).optional(),
14
+ timeoutMs: z.number().int().positive().max(2147483647).optional(),
15
+ maxOutputBytes: z.number().int().positive().optional(),
16
+ capture: z.boolean().default(false),
17
+ stdin: z.custom((v) => v instanceof Uint8Array).optional(),
18
+ maxStdinBytes: z.number().int().positive().default(1024 * 1024),
19
+ onOutput: z.custom((v) => typeof v === "function").optional(),
20
+ killGraceMs: z.number().int().nonnegative().max(1e4).default(100),
21
+ cleanupTimeoutMs: z.number().int().positive().max(30000).default(2000)
22
+ }).superRefine((input, ctx) => {
23
+ if (!input.signal && input.timeoutMs === undefined)
24
+ ctx.addIssue({ code: "custom", message: "Declare a caller signal or a finite timeout" });
25
+ if (input.capture && input.maxOutputBytes === undefined)
26
+ ctx.addIssue({
27
+ code: "custom",
28
+ message: "Capture requires a finite combined output budget"
29
+ });
30
+ if (input.stdin && input.stdin.byteLength > input.maxStdinBytes)
31
+ ctx.addIssue({ code: "custom", message: "stdin exceeds maxStdinBytes" });
32
+ });
33
+ var NativeCommandResultSchema = z.object({
34
+ exitCode: z.number().int().nullable(),
35
+ signal: z.string().nullable(),
36
+ stdout: z.instanceof(Uint8Array),
37
+ stderr: z.instanceof(Uint8Array)
38
+ });
39
+
40
+ class NativeCommandError2 extends Error {
41
+ code;
42
+ constructor(code, message, options) {
43
+ super(message, options);
44
+ this.code = code;
45
+ this.name = "NativeCommandError";
46
+ }
47
+ }
48
+
49
+ // src/process/command-owner.ts
50
+ import { spawn } from "node:child_process";
51
+
52
+ // src/process/group.ts
53
+ function signalGroup(pid, signal) {
54
+ if (pid === undefined)
55
+ return;
56
+ try {
57
+ process.kill(-pid, signal);
58
+ } catch (error) {
59
+ if (!(error instanceof Error && ("code" in error) && error.code === "ESRCH"))
60
+ throw error;
61
+ }
62
+ }
63
+ function exists(pid) {
64
+ if (pid === undefined)
65
+ return false;
66
+ try {
67
+ process.kill(-pid, 0);
68
+ return true;
69
+ } catch (error) {
70
+ if (error instanceof Error && "code" in error && error.code === "ESRCH")
71
+ return false;
72
+ throw error;
73
+ }
74
+ }
75
+ async function stopCommandGroup(pid, graceMs) {
76
+ signalGroup(pid, "SIGTERM");
77
+ const until = Date.now() + graceMs;
78
+ while (Date.now() < until && exists(pid))
79
+ await new Promise((resolve) => setTimeout(resolve, Math.min(10, until - Date.now())));
80
+ signalGroup(pid, "SIGKILL");
81
+ }
82
+ function commandCleanupError(cause) {
83
+ return new NativeCommandError2("COMMAND_CLEANUP", "Command cleanup did not complete", {
84
+ cause
85
+ });
86
+ }
87
+ function waitForCommandClose(closed, timeoutMs) {
88
+ return raceAbort(closed, AbortSignal.timeout(timeoutMs));
89
+ }
90
+
91
+ // src/process/command-owner.ts
92
+ function startNativeCommand(input, onSpawn) {
93
+ const options = NativeCommandOptionsSchema.parse(input);
94
+ options.signal?.throwIfAborted();
95
+ if (process.platform === "win32")
96
+ throw new NativeCommandError2("COMMAND_UNAVAILABLE", "POSIX process groups are required");
97
+ const controller = new AbortController;
98
+ const abort = () => controller.abort(options.signal?.reason);
99
+ options.signal?.addEventListener("abort", abort, { once: true });
100
+ if (options.signal?.aborted)
101
+ abort();
102
+ const timer = options.timeoutMs === undefined ? undefined : setTimeout(() => controller.abort(new NativeCommandError2("COMMAND_LIMIT", "Command deadline exceeded")), options.timeoutMs);
103
+ let child;
104
+ try {
105
+ child = spawn(options.executable, options.args, {
106
+ cwd: options.cwd,
107
+ env: options.envPolicy === "ambient" ? { ...process.env, ...options.env } : options.env ?? {},
108
+ detached: true,
109
+ stdio: ["pipe", "pipe", "pipe"]
110
+ });
111
+ } catch (cause) {
112
+ if (timer !== undefined)
113
+ clearTimeout(timer);
114
+ options.signal?.removeEventListener("abort", abort);
115
+ throw new NativeCommandError2("COMMAND_UNAVAILABLE", "Command could not start", { cause });
116
+ }
117
+ const stdout = [];
118
+ const stderr = [];
119
+ let total = 0;
120
+ let finished = false;
121
+ let spawnFailure;
122
+ const closed = new Promise((resolve) => {
123
+ child.once("error", (error) => {
124
+ spawnFailure = new NativeCommandError2("COMMAND_UNAVAILABLE", "Command could not start", {
125
+ cause: error
126
+ });
127
+ controller.abort(spawnFailure);
128
+ });
129
+ child.once("close", (exitCode, signal) => resolve({ exitCode, signal }));
130
+ });
131
+ child.stdin.on("error", (error) => {
132
+ if (!(("code" in error) && error.code === "EPIPE"))
133
+ controller.abort(error);
134
+ });
135
+ const drain = async (stream, channel, captured) => {
136
+ for await (const chunk of stream) {
137
+ controller.signal.throwIfAborted();
138
+ if (!(chunk instanceof Uint8Array))
139
+ throw new TypeError("Expected binary command output");
140
+ total += chunk.byteLength;
141
+ if (options.maxOutputBytes !== undefined && total > options.maxOutputBytes)
142
+ throw new NativeCommandError2("COMMAND_LIMIT", "Command output budget exceeded");
143
+ if (options.capture)
144
+ captured.push(Uint8Array.from(chunk));
145
+ if (options.onOutput)
146
+ await raceAbort(Promise.resolve().then(() => {
147
+ controller.signal.throwIfAborted();
148
+ return options.onOutput?.(chunk, channel, controller.signal);
149
+ }), controller.signal);
150
+ }
151
+ };
152
+ const result = (async () => {
153
+ try {
154
+ onSpawn?.(child);
155
+ child.stdin.end(options.stdin);
156
+ const drains = Promise.all([
157
+ drain(child.stdout, "stdout", stdout),
158
+ drain(child.stderr, "stderr", stderr)
159
+ ]);
160
+ await raceAbort(drains, controller.signal);
161
+ const status = await raceAbort(closed, controller.signal);
162
+ if (spawnFailure !== undefined)
163
+ throw spawnFailure;
164
+ return { ...status, stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr) };
165
+ } catch (error) {
166
+ controller.abort(error);
167
+ child.stdin.destroy();
168
+ child.stdout.destroy();
169
+ child.stderr.destroy();
170
+ try {
171
+ await stopCommandGroup(child.pid, options.killGraceMs);
172
+ await waitForCommandClose(closed, options.cleanupTimeoutMs);
173
+ } catch (cleanup) {
174
+ throw commandCleanupError(new AggregateError([error, cleanup], "Command failed and cleanup failed"));
175
+ }
176
+ throw error;
177
+ } finally {
178
+ finished = true;
179
+ if (timer !== undefined)
180
+ clearTimeout(timer);
181
+ options.signal?.removeEventListener("abort", abort);
182
+ controller.abort(new Error("Command completed"));
183
+ }
184
+ })();
185
+ result.catch(() => {
186
+ return;
187
+ });
188
+ return {
189
+ result,
190
+ async stop() {
191
+ if (!finished)
192
+ controller.abort(new DOMException("Command stopped", "AbortError"));
193
+ await result.then(() => {
194
+ return;
195
+ }, () => {
196
+ return;
197
+ });
198
+ }
199
+ };
200
+ }
201
+
202
+ export { NativeCommandError2, startNativeCommand };
@@ -0,0 +1,25 @@
1
+ // src/internal/abort-race.ts
2
+ function raceAbort(operation, signal) {
3
+ if (signal.aborted) {
4
+ operation.catch(() => {
5
+ return;
6
+ });
7
+ return Promise.reject(signal.reason);
8
+ }
9
+ return new Promise((resolve, reject) => {
10
+ const abort = () => {
11
+ signal.removeEventListener("abort", abort);
12
+ reject(signal.reason);
13
+ };
14
+ signal.addEventListener("abort", abort, { once: true });
15
+ operation.then((value) => {
16
+ signal.removeEventListener("abort", abort);
17
+ resolve(value);
18
+ }, (error) => {
19
+ signal.removeEventListener("abort", abort);
20
+ reject(error);
21
+ });
22
+ });
23
+ }
24
+
25
+ export { raceAbort };
@@ -2,15 +2,19 @@ import {
2
2
  ManagedFilePathSchema2,
3
3
  ManagedFileRefSchema2
4
4
  } from "./index-xqhede6x.js";
5
+ import {
6
+ AtomicFilePublicationError2,
7
+ publishAtomicFile
8
+ } from "./index-8a9ksm91.js";
5
9
  import {
6
10
  isWithinDir2
7
11
  } from "./index-5cc6r990.js";
8
12
 
9
13
  // src/files/boundary.ts
10
14
  import { randomUUID } from "node:crypto";
11
- import { constants } from "node:fs";
12
- import { link, mkdir, open, realpath, rename, stat, unlink } from "node:fs/promises";
13
- import { basename, dirname, resolve } from "node:path";
15
+ import { constants as constants2 } from "node:fs";
16
+ import { mkdir, open as open2, realpath, stat, unlink } from "node:fs/promises";
17
+ import { basename as basename2, dirname, resolve } from "node:path";
14
18
 
15
19
  // src/files/file-io.ts
16
20
  var ManagedFileInspectionSchema = ManagedFileRefSchema2.pick({
@@ -70,22 +74,29 @@ function inspectedRef(path, size, inspection, fallback = {}) {
70
74
  async function readHandle(handle, maxBytes, signal) {
71
75
  const chunks = [];
72
76
  let total = 0;
73
- while (true) {
74
- signal?.throwIfAborted();
77
+ let ended = false;
78
+ while (!ended) {
75
79
  const capacity = Math.min(64 * 1024, maxBytes + 1 - total);
76
- if (capacity <= 0) {
80
+ if (capacity <= 0)
77
81
  throw new ManagedFileError2("FILE_TOO_LARGE", `file exceeds the ${maxBytes}-byte cap`);
78
- }
79
82
  const chunk = new Uint8Array(capacity);
80
- const { bytesRead } = await handle.read(chunk, 0, capacity);
81
- if (bytesRead === 0)
82
- break;
83
- total += bytesRead;
84
- if (total > maxBytes) {
85
- throw new ManagedFileError2("FILE_TOO_LARGE", `file exceeds the ${maxBytes}-byte cap`);
83
+ let filled = 0;
84
+ while (filled < capacity) {
85
+ signal?.throwIfAborted();
86
+ const { bytesRead } = await handle.read(chunk, filled, capacity - filled);
87
+ if (bytesRead === 0) {
88
+ ended = true;
89
+ break;
90
+ }
91
+ filled += bytesRead;
92
+ total += bytesRead;
93
+ if (total > maxBytes)
94
+ throw new ManagedFileError2("FILE_TOO_LARGE", `file exceeds the ${maxBytes}-byte cap`);
86
95
  }
87
- chunks.push(chunk.subarray(0, bytesRead));
96
+ if (filled > 0)
97
+ chunks.push(chunk.subarray(0, filled));
88
98
  }
99
+ signal?.throwIfAborted();
89
100
  const bytes = new Uint8Array(total);
90
101
  let offset = 0;
91
102
  for (const chunk of chunks) {
@@ -149,13 +160,77 @@ async function writeSource(handle, source, maxBytes, inspectionBytes, signal) {
149
160
  return { size, prefix };
150
161
  }
151
162
 
163
+ // src/files/read.ts
164
+ import { constants } from "node:fs";
165
+ import { open } from "node:fs/promises";
166
+ import { basename } from "node:path";
167
+ async function readManagedDescriptor(target, path, maxBytes, options, inspection, openDescriptor = open) {
168
+ if (options.rejectSymlinks && !constants.O_NOFOLLOW)
169
+ throw new ManagedFileError2("FILE_UNSUPPORTED", "strict leaf nofollow is unavailable");
170
+ const flags = constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0);
171
+ const handle = await openDescriptor(target, flags);
172
+ let failed = false;
173
+ let failure;
174
+ let source;
175
+ try {
176
+ options.signal?.throwIfAborted();
177
+ const before = await handle.stat();
178
+ if (!before.isFile())
179
+ throw new ManagedFileError2("FILE_NOT_REGULAR", "managed path is not a regular file");
180
+ if (options.singleLink && before.nlink !== 1)
181
+ throw new ManagedFileError2("FILE_UNSAFE_LINK", "managed file has multiple links");
182
+ if (before.size > maxBytes)
183
+ throw new ManagedFileError2("FILE_TOO_LARGE", `file exceeds the ${maxBytes}-byte cap`);
184
+ const bytes = await readHandle(handle, maxBytes, options.signal);
185
+ const after = options.stable || options.observe ? await handle.stat() : before;
186
+ if (options.stable && (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || before.nlink !== after.nlink || bytes.length !== after.size))
187
+ throw new ManagedFileError2("FILE_CHANGED", "managed file changed during read");
188
+ options.signal?.throwIfAborted();
189
+ const inspected = await inspectFile(inspection.inspect, {
190
+ prefix: bytes.slice(0, inspection.bytes),
191
+ name: basename(path)
192
+ }, inspection.timeoutMs, options.signal);
193
+ source = {
194
+ ref: inspectedRef(path, bytes.byteLength, inspected),
195
+ bytes,
196
+ ...options.observe ? {
197
+ observation: {
198
+ dev: after.dev,
199
+ ino: after.ino,
200
+ size: after.size,
201
+ nlink: after.nlink,
202
+ mtimeMs: after.mtimeMs,
203
+ ctimeMs: after.ctimeMs
204
+ }
205
+ } : {}
206
+ };
207
+ } catch (error) {
208
+ failed = true;
209
+ failure = error;
210
+ } finally {
211
+ try {
212
+ await handle.close();
213
+ } catch (error) {
214
+ if (!failed) {
215
+ failed = true;
216
+ failure = error;
217
+ }
218
+ }
219
+ }
220
+ if (failed)
221
+ throw failure;
222
+ if (source === undefined)
223
+ throw new Error("Read completed without a result");
224
+ return source;
225
+ }
226
+
152
227
  // src/files/boundary.ts
153
228
  var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
154
229
  var DEFAULT_INSPECTION_BYTES = 64 * 1024;
155
230
  var DEFAULT_INSPECTION_TIMEOUT_MS = 15000;
156
231
  var nofollowFlag;
157
232
  var noFollow = () => {
158
- nofollowFlag ??= constants.O_NOFOLLOW ?? 0;
233
+ nofollowFlag ??= constants2.O_NOFOLLOW ?? 0;
159
234
  return nofollowFlag;
160
235
  };
161
236
 
@@ -271,41 +346,35 @@ async function createManagedFileBoundary2(config) {
271
346
  async read(path, options = {}) {
272
347
  const resolved = targetFor(path);
273
348
  options.signal?.throwIfAborted();
274
- const realTarget = await realpathOrNull(resolved.target, "failed to resolve managed file");
275
- if (realTarget === null) {
276
- throw new ManagedFileError2("FILE_NOT_FOUND", "managed file not found");
277
- }
278
- if (!isWithinDir2(root, realTarget)) {
279
- throw new ManagedFileError2("FILE_OUTSIDE_ROOT", "managed-file path escapes its boundary");
349
+ let readTarget;
350
+ if (options.rejectSymlinks) {
351
+ const parent = await existingParent(root, resolved.target);
352
+ readTarget = resolve(parent, basename2(resolved.target));
353
+ } else {
354
+ const realTarget = await realpathOrNull(resolved.target, "failed to resolve managed file");
355
+ if (realTarget === null)
356
+ throw new ManagedFileError2("FILE_NOT_FOUND", "managed file not found");
357
+ if (!isWithinDir2(root, realTarget))
358
+ throw new ManagedFileError2("FILE_OUTSIDE_ROOT", "managed-file path escapes its boundary");
359
+ readTarget = realTarget;
280
360
  }
281
361
  const maxBytes = positiveLimit(options.maxBytes, maxReadBytes, "maxBytes");
282
- let handle;
283
362
  try {
284
- handle = await open(realTarget, constants.O_RDONLY | noFollow());
285
- const info = await handle.stat();
286
- if (!info.isFile()) {
287
- throw new ManagedFileError2("FILE_NOT_REGULAR", "managed path is not a regular file");
288
- }
289
- const bytes = await readHandle(handle, maxBytes, options.signal);
290
- const inspection = await inspectFile(config.inspect, {
291
- prefix: bytes.slice(0, inspectionBytes),
292
- name: basename(resolved.path)
293
- }, inspectionTimeoutMs, options.signal);
294
- return {
295
- ref: inspectedRef(resolved.path, bytes.byteLength, inspection),
296
- bytes
297
- };
363
+ return await readManagedDescriptor(readTarget, resolved.path, maxBytes, options, {
364
+ inspect: config.inspect,
365
+ bytes: inspectionBytes,
366
+ timeoutMs: inspectionTimeoutMs
367
+ });
298
368
  } catch (error) {
299
- if (options.signal?.aborted && error === options.signal.reason)
300
- throw error;
301
- if (errorCode(error) === "ENOENT") {
369
+ if (options.signal?.aborted)
370
+ throw options.signal.reason;
371
+ if (errorCode(error) === "ELOOP")
372
+ throw new ManagedFileError2("FILE_UNSAFE_LINK", "managed file leaf is a symlink", {
373
+ cause: error
374
+ });
375
+ if (errorCode(error) === "ENOENT")
302
376
  throw new ManagedFileError2("FILE_NOT_FOUND", "managed file not found");
303
- }
304
377
  throw ioError("failed to read managed file", error);
305
- } finally {
306
- await handle?.close().catch(() => {
307
- return;
308
- });
309
378
  }
310
379
  },
311
380
  async write(path, source, options = {}) {
@@ -318,7 +387,7 @@ async function createManagedFileBoundary2(config) {
318
387
  let committed = false;
319
388
  let size = 0;
320
389
  try {
321
- handle = await open(temporary, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | noFollow(), 384);
390
+ handle = await open2(temporary, constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY | noFollow(), 384);
322
391
  const written = await writeSource(handle, source, maxBytes, inspectionBytes, options.signal);
323
392
  size = written.size;
324
393
  if (options.durable)
@@ -328,28 +397,28 @@ async function createManagedFileBoundary2(config) {
328
397
  const inspection = await inspectFile(config.inspect, {
329
398
  prefix: written.prefix,
330
399
  ...options.mediaType ? { declaredMediaType: options.mediaType } : {},
331
- name: options.name ?? basename(resolved.path)
400
+ name: options.name ?? basename2(resolved.path)
332
401
  }, inspectionTimeoutMs, options.signal);
333
402
  options.signal?.throwIfAborted();
334
- if (options.replace) {
335
- await rename(temporary, resolved.target);
403
+ try {
404
+ await publishAtomicFile(temporary, resolved.target, options.replace ?? false, options.durable ?? false);
336
405
  committed = true;
337
- } else {
338
- try {
339
- await link(temporary, resolved.target);
406
+ } catch (error) {
407
+ if (error instanceof AtomicFilePublicationError2) {
340
408
  committed = true;
341
- } catch (error) {
342
- if (errorCode(error) === "EEXIST") {
343
- throw new ManagedFileError2("FILE_EXISTS", "managed file already exists");
344
- }
345
409
  throw error;
346
410
  }
411
+ if (errorCode(error) === "EEXIST")
412
+ throw new ManagedFileError2("FILE_EXISTS", "managed file already exists");
413
+ throw error;
347
414
  }
348
415
  return inspectedRef(resolved.path, size, inspection, {
349
416
  mediaType: options.mediaType,
350
417
  name: options.name
351
418
  });
352
419
  } catch (error) {
420
+ if (error instanceof AtomicFilePublicationError2)
421
+ throw error;
353
422
  if (options.signal?.aborted && error === options.signal.reason)
354
423
  throw error;
355
424
  throw ioError("failed to write managed file", error);
@@ -0,0 +1,3 @@
1
+ /** Releases an await on cancellation without assuming a callback cooperates with its signal. */
2
+ export declare function raceAbort<T>(operation: Promise<T>, signal: AbortSignal): Promise<T>;
3
+ //# sourceMappingURL=abort-race.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort-race.d.ts","sourceRoot":"","sources":["../../src/internal/abort-race.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,wBAAgB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAsBnF"}
@@ -29,14 +29,32 @@ export interface WriteFileAtomicOptions {
29
29
  * caller states, never one a default makes for them.
30
30
  */
31
31
  mode?: number;
32
+ /** Replace by default; false atomically refuses any existing file or link. */
33
+ replace?: boolean;
34
+ /** Default file: fsync bytes. directory also syncs the parent after publication. */
35
+ durability?: 'none' | 'file' | 'directory';
32
36
  }
33
37
  /**
34
38
  * Write `data` to `target` atomically: a reader sees the old contents or the
35
39
  * new ones, never part of either, and the new file has `mode` from its first
36
- * visible moment. A failed write leaves the target untouched and no staging
37
- * file behind.
40
+ * visible moment. A failure before publication leaves the target untouched. Directory-sync or
41
+ * staging-link cleanup failure after publication throws AtomicFilePublicationError
42
+ * with published=true; do not blindly repeat an external action.
38
43
  */
39
44
  export declare function writeFileAtomic(target: string, data: Uint8Array | string, options?: WriteFileAtomicOptions): Promise<void>;
45
+ interface AtomicFileWriteIO {
46
+ open(path: string, flags: 'wx', mode: number): Promise<{
47
+ writeFile(bytes: Uint8Array): Promise<void>;
48
+ chmod(mode: number): Promise<void>;
49
+ sync(): Promise<void>;
50
+ close(): Promise<void>;
51
+ }>;
52
+ unlink(path: string): Promise<void>;
53
+ publish(staged: string, target: string, replace: boolean, directorySync: boolean): Promise<void>;
54
+ }
55
+ /** Internal syscall seam for publication-order and precommit fault controls. */
56
+ export declare function writeAtomicFileData(target: string, data: Uint8Array | string, options: WriteFileAtomicOptions, io?: AtomicFileWriteIO): Promise<void>;
40
57
  /** The synchronous form of {@link writeFileAtomic}, with the same guarantees in the same order. */
41
58
  export declare function writeFileAtomicSync(target: string, data: Uint8Array | string, options?: WriteFileAtomicOptions): void;
59
+ export {};
42
60
  //# sourceMappingURL=atomic-file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"atomic-file.d.ts","sourceRoot":"","sources":["../../src/internal/atomic-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAeH,0EAA0E;AAC1E,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAYD;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,mGAAmG;AACnG,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,OAAO,GAAE,sBAA2B,GACnC,IAAI,CA6BN"}
1
+ {"version":3,"file":"atomic-file.d.ts","sourceRoot":"","sources":["../../src/internal/atomic-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAQH,0EAA0E;AAC1E,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC;CAC5C;AAYD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,UAAU,iBAAiB;IACzB,IAAI,CACF,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,IAAI,EACX,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QACT,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5C,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;KACxB,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,CACL,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,OAAO,GACrB,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAED,gFAAgF;AAChF,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,OAAO,EAAE,sBAAsB,EAC/B,EAAE,GAAE,iBAAgE,GACnE,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED,mGAAmG;AACnG,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,OAAO,GAAE,sBAA2B,GACnC,IAAI,CAiDN"}
@@ -0,0 +1,19 @@
1
+ /** The new target is visible; failure is not evidence that publication did not occur. */
2
+ export declare class AtomicFilePublicationError extends Error {
3
+ readonly phase: 'cleanup' | 'directory-sync' | 'directory-close';
4
+ readonly published = true;
5
+ constructor(phase: 'cleanup' | 'directory-sync' | 'directory-close', cause: unknown);
6
+ }
7
+ /** Native publication syscalls; injectable only at this internal IO boundary for fault controls. */
8
+ export interface PublicationIO {
9
+ rename(from: string, to: string): Promise<void>;
10
+ link(from: string, to: string): Promise<void>;
11
+ unlink(path: string): Promise<void>;
12
+ open(path: string, flags: 'r'): Promise<{
13
+ sync(): Promise<void>;
14
+ close(): Promise<void>;
15
+ }>;
16
+ }
17
+ export declare function publishAtomicFile(staged: string, target: string, replace: boolean, directorySync: boolean, io?: PublicationIO): Promise<void>;
18
+ export declare function publishAtomicFileSync(staged: string, target: string, replace: boolean, directorySync: boolean): void;
19
+ //# sourceMappingURL=atomic-publication.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-publication.d.ts","sourceRoot":"","sources":["../../src/internal/atomic-publication.ts"],"names":[],"mappings":"AAIA,yFAAyF;AACzF,qBAAa,0BAA2B,SAAQ,KAAK;aAGjC,KAAK,EAAE,SAAS,GAAG,gBAAgB,GAAG,iBAAiB;IAFzE,QAAQ,CAAC,SAAS,QAAQ;IAC1B,YACkB,KAAK,EAAE,SAAS,GAAG,gBAAgB,GAAG,iBAAiB,EACvE,KAAK,EAAE,OAAO,EAIf;CACF;AAED,oGAAoG;AACpG,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC;QAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC,CAAC;CAC5F;AAGD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,OAAO,EACtB,EAAE,GAAE,aAAsB,GACzB,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,OAAO,GACrB,IAAI,CAmCN"}
@@ -0,0 +1,8 @@
1
+ /** Validation shared by durable JSON and canonical JSON; serialization has one separate owner. */
2
+ export declare function isJsonData(value: unknown, limits: {
3
+ maxDepth: number;
4
+ maxNodes: number;
5
+ maxBytes: number;
6
+ omitObjectUndefined: boolean;
7
+ }): boolean;
8
+ //# sourceMappingURL=json-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-data.d.ts","sourceRoot":"","sources":["../../src/internal/json-data.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,wBAAgB,UAAU,CACxB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE;IACN,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,GACA,OAAO,CA0DT"}
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+ declare const CanonicalJsonOptionsSchema: z.ZodObject<{
3
+ maxDepth: z.ZodDefault<z.ZodNumber>;
4
+ maxNodes: z.ZodDefault<z.ZodNumber>;
5
+ maxBytes: z.ZodDefault<z.ZodNumber>;
6
+ }, z.core.$strict>;
7
+ export type CanonicalJsonOptions = z.input<typeof CanonicalJsonOptionsSchema>;
8
+ /** Bounded plain JSON, UTF-16 sorted keys; undefined object members are omitted. */
9
+ export declare function canonicalJson(value: unknown, options?: CanonicalJsonOptions): string;
10
+ export {};
11
+ //# sourceMappingURL=canonical-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-json.d.ts","sourceRoot":"","sources":["../../src/primitives/canonical-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,QAAA,MAAM,0BAA0B;;;;kBAQ9B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAOxF"}
@@ -1,4 +1,5 @@
1
1
  export { type AuditOmitPolicy, type AuditPolicy, type AuditRecord, type AuditRecordPolicy, AuditRecordSchema, assertAuditDeclared, audit, type CreateAuditRecordInput, createAuditRecord, } from './audit.js';
2
+ export { type CanonicalJsonOptions, canonicalJson } from './canonical-json.js';
2
3
  export { type DeadlineResult, DeadlineResultSchema, defineDeadlinePolicy, } from './deadline.js';
3
4
  export { type DomainEventDeliveryClaim, DomainEventDeliveryClaimSchema, type DomainEventDeliveryOutcome, DomainEventDeliveryOutcomeSchema, type DomainEventDeliveryPlan, type DomainEventDestination, DomainEventDestinationSchema, type DomainEventDispatchResult, type DomainEventOutbox, type DomainEventRoute, type DomainEventTransport, defineDomainEventDelivery, } from './delivery.js';
4
5
  export { createDomainEventSchema, type DomainEvent, type DomainEventActor, DomainEventActorSchema, DomainEventSchema, type DomainEventSubject, DomainEventSubjectSchema, } from './event.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/primitives/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,EACL,KAAK,sBAAsB,EAC3B,iBAAiB,GAClB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,cAAc,EACnB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,wBAAwB,EAC7B,8BAA8B,EAC9B,KAAK,0BAA0B,EAC/B,gCAAgC,EAChC,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,uBAAuB,EACvB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,wBAAwB,GACzB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,8BAA8B,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,GAChC,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,UAAU,EACf,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,QAAQ,EACR,iBAAiB,EACjB,WAAW,EACX,KAAK,KAAK,EACV,KAAK,UAAU,EACf,KAAK,UAAU,EACf,aAAa,EACb,UAAU,EACV,UAAU,EACV,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,sBAAsB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,wBAAwB,EACxB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/primitives/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,EACL,KAAK,sBAAsB,EAC3B,iBAAiB,GAClB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EACL,KAAK,cAAc,EACnB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,wBAAwB,EAC7B,8BAA8B,EAC9B,KAAK,0BAA0B,EAC/B,gCAAgC,EAChC,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,uBAAuB,EACvB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,wBAAwB,GACzB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,8BAA8B,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,GAChC,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,UAAU,EACf,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,QAAQ,EACR,iBAAiB,EACjB,WAAW,EACX,KAAK,KAAK,EACV,KAAK,UAAU,EACf,KAAK,UAAU,EACf,aAAa,EACb,UAAU,EACV,UAAU,EACV,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,sBAAsB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,wBAAwB,EACxB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type ChildProcessWithoutNullStreams } from 'node:child_process';
2
+ import { type NativeCommandOptions } from './contract.js';
3
+ /** One-shot native execution; sandbox policy/admission is composed above this owner. */
4
+ export declare function startNativeCommand(input: NativeCommandOptions, onSpawn?: (child: ChildProcessWithoutNullStreams) => void): {
5
+ result: Promise<{
6
+ exitCode: number | null;
7
+ signal: string | null;
8
+ stdout: Uint8Array<ArrayBuffer>;
9
+ stderr: Uint8Array<ArrayBuffer>;
10
+ }>;
11
+ stop(): Promise<void>;
12
+ };
13
+ //# sourceMappingURL=command-owner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-owner.d.ts","sourceRoot":"","sources":["../../src/process/command-owner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,8BAA8B,EAAS,MAAM,oBAAoB,CAAC;AAGhF,OAAO,EAEL,KAAK,oBAAoB,EAG1B,MAAM,YAAY,CAAC;AAGpB,wFAAwF;AACxF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,oBAAoB,EAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,IAAI;;;;;;;;EA0H1D"}
@@ -0,0 +1,3 @@
1
+ import type { NativeCommandOptions, NativeCommandResult } from './contract.js';
2
+ export declare function runNativeCommand(input: NativeCommandOptions): Promise<NativeCommandResult>;
3
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/process/command.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAE1F"}