@ny4n/durable 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +711 -2
- package/dist/documents.d.ts +66 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +98 -0
- package/dist/documents.js.map +1 -0
- package/dist/entries.d.ts +31 -0
- package/dist/entries.d.ts.map +1 -0
- package/dist/entries.js +31 -0
- package/dist/entries.js.map +1 -0
- package/dist/env/decode.d.ts +16 -0
- package/dist/env/decode.d.ts.map +1 -0
- package/dist/env/decode.js +28 -0
- package/dist/env/decode.js.map +1 -0
- package/dist/env/index.d.ts +263 -0
- package/dist/env/index.d.ts.map +1 -0
- package/dist/env/index.js +49 -0
- package/dist/env/index.js.map +1 -0
- package/dist/env/line-scan.d.ts +13 -0
- package/dist/env/line-scan.d.ts.map +1 -0
- package/dist/env/line-scan.js +147 -0
- package/dist/env/line-scan.js.map +1 -0
- package/dist/env/node-watch.d.ts +29 -0
- package/dist/env/node-watch.d.ts.map +1 -0
- package/dist/env/node-watch.js +485 -0
- package/dist/env/node-watch.js.map +1 -0
- package/dist/env/node.d.ts +56 -0
- package/dist/env/node.d.ts.map +1 -0
- package/dist/env/node.js +1166 -0
- package/dist/env/node.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +53 -0
- package/dist/errors.js.map +1 -0
- package/dist/harness/agent.d.ts +33 -0
- package/dist/harness/agent.d.ts.map +1 -0
- package/dist/harness/agent.js +235 -0
- package/dist/harness/agent.js.map +1 -0
- package/dist/harness/assistant.d.ts +15 -0
- package/dist/harness/assistant.d.ts.map +1 -0
- package/dist/harness/assistant.js +23 -0
- package/dist/harness/assistant.js.map +1 -0
- package/dist/harness/compaction.d.ts +56 -0
- package/dist/harness/compaction.d.ts.map +1 -0
- package/dist/harness/compaction.js +384 -0
- package/dist/harness/compaction.js.map +1 -0
- package/dist/harness/context.d.ts +63 -0
- package/dist/harness/context.d.ts.map +1 -0
- package/dist/harness/context.js +221 -0
- package/dist/harness/context.js.map +1 -0
- package/dist/harness/define.d.ts +18 -0
- package/dist/harness/define.d.ts.map +1 -0
- package/dist/harness/define.js +25 -0
- package/dist/harness/define.js.map +1 -0
- package/dist/harness/events.d.ts +180 -0
- package/dist/harness/events.d.ts.map +1 -0
- package/dist/harness/events.js +370 -0
- package/dist/harness/events.js.map +1 -0
- package/dist/harness/generation.d.ts +54 -0
- package/dist/harness/generation.d.ts.map +1 -0
- package/dist/harness/generation.js +514 -0
- package/dist/harness/generation.js.map +1 -0
- package/dist/harness/harness.d.ts +17 -0
- package/dist/harness/harness.d.ts.map +1 -0
- package/dist/harness/harness.js +332 -0
- package/dist/harness/harness.js.map +1 -0
- package/dist/harness/inbox.d.ts +59 -0
- package/dist/harness/inbox.d.ts.map +1 -0
- package/dist/harness/inbox.js +97 -0
- package/dist/harness/inbox.js.map +1 -0
- package/dist/harness/json.d.ts +10 -0
- package/dist/harness/json.d.ts.map +1 -0
- package/dist/harness/json.js +33 -0
- package/dist/harness/json.js.map +1 -0
- package/dist/harness/live.d.ts +142 -0
- package/dist/harness/live.d.ts.map +1 -0
- package/dist/harness/live.js +149 -0
- package/dist/harness/live.js.map +1 -0
- package/dist/harness/output.d.ts +69 -0
- package/dist/harness/output.d.ts.map +1 -0
- package/dist/harness/output.js +318 -0
- package/dist/harness/output.js.map +1 -0
- package/dist/harness/prompt.d.ts +27 -0
- package/dist/harness/prompt.d.ts.map +1 -0
- package/dist/harness/prompt.js +131 -0
- package/dist/harness/prompt.js.map +1 -0
- package/dist/harness/provider.d.ts +14 -0
- package/dist/harness/provider.d.ts.map +1 -0
- package/dist/harness/provider.js +30 -0
- package/dist/harness/provider.js.map +1 -0
- package/dist/harness/registry.d.ts +6 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +101 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/run.d.ts +17 -0
- package/dist/harness/run.d.ts.map +1 -0
- package/dist/harness/run.js +26 -0
- package/dist/harness/run.js.map +1 -0
- package/dist/harness/scheduler.d.ts +106 -0
- package/dist/harness/scheduler.d.ts.map +1 -0
- package/dist/harness/scheduler.js +1743 -0
- package/dist/harness/scheduler.js.map +1 -0
- package/dist/harness/submissions.d.ts +29 -0
- package/dist/harness/submissions.d.ts.map +1 -0
- package/dist/harness/submissions.js +175 -0
- package/dist/harness/submissions.js.map +1 -0
- package/dist/harness/task-graph.d.ts +49 -0
- package/dist/harness/task-graph.d.ts.map +1 -0
- package/dist/harness/task-graph.js +168 -0
- package/dist/harness/task-graph.js.map +1 -0
- package/dist/harness/tool.d.ts +76 -0
- package/dist/harness/tool.d.ts.map +1 -0
- package/dist/harness/tool.js +736 -0
- package/dist/harness/tool.js.map +1 -0
- package/dist/harness/types.d.ts +713 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +2 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/harness/usage.d.ts +18 -0
- package/dist/harness/usage.d.ts.map +1 -0
- package/dist/harness/usage.js +57 -0
- package/dist/harness/usage.js.map +1 -0
- package/dist/harness/util.d.ts +19 -0
- package/dist/harness/util.d.ts.map +1 -0
- package/dist/harness/util.js +58 -0
- package/dist/harness/util.js.map +1 -0
- package/dist/harness/view.d.ts +47 -0
- package/dist/harness/view.d.ts.map +1 -0
- package/dist/harness/view.js +206 -0
- package/dist/harness/view.js.map +1 -0
- package/dist/ids.d.ts +6 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +9 -0
- package/dist/ids.js.map +1 -0
- package/dist/images/cloudflare.d.ts +4 -0
- package/dist/images/cloudflare.d.ts.map +1 -0
- package/dist/images/cloudflare.js +9 -0
- package/dist/images/cloudflare.js.map +1 -0
- package/dist/images/exif.d.ts +3 -0
- package/dist/images/exif.d.ts.map +1 -0
- package/dist/images/exif.js +71 -0
- package/dist/images/exif.js.map +1 -0
- package/dist/images/index.d.ts +18 -0
- package/dist/images/index.d.ts.map +1 -0
- package/dist/images/index.js +125 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/node-worker.d.ts +17 -0
- package/dist/images/node-worker.d.ts.map +1 -0
- package/dist/images/node-worker.js +16 -0
- package/dist/images/node-worker.js.map +1 -0
- package/dist/images/node.d.ts +21 -0
- package/dist/images/node.d.ts.map +1 -0
- package/dist/images/node.js +121 -0
- package/dist/images/node.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/session/forks.d.ts +10 -0
- package/dist/session/forks.d.ts.map +1 -0
- package/dist/session/forks.js +42 -0
- package/dist/session/forks.js.map +1 -0
- package/dist/session/observation.d.ts +42 -0
- package/dist/session/observation.d.ts.map +1 -0
- package/dist/session/observation.js +281 -0
- package/dist/session/observation.js.map +1 -0
- package/dist/session/session.d.ts +133 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +811 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/transaction.d.ts +119 -0
- package/dist/session/transaction.d.ts.map +1 -0
- package/dist/session/transaction.js +795 -0
- package/dist/session/transaction.js.map +1 -0
- package/dist/storage/jsonl/index.d.ts +2 -0
- package/dist/storage/jsonl/index.d.ts.map +1 -0
- package/dist/storage/jsonl/index.js +2 -0
- package/dist/storage/jsonl/index.js.map +1 -0
- package/dist/storage/jsonl/node.d.ts +6 -0
- package/dist/storage/jsonl/node.d.ts.map +1 -0
- package/dist/storage/jsonl/node.js +8 -0
- package/dist/storage/jsonl/node.js.map +1 -0
- package/dist/storage/jsonl/storage.d.ts +143 -0
- package/dist/storage/jsonl/storage.d.ts.map +1 -0
- package/dist/storage/jsonl/storage.js +682 -0
- package/dist/storage/jsonl/storage.js.map +1 -0
- package/dist/storage/memory.d.ts +62 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +733 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/scan.d.ts +18 -0
- package/dist/storage/scan.d.ts.map +1 -0
- package/dist/storage/scan.js +29 -0
- package/dist/storage/scan.js.map +1 -0
- package/dist/storage/sqlite/cloudflare.d.ts +46 -0
- package/dist/storage/sqlite/cloudflare.d.ts.map +1 -0
- package/dist/storage/sqlite/cloudflare.js +106 -0
- package/dist/storage/sqlite/cloudflare.js.map +1 -0
- package/dist/storage/sqlite/database.d.ts +37 -0
- package/dist/storage/sqlite/database.d.ts.map +1 -0
- package/dist/storage/sqlite/database.js +2 -0
- package/dist/storage/sqlite/database.js.map +1 -0
- package/dist/storage/sqlite/index.d.ts +4 -0
- package/dist/storage/sqlite/index.d.ts.map +1 -0
- package/dist/storage/sqlite/index.js +3 -0
- package/dist/storage/sqlite/index.js.map +1 -0
- package/dist/storage/sqlite/migrations.d.ts +11 -0
- package/dist/storage/sqlite/migrations.d.ts.map +1 -0
- package/dist/storage/sqlite/migrations.js +111 -0
- package/dist/storage/sqlite/migrations.js.map +1 -0
- package/dist/storage/sqlite/node.d.ts +41 -0
- package/dist/storage/sqlite/node.d.ts.map +1 -0
- package/dist/storage/sqlite/node.js +179 -0
- package/dist/storage/sqlite/node.js.map +1 -0
- package/dist/storage/sqlite/storage.d.ts +65 -0
- package/dist/storage/sqlite/storage.d.ts.map +1 -0
- package/dist/storage/sqlite/storage.js +670 -0
- package/dist/storage/sqlite/storage.js.map +1 -0
- package/dist/tasks.d.ts +6 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +5 -0
- package/dist/tasks.js.map +1 -0
- package/dist/testing/assertions.d.ts +5 -0
- package/dist/testing/assertions.d.ts.map +1 -0
- package/dist/testing/assertions.js +25 -0
- package/dist/testing/assertions.js.map +1 -0
- package/dist/testing/env-conformance.d.ts +7 -0
- package/dist/testing/env-conformance.d.ts.map +1 -0
- package/dist/testing/env-conformance.js +431 -0
- package/dist/testing/env-conformance.js.map +1 -0
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +6 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/runner.d.ts +12 -0
- package/dist/testing/runner.d.ts.map +1 -0
- package/dist/testing/runner.js +23 -0
- package/dist/testing/runner.js.map +1 -0
- package/dist/testing/storage-benchmark.d.ts +41 -0
- package/dist/testing/storage-benchmark.d.ts.map +1 -0
- package/dist/testing/storage-benchmark.js +350 -0
- package/dist/testing/storage-benchmark.js.map +1 -0
- package/dist/testing/storage-conformance.d.ts +4 -0
- package/dist/testing/storage-conformance.d.ts.map +1 -0
- package/dist/testing/storage-conformance.js +1213 -0
- package/dist/testing/storage-conformance.js.map +1 -0
- package/dist/testing/types.d.ts +37 -0
- package/dist/testing/types.d.ts.map +1 -0
- package/dist/testing/types.js +2 -0
- package/dist/testing/types.js.map +1 -0
- package/dist/tools/bash.d.ts +55 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +138 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit-diff.d.ts +89 -0
- package/dist/tools/edit-diff.d.ts.map +1 -0
- package/dist/tools/edit-diff.js +386 -0
- package/dist/tools/edit-diff.js.map +1 -0
- package/dist/tools/edit.d.ts +18 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +117 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/env.d.ts +5 -0
- package/dist/tools/env.d.ts.map +1 -0
- package/dist/tools/env.js +7 -0
- package/dist/tools/env.js.map +1 -0
- package/dist/tools/file-mutation-queue.d.ts +9 -0
- package/dist/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/tools/file-mutation-queue.js +52 -0
- package/dist/tools/file-mutation-queue.js.map +1 -0
- package/dist/tools/image-processor.d.ts +47 -0
- package/dist/tools/image-processor.d.ts.map +1 -0
- package/dist/tools/image-processor.js +30 -0
- package/dist/tools/image-processor.js.map +1 -0
- package/dist/tools/image.d.ts +20 -0
- package/dist/tools/image.d.ts.map +1 -0
- package/dist/tools/image.js +178 -0
- package/dist/tools/image.js.map +1 -0
- package/dist/tools/index.d.ts +46 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +23 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/path-utils.d.ts +5 -0
- package/dist/tools/path-utils.d.ts.map +1 -0
- package/dist/tools/path-utils.js +26 -0
- package/dist/tools/path-utils.js.map +1 -0
- package/dist/tools/read.d.ts +25 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +235 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/write.d.ts +10 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +31 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/truncate.d.ts +64 -0
- package/dist/truncate.d.ts.map +1 -0
- package/dist/truncate.js +154 -0
- package/dist/truncate.js.map +1 -0
- package/dist/types.d.ts +932 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +140 -5
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { BACKGROUND_CONTEXT, withAbortSignal } from "@ny4n/chord/context";
|
|
2
|
+
import { getOrThrow, } from "../env/index.js";
|
|
3
|
+
const context = BACKGROUND_CONTEXT;
|
|
4
|
+
const decoder = new TextDecoder();
|
|
5
|
+
const encoder = new TextEncoder();
|
|
6
|
+
function abortedContext() {
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
controller.abort();
|
|
9
|
+
return withAbortSignal(controller.signal, BACKGROUND_CONTEXT);
|
|
10
|
+
}
|
|
11
|
+
function errorCode(result) {
|
|
12
|
+
return result.ok ? undefined : result.error.code;
|
|
13
|
+
}
|
|
14
|
+
async function readAll(env, path, maxEntries) {
|
|
15
|
+
const reader = getOrThrow(await env.openDirReader(path, context));
|
|
16
|
+
const pages = [];
|
|
17
|
+
try {
|
|
18
|
+
for (let page = 0; page < 1000; page++) {
|
|
19
|
+
const next = getOrThrow(await reader.next(maxEntries, context));
|
|
20
|
+
pages.push(next.entries);
|
|
21
|
+
if (next.done)
|
|
22
|
+
return { pages, done: true };
|
|
23
|
+
}
|
|
24
|
+
return { pages, done: false };
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
await reader.close(context);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Whether a change reports `path`: an overflow, or a reported path at or above it. */
|
|
31
|
+
function covers(change, path) {
|
|
32
|
+
if ("overflow" in change)
|
|
33
|
+
return true;
|
|
34
|
+
if (!("paths" in change))
|
|
35
|
+
return false;
|
|
36
|
+
return change.paths.some((reported) => path === reported || path.startsWith(`${reported}/`) || path.startsWith(`${reported}\\`));
|
|
37
|
+
}
|
|
38
|
+
/** Watch `targets` while `run` changes files, with a helper that waits for a change reporting a path. */
|
|
39
|
+
async function watching(env, targets, run) {
|
|
40
|
+
const changes = [];
|
|
41
|
+
const watcher = getOrThrow(await env.watch(targets, (change) => changes.push(change), context));
|
|
42
|
+
try {
|
|
43
|
+
const absolute = async (path) => getOrThrow(await env.absolutePath(path, context));
|
|
44
|
+
await run({
|
|
45
|
+
changes,
|
|
46
|
+
absolute,
|
|
47
|
+
async expectChange(path, change) {
|
|
48
|
+
const target = await absolute(path);
|
|
49
|
+
const from = changes.length;
|
|
50
|
+
await change();
|
|
51
|
+
const deadline = Date.now() + 3000;
|
|
52
|
+
while (!changes.slice(from).some((entry) => covers(entry, target))) {
|
|
53
|
+
if (Date.now() > deadline) {
|
|
54
|
+
throw new Error(`No change reported ${target}; got ${JSON.stringify(changes.slice(from))}`);
|
|
55
|
+
}
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await watcher.close(context);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Creates runner-independent cases for an `ExecutionEnv`. `withEnv` must call and await its callback exactly once per
|
|
67
|
+
* case with an environment whose `cwd` is a fresh, empty, writable directory.
|
|
68
|
+
*/
|
|
69
|
+
export function createEnvConformance(options) {
|
|
70
|
+
const assert = options.assertions;
|
|
71
|
+
const shell = options.shell ?? ["sh", "-c"];
|
|
72
|
+
const symlinks = options.symlinks ?? true;
|
|
73
|
+
const createCase = (name, test, timeoutMs) => ({
|
|
74
|
+
name,
|
|
75
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
76
|
+
run: () => options.withEnv(test),
|
|
77
|
+
});
|
|
78
|
+
// Watch cases wait up to three seconds per step, longer than test runners allow by default.
|
|
79
|
+
const watchCase = (name, test) => createCase(name, test, 30_000);
|
|
80
|
+
const execCollect = async (env, command, cwd) => {
|
|
81
|
+
const output = { stdout: "", stderr: "" };
|
|
82
|
+
const result = await env.exec(command, {
|
|
83
|
+
...(cwd === undefined ? {} : { cwd }),
|
|
84
|
+
onOutput: (text, _context, info) => {
|
|
85
|
+
output[info.stream] += text;
|
|
86
|
+
},
|
|
87
|
+
}, context);
|
|
88
|
+
return { result, ...output };
|
|
89
|
+
};
|
|
90
|
+
const cases = [
|
|
91
|
+
createCase("binary reader reads byte ranges of the opened file", async (env) => {
|
|
92
|
+
getOrThrow(await env.writeFile("data.txt", "hello world", context));
|
|
93
|
+
const reader = getOrThrow(await env.openBinaryReader("data.txt", undefined, context));
|
|
94
|
+
const info = getOrThrow(await reader.info(context));
|
|
95
|
+
assert.partialDeepEqual(info, { name: "data.txt", kind: "file", size: 11 });
|
|
96
|
+
assert.strictEqual(decoder.decode(getOrThrow(await reader.read(0, 5, context))), "hello");
|
|
97
|
+
assert.strictEqual(decoder.decode(getOrThrow(await reader.read(6, 100, context))), "world");
|
|
98
|
+
assert.strictEqual(getOrThrow(await reader.read(11, 4, context)).length, 0);
|
|
99
|
+
assert.strictEqual(getOrThrow(await reader.read(50, 1, context)).length, 0);
|
|
100
|
+
assert.strictEqual(getOrThrow(await reader.read(3, 0, context)).length, 0);
|
|
101
|
+
assert.strictEqual(errorCode(await reader.read(-1, 1, context)), "invalid");
|
|
102
|
+
assert.strictEqual(errorCode(await reader.read(0, 1.5, context)), "invalid");
|
|
103
|
+
assert.strictEqual(errorCode(await reader.read(0, 1, abortedContext())), "aborted");
|
|
104
|
+
await reader.close(context);
|
|
105
|
+
await reader.close(context);
|
|
106
|
+
assert.strictEqual(errorCode(await reader.read(0, 1, context)), "invalid");
|
|
107
|
+
assert.strictEqual(errorCode(await reader.info(context)), "invalid");
|
|
108
|
+
}),
|
|
109
|
+
createCase("binary reader scans lines like decoding the whole file", async (env) => {
|
|
110
|
+
// A byte-order mark, an invalid sequence before a newline, an empty line, a later U+FEFF, and no final newline.
|
|
111
|
+
const bytes = Uint8Array.from([
|
|
112
|
+
0xef, 0xbb, 0xbf, 0x61, 0x0a, 0xe2, 0x82, 0x0a, 0x0a, 0xef, 0xbb, 0xbf, 0x62, 0x0a, 0xc3, 0xa9,
|
|
113
|
+
]);
|
|
114
|
+
getOrThrow(await env.writeFile("lines.txt", bytes, context));
|
|
115
|
+
const lines = new TextDecoder().decode(bytes).split("\n");
|
|
116
|
+
const reader = getOrThrow(await env.openBinaryReader("lines.txt", undefined, context));
|
|
117
|
+
try {
|
|
118
|
+
const ranges = [
|
|
119
|
+
[0, undefined],
|
|
120
|
+
[0, 1],
|
|
121
|
+
[1, 3],
|
|
122
|
+
[2, 3],
|
|
123
|
+
[3, undefined],
|
|
124
|
+
[4, 9],
|
|
125
|
+
];
|
|
126
|
+
for (const [startLine, endLine] of ranges) {
|
|
127
|
+
const scan = getOrThrow(await reader.scanLines({ startLine, ...(endLine === undefined ? {} : { endLine }) }, context));
|
|
128
|
+
const selected = lines.slice(startLine, endLine);
|
|
129
|
+
const range = (from, to) => new TextDecoder("utf-8", { ignoreBOM: from > 0 }).decode(bytes.subarray(from, to));
|
|
130
|
+
assert.strictEqual(scan.newlines, lines.length - 1);
|
|
131
|
+
assert.strictEqual(range(scan.start, scan.end), selected.join("\n"));
|
|
132
|
+
assert.strictEqual(scan.selectedBytes, encoder.encode(selected.join("\n")).length);
|
|
133
|
+
assert.strictEqual(range(scan.start, scan.firstLineEnd), lines[startLine]);
|
|
134
|
+
assert.strictEqual(scan.firstLineBytes, encoder.encode(lines[startLine]).length);
|
|
135
|
+
}
|
|
136
|
+
assert.partialDeepEqual(getOrThrow(await reader.scanLines({ startLine: 9 }, context)), {
|
|
137
|
+
start: bytes.length,
|
|
138
|
+
end: bytes.length,
|
|
139
|
+
selectedBytes: 0,
|
|
140
|
+
});
|
|
141
|
+
assert.strictEqual(errorCode(await reader.scanLines({ startLine: 2, endLine: 2 }, context)), "invalid");
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
await reader.close(context);
|
|
145
|
+
}
|
|
146
|
+
}),
|
|
147
|
+
createCase("binary reader keeps reading the file it opened after a rename", async (env) => {
|
|
148
|
+
getOrThrow(await env.writeFile("a.txt", "one", context));
|
|
149
|
+
const reader = getOrThrow(await env.openBinaryReader("a.txt", undefined, context));
|
|
150
|
+
try {
|
|
151
|
+
getOrThrow(await env.renameFile("a.txt", "b.txt", context));
|
|
152
|
+
getOrThrow(await env.writeFile("a.txt", "two", context));
|
|
153
|
+
assert.strictEqual(decoder.decode(getOrThrow(await reader.read(0, 10, context))), "one");
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
await reader.close(context);
|
|
157
|
+
}
|
|
158
|
+
}),
|
|
159
|
+
createCase("binary reader refuses directories, missing files and aborted opens", async (env) => {
|
|
160
|
+
getOrThrow(await env.createDir("dir", undefined, context));
|
|
161
|
+
getOrThrow(await env.writeFile("file.txt", "x", context));
|
|
162
|
+
assert.strictEqual(errorCode(await env.openBinaryReader("dir", undefined, context)), "is_directory");
|
|
163
|
+
assert.strictEqual(errorCode(await env.openBinaryReader("missing.txt", undefined, context)), "not_found");
|
|
164
|
+
assert.strictEqual(errorCode(await env.openBinaryReader("file.txt", undefined, abortedContext())), "aborted");
|
|
165
|
+
}),
|
|
166
|
+
createCase("directory reader pages every entry exactly once", async (env) => {
|
|
167
|
+
const names = ["a.txt", "b.txt", "c.txt", "d.txt", "e.txt"];
|
|
168
|
+
for (const name of names)
|
|
169
|
+
getOrThrow(await env.writeFile(name, name, context));
|
|
170
|
+
getOrThrow(await env.createDir("sub", undefined, context));
|
|
171
|
+
const { pages, done } = await readAll(env, ".", 2);
|
|
172
|
+
assert.ok(done, "directory reader reached the end");
|
|
173
|
+
for (const page of pages)
|
|
174
|
+
assert.ok(page.length <= 2, "page within maxEntries");
|
|
175
|
+
const entries = pages.flat();
|
|
176
|
+
assert.deepEqual(entries.map((entry) => entry.name).sort(), [...names, "sub"].sort());
|
|
177
|
+
assert.strictEqual(entries.find((entry) => entry.name === "sub")?.kind, "directory");
|
|
178
|
+
assert.partialDeepEqual(entries.find((entry) => entry.name === "a.txt"), { kind: "file", size: 5 });
|
|
179
|
+
}),
|
|
180
|
+
createCase("directory reader reports the end and refuses use after close", async (env) => {
|
|
181
|
+
getOrThrow(await env.createDir("empty", undefined, context));
|
|
182
|
+
const reader = getOrThrow(await env.openDirReader("empty", context));
|
|
183
|
+
assert.deepEqual(getOrThrow(await reader.next(10, context)), { entries: [], done: true });
|
|
184
|
+
assert.deepEqual(getOrThrow(await reader.next(10, context)), { entries: [], done: true });
|
|
185
|
+
assert.strictEqual(errorCode(await reader.next(0, context)), "invalid");
|
|
186
|
+
assert.strictEqual(errorCode(await reader.next(1, abortedContext())), "aborted");
|
|
187
|
+
await reader.close(context);
|
|
188
|
+
await reader.close(context);
|
|
189
|
+
assert.strictEqual(errorCode(await reader.next(1, context)), "invalid");
|
|
190
|
+
}),
|
|
191
|
+
createCase("directory reader refuses missing paths and files", async (env) => {
|
|
192
|
+
getOrThrow(await env.writeFile("file.txt", "x", context));
|
|
193
|
+
assert.strictEqual(errorCode(await env.openDirReader("missing", context)), "not_found");
|
|
194
|
+
const file = await env.openDirReader("file.txt", context);
|
|
195
|
+
assert.strictEqual(errorCode(file), "not_directory");
|
|
196
|
+
assert.strictEqual(errorCode(await env.openDirReader(".", abortedContext())), "aborted");
|
|
197
|
+
}),
|
|
198
|
+
createCase("directory reader skips entries removed during enumeration", async (env) => {
|
|
199
|
+
getOrThrow(await env.createDir("dir", undefined, context));
|
|
200
|
+
for (const name of ["x", "y", "z"])
|
|
201
|
+
getOrThrow(await env.writeFile(`dir/${name}`, name, context));
|
|
202
|
+
const reader = getOrThrow(await env.openDirReader("dir", context));
|
|
203
|
+
try {
|
|
204
|
+
for (const name of ["x", "y", "z"])
|
|
205
|
+
getOrThrow(await env.remove(`dir/${name}`, undefined, context));
|
|
206
|
+
const entries = [];
|
|
207
|
+
for (let page = 0; page < 10; page++) {
|
|
208
|
+
const next = getOrThrow(await reader.next(10, context));
|
|
209
|
+
entries.push(...next.entries);
|
|
210
|
+
if (next.done)
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
assert.deepEqual(entries, []);
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
await reader.close(context);
|
|
217
|
+
}
|
|
218
|
+
}),
|
|
219
|
+
watchCase("watch reports a missing file's creation, changes, replacement and removal", async (env) => {
|
|
220
|
+
await watching(env, [{ path: "AGENTS.md" }], async ({ expectChange }) => {
|
|
221
|
+
await expectChange("AGENTS.md", async () => {
|
|
222
|
+
getOrThrow(await env.writeFile("AGENTS.md", "one", context));
|
|
223
|
+
});
|
|
224
|
+
await expectChange("AGENTS.md", async () => {
|
|
225
|
+
getOrThrow(await env.writeFile("AGENTS.md", "two!", context));
|
|
226
|
+
});
|
|
227
|
+
// Editors replace a file by renaming a new one over it.
|
|
228
|
+
await expectChange("AGENTS.md", async () => {
|
|
229
|
+
getOrThrow(await env.writeFile("AGENTS.md.tmp", "three", context));
|
|
230
|
+
getOrThrow(await env.renameFile("AGENTS.md.tmp", "AGENTS.md", context));
|
|
231
|
+
});
|
|
232
|
+
await expectChange("AGENTS.md", async () => {
|
|
233
|
+
getOrThrow(await env.writeFile("AGENTS.md", "four", context));
|
|
234
|
+
});
|
|
235
|
+
await expectChange("AGENTS.md", async () => {
|
|
236
|
+
getOrThrow(await env.remove("AGENTS.md", undefined, context));
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}),
|
|
240
|
+
watchCase("watch reports a missing target whose ancestors are created", async (env) => {
|
|
241
|
+
await watching(env, [{ path: "a/b/c/AGENTS.md" }], async ({ expectChange }) => {
|
|
242
|
+
await expectChange("a/b/c/AGENTS.md", async () => {
|
|
243
|
+
getOrThrow(await env.writeFile("a/b/c/AGENTS.md", "x", context));
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
}),
|
|
247
|
+
watchCase("watch follows directories created together with their contents", async (env) => {
|
|
248
|
+
getOrThrow(await env.createDir("skills", undefined, context));
|
|
249
|
+
await watching(env, [{ path: "skills", recursive: true }], async ({ expectChange }) => {
|
|
250
|
+
// Written before any watcher on the new directories can exist.
|
|
251
|
+
await expectChange("skills/a/b/SKILL.md", async () => {
|
|
252
|
+
getOrThrow(await env.writeFile("skills/a/b/SKILL.md", "one", context));
|
|
253
|
+
});
|
|
254
|
+
await expectChange("skills/a/b/SKILL.md", async () => {
|
|
255
|
+
getOrThrow(await env.writeFile("skills/a/b/SKILL.md", "two!", context));
|
|
256
|
+
});
|
|
257
|
+
await expectChange("skills/a/b/c/SKILL.md", async () => {
|
|
258
|
+
getOrThrow(await env.writeFile("skills/a/b/c/SKILL.md", "deeper", context));
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}),
|
|
262
|
+
watchCase("watch keeps watching a path whose parent is renamed and recreated", async (env) => {
|
|
263
|
+
getOrThrow(await env.writeFile("proj/.nyan/skills/x.md", "x", context));
|
|
264
|
+
await watching(env, [{ path: "proj/.nyan/skills", recursive: true }], async ({ expectChange }) => {
|
|
265
|
+
await expectChange("proj/.nyan/skills", async () => {
|
|
266
|
+
getOrThrow(await env.renameFile("proj/.nyan", "proj/old", context));
|
|
267
|
+
});
|
|
268
|
+
await expectChange("proj/.nyan/skills/y.md", async () => {
|
|
269
|
+
getOrThrow(await env.writeFile("proj/.nyan/skills/y.md", "y", context));
|
|
270
|
+
});
|
|
271
|
+
await expectChange("proj/.nyan/skills/y.md", async () => {
|
|
272
|
+
getOrThrow(await env.writeFile("proj/.nyan/skills/y.md", "yy", context));
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
}),
|
|
276
|
+
watchCase("watch skips excluded entries and reports a rename out of them", async (env) => {
|
|
277
|
+
getOrThrow(await env.createDir("skills", undefined, context));
|
|
278
|
+
const targets = [
|
|
279
|
+
{ path: "skills", recursive: true, exclude: { hidden: true, names: ["node_modules"] } },
|
|
280
|
+
];
|
|
281
|
+
await watching(env, targets, async ({ changes, expectChange, absolute }) => {
|
|
282
|
+
getOrThrow(await env.writeFile("skills/node_modules/dep/SKILL.md", "dep", context));
|
|
283
|
+
getOrThrow(await env.writeFile("skills/.SKILL.md.tmp", "draft", context));
|
|
284
|
+
await expectChange("skills/SKILL.md", async () => {
|
|
285
|
+
getOrThrow(await env.renameFile("skills/.SKILL.md.tmp", "skills/SKILL.md", context));
|
|
286
|
+
});
|
|
287
|
+
const hidden = [await absolute("skills/node_modules"), await absolute("skills/.SKILL.md.tmp")];
|
|
288
|
+
for (const change of changes) {
|
|
289
|
+
if (!("paths" in change))
|
|
290
|
+
continue;
|
|
291
|
+
for (const path of change.paths) {
|
|
292
|
+
assert.ok(!hidden.some((excluded) => path === excluded || path.startsWith(excluded)), `excluded ${path}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}),
|
|
297
|
+
watchCase("watch keeps recursive coverage where a non-recursive target overlaps", async (env) => {
|
|
298
|
+
getOrThrow(await env.writeFile("skills/a/one.md", "one", context));
|
|
299
|
+
const targets = [{ path: "skills" }, { path: "skills", recursive: true }];
|
|
300
|
+
await watching(env, targets, async ({ expectChange }) => {
|
|
301
|
+
await expectChange("skills/a/two.md", async () => {
|
|
302
|
+
getOrThrow(await env.writeFile("skills/a/two.md", "two", context));
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
}),
|
|
306
|
+
watchCase("watch follows a directory replaced at the same path", async (env) => {
|
|
307
|
+
getOrThrow(await env.writeFile("skills/a/x.md", "x", context));
|
|
308
|
+
await watching(env, [{ path: "skills", recursive: true }], async ({ expectChange }) => {
|
|
309
|
+
await expectChange("skills/a", async () => {
|
|
310
|
+
getOrThrow(await env.renameFile("skills/a", "skills-old", context));
|
|
311
|
+
getOrThrow(await env.createDir("skills/a", undefined, context));
|
|
312
|
+
});
|
|
313
|
+
await expectChange("skills/a/y.md", async () => {
|
|
314
|
+
getOrThrow(await env.writeFile("skills/a/y.md", "y", context));
|
|
315
|
+
});
|
|
316
|
+
await expectChange("skills/a/y.md", async () => {
|
|
317
|
+
getOrThrow(await env.writeFile("skills/a/y.md", "yy", context));
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
}),
|
|
321
|
+
watchCase("watch stops reporting once closed", async (env) => {
|
|
322
|
+
const changes = [];
|
|
323
|
+
const watcher = getOrThrow(await env.watch([{ path: "file.txt" }], (change) => changes.push(change), context));
|
|
324
|
+
assert.ok(watcher.mode === "native" || watcher.mode === "polling", "watcher reports its mode");
|
|
325
|
+
await watcher.close(context);
|
|
326
|
+
await watcher.close(context);
|
|
327
|
+
getOrThrow(await env.writeFile("file.txt", "x", context));
|
|
328
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
329
|
+
assert.deepEqual(changes, []);
|
|
330
|
+
}),
|
|
331
|
+
createCase("argv exec passes arguments to the program without shell parsing", async (env) => {
|
|
332
|
+
const hostile = "it's $(touch pwned) `touch pwned` *; touch pwned";
|
|
333
|
+
const { result, stdout } = await execCollect(env, [
|
|
334
|
+
...shell,
|
|
335
|
+
'printf "%s|%s" "$1" "$2"',
|
|
336
|
+
"argv0",
|
|
337
|
+
hostile,
|
|
338
|
+
"a b",
|
|
339
|
+
]);
|
|
340
|
+
assert.strictEqual(getOrThrow(result).exitCode, 0);
|
|
341
|
+
assert.strictEqual(stdout, `${hostile}|a b`);
|
|
342
|
+
assert.strictEqual(getOrThrow(await env.exists("pwned", context)), false);
|
|
343
|
+
}),
|
|
344
|
+
createCase("exec reports the stream of every chunk in both forms", async (env) => {
|
|
345
|
+
const script = "printf out; printf err >&2; printf more";
|
|
346
|
+
const argv = await execCollect(env, [...shell, script]);
|
|
347
|
+
assert.strictEqual(getOrThrow(argv.result).exitCode, 0);
|
|
348
|
+
assert.strictEqual(argv.stdout, "outmore");
|
|
349
|
+
assert.strictEqual(argv.stderr, "err");
|
|
350
|
+
const string = await execCollect(env, script);
|
|
351
|
+
assert.strictEqual(getOrThrow(string.result).exitCode, 0);
|
|
352
|
+
assert.strictEqual(string.stdout, "outmore");
|
|
353
|
+
assert.strictEqual(string.stderr, "err");
|
|
354
|
+
}),
|
|
355
|
+
createCase("argv exec honors cwd and exit codes", async (env) => {
|
|
356
|
+
getOrThrow(await env.createDir("sub", undefined, context));
|
|
357
|
+
const made = await execCollect(env, [...shell, "printf x > made.txt; exit 3"], "sub");
|
|
358
|
+
assert.strictEqual(getOrThrow(made.result).exitCode, 3);
|
|
359
|
+
assert.strictEqual(getOrThrow(await env.readTextFile("sub/made.txt", context)), "x");
|
|
360
|
+
}),
|
|
361
|
+
createCase("argv exec reports missing programs and empty argv as spawn errors", async (env) => {
|
|
362
|
+
assert.strictEqual(errorCode(await env.exec(["nyan-durable-conformance-missing-program"], undefined, context)), "spawn_error");
|
|
363
|
+
assert.strictEqual(errorCode(await env.exec([], undefined, context)), "spawn_error");
|
|
364
|
+
}),
|
|
365
|
+
createCase("windowed exec keeps the exact tail and counts what it skips", async (env) => {
|
|
366
|
+
const lines = 2000;
|
|
367
|
+
const window = { maxBytes: 200, maxLines: 5, minIntervalMs: 0, bytesPerSecond: 1_000_000_000 };
|
|
368
|
+
let bytes = 0;
|
|
369
|
+
let newlines = 0;
|
|
370
|
+
let tail = "";
|
|
371
|
+
const result = await env.exec([...shell, `i=0; while [ $i -lt ${lines} ]; do echo line-$i; i=$((i+1)); done`], {
|
|
372
|
+
window,
|
|
373
|
+
onOutput: (text, _context, info) => {
|
|
374
|
+
if (info.skipped !== undefined) {
|
|
375
|
+
bytes += info.skipped.bytes;
|
|
376
|
+
newlines += info.skipped.newlines;
|
|
377
|
+
const after = encoder.encode(text).length;
|
|
378
|
+
const afterNewlines = text.split("\n").length - 1;
|
|
379
|
+
assert.ok(after > window.maxBytes || afterNewlines > window.maxLines, "a skip is followed by more than the window");
|
|
380
|
+
tail = "";
|
|
381
|
+
}
|
|
382
|
+
bytes += encoder.encode(text).length;
|
|
383
|
+
newlines += text.split("\n").length - 1;
|
|
384
|
+
tail += text;
|
|
385
|
+
},
|
|
386
|
+
}, context);
|
|
387
|
+
assert.strictEqual(getOrThrow(result).exitCode, 0);
|
|
388
|
+
const expected = Array.from({ length: lines }, (_, index) => `line-${index}\n`);
|
|
389
|
+
assert.strictEqual(bytes, expected.join("").length);
|
|
390
|
+
assert.strictEqual(newlines, lines);
|
|
391
|
+
assert.ok(tail.endsWith(expected.slice(-window.maxLines).join("")), "the delivered output ends with the tail");
|
|
392
|
+
}),
|
|
393
|
+
createCase("argv exec distinguishes timeout from abort", async (env) => {
|
|
394
|
+
const timedOut = await env.exec([...shell, "sleep 2"], { timeout: 0.1 }, context);
|
|
395
|
+
assert.strictEqual(errorCode(timedOut), "timeout");
|
|
396
|
+
const controller = new AbortController();
|
|
397
|
+
const running = env.exec([...shell, "sleep 2"], undefined, withAbortSignal(controller.signal, context));
|
|
398
|
+
setTimeout(() => controller.abort(), 100);
|
|
399
|
+
assert.strictEqual(errorCode(await running), "aborted");
|
|
400
|
+
}),
|
|
401
|
+
];
|
|
402
|
+
if (symlinks) {
|
|
403
|
+
cases.push(createCase("binary reader follows symlinks unless noFollow refuses the final one", async (env) => {
|
|
404
|
+
getOrThrow(await env.writeFile("target.txt", "target", context));
|
|
405
|
+
getOrThrow(await env.createDir("sub", undefined, context));
|
|
406
|
+
getOrThrow(await env.writeFile("sub/inner.txt", "inner", context));
|
|
407
|
+
const linked = await env.exec([...shell, "ln -s target.txt link.txt && ln -s sub dirlink"], undefined, context);
|
|
408
|
+
assert.strictEqual(getOrThrow(linked).exitCode, 0);
|
|
409
|
+
const followed = getOrThrow(await env.openBinaryReader("link.txt", undefined, context));
|
|
410
|
+
assert.strictEqual(decoder.decode(getOrThrow(await followed.read(0, 10, context))), "target");
|
|
411
|
+
await followed.close(context);
|
|
412
|
+
assert.strictEqual(errorCode(await env.openBinaryReader("link.txt", { noFollow: true }, context)), "invalid");
|
|
413
|
+
// Only the final component is refused; earlier symlinked directories still resolve.
|
|
414
|
+
const inner = getOrThrow(await env.openBinaryReader("dirlink/inner.txt", { noFollow: true }, context));
|
|
415
|
+
assert.strictEqual(decoder.decode(getOrThrow(await inner.read(0, 10, context))), "inner");
|
|
416
|
+
await inner.close(context);
|
|
417
|
+
}), watchCase("watch reports changes to the file a watched symbolic link points to", async (env) => {
|
|
418
|
+
getOrThrow(await env.writeFile("data/real.md", "one", context));
|
|
419
|
+
getOrThrow(await env.createDir("config", undefined, context));
|
|
420
|
+
const linked = await env.exec([...shell, "ln -s ../data/real.md config/AGENTS.md"], undefined, context);
|
|
421
|
+
assert.strictEqual(getOrThrow(linked).exitCode, 0);
|
|
422
|
+
await watching(env, [{ path: "config/AGENTS.md" }], async ({ expectChange }) => {
|
|
423
|
+
await expectChange("config/AGENTS.md", async () => {
|
|
424
|
+
getOrThrow(await env.writeFile("data/real.md", "two!", context));
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
return cases;
|
|
430
|
+
}
|
|
431
|
+
//# sourceMappingURL=env-conformance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-conformance.js","sourceRoot":"","sources":["../../src/testing/env-conformance.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@ny4n/chord\";\nimport { BACKGROUND_CONTEXT, withAbortSignal } from \"@ny4n/chord/context\";\nimport {\n\ttype ExecutionEnv,\n\ttype FileInfo,\n\tgetOrThrow,\n\ttype Result,\n\ttype ShellOutputInfo,\n\ttype WatchChange,\n\ttype WatchTarget,\n} from \"../env/index.ts\";\nimport type { EnvConformanceCase, EnvConformanceOptions } from \"./types.ts\";\n\nconst context = BACKGROUND_CONTEXT;\nconst decoder = new TextDecoder();\nconst encoder = new TextEncoder();\n\ntype EnvTest = (env: ExecutionEnv) => Promise<void>;\n\nfunction abortedContext(): Context {\n\tconst controller = new AbortController();\n\tcontroller.abort();\n\treturn withAbortSignal(controller.signal, BACKGROUND_CONTEXT);\n}\n\nfunction errorCode(result: Result<unknown, { code: string }>): string | undefined {\n\treturn result.ok ? undefined : result.error.code;\n}\n\nasync function readAll(\n\tenv: ExecutionEnv,\n\tpath: string,\n\tmaxEntries: number,\n): Promise<{ pages: FileInfo[][]; done: boolean }> {\n\tconst reader = getOrThrow(await env.openDirReader(path, context));\n\tconst pages: FileInfo[][] = [];\n\ttry {\n\t\tfor (let page = 0; page < 1000; page++) {\n\t\t\tconst next = getOrThrow(await reader.next(maxEntries, context));\n\t\t\tpages.push(next.entries);\n\t\t\tif (next.done) return { pages, done: true };\n\t\t}\n\t\treturn { pages, done: false };\n\t} finally {\n\t\tawait reader.close(context);\n\t}\n}\n\n/** Whether a change reports `path`: an overflow, or a reported path at or above it. */\nfunction covers(change: WatchChange, path: string): boolean {\n\tif (\"overflow\" in change) return true;\n\tif (!(\"paths\" in change)) return false;\n\treturn change.paths.some(\n\t\t(reported) => path === reported || path.startsWith(`${reported}/`) || path.startsWith(`${reported}\\\\`),\n\t);\n}\n\n/** Watch `targets` while `run` changes files, with a helper that waits for a change reporting a path. */\nasync function watching(\n\tenv: ExecutionEnv,\n\ttargets: readonly WatchTarget[],\n\trun: (helpers: {\n\t\treadonly changes: WatchChange[];\n\t\t/** Wait up to three seconds for a change, after the call, that reports `path`. */\n\t\texpectChange(path: string, change: () => Promise<void>): Promise<void>;\n\t\tabsolute(path: string): Promise<string>;\n\t}) => Promise<void>,\n): Promise<void> {\n\tconst changes: WatchChange[] = [];\n\tconst watcher = getOrThrow(await env.watch(targets, (change) => changes.push(change), context));\n\ttry {\n\t\tconst absolute = async (path: string) => getOrThrow(await env.absolutePath(path, context));\n\t\tawait run({\n\t\t\tchanges,\n\t\t\tabsolute,\n\t\t\tasync expectChange(path, change) {\n\t\t\t\tconst target = await absolute(path);\n\t\t\t\tconst from = changes.length;\n\t\t\t\tawait change();\n\t\t\t\tconst deadline = Date.now() + 3000;\n\t\t\t\twhile (!changes.slice(from).some((entry) => covers(entry, target))) {\n\t\t\t\t\tif (Date.now() > deadline) {\n\t\t\t\t\t\tthrow new Error(`No change reported ${target}; got ${JSON.stringify(changes.slice(from))}`);\n\t\t\t\t\t}\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 20));\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t} finally {\n\t\tawait watcher.close(context);\n\t}\n}\n\n/**\n * Creates runner-independent cases for an `ExecutionEnv`. `withEnv` must call and await its callback exactly once per\n * case with an environment whose `cwd` is a fresh, empty, writable directory.\n */\nexport function createEnvConformance(options: EnvConformanceOptions): readonly EnvConformanceCase[] {\n\tconst assert = options.assertions;\n\tconst shell = options.shell ?? [\"sh\", \"-c\"];\n\tconst symlinks = options.symlinks ?? true;\n\tconst createCase = (name: string, test: EnvTest, timeoutMs?: number): EnvConformanceCase => ({\n\t\tname,\n\t\t...(timeoutMs === undefined ? {} : { timeoutMs }),\n\t\trun: () => options.withEnv(test),\n\t});\n\t// Watch cases wait up to three seconds per step, longer than test runners allow by default.\n\tconst watchCase = (name: string, test: EnvTest): EnvConformanceCase => createCase(name, test, 30_000);\n\tconst execCollect = async (env: ExecutionEnv, command: string | readonly string[], cwd?: string) => {\n\t\tconst output: Record<ShellOutputInfo[\"stream\"], string> = { stdout: \"\", stderr: \"\" };\n\t\tconst result = await env.exec(\n\t\t\tcommand,\n\t\t\t{\n\t\t\t\t...(cwd === undefined ? {} : { cwd }),\n\t\t\t\tonOutput: (text, _context, info) => {\n\t\t\t\t\toutput[info.stream] += text;\n\t\t\t\t},\n\t\t\t},\n\t\t\tcontext,\n\t\t);\n\t\treturn { result, ...output };\n\t};\n\n\tconst cases: EnvConformanceCase[] = [\n\t\tcreateCase(\"binary reader reads byte ranges of the opened file\", async (env) => {\n\t\t\tgetOrThrow(await env.writeFile(\"data.txt\", \"hello world\", context));\n\t\t\tconst reader = getOrThrow(await env.openBinaryReader(\"data.txt\", undefined, context));\n\t\t\tconst info = getOrThrow(await reader.info(context));\n\t\t\tassert.partialDeepEqual(info, { name: \"data.txt\", kind: \"file\", size: 11 });\n\t\t\tassert.strictEqual(decoder.decode(getOrThrow(await reader.read(0, 5, context))), \"hello\");\n\t\t\tassert.strictEqual(decoder.decode(getOrThrow(await reader.read(6, 100, context))), \"world\");\n\t\t\tassert.strictEqual(getOrThrow(await reader.read(11, 4, context)).length, 0);\n\t\t\tassert.strictEqual(getOrThrow(await reader.read(50, 1, context)).length, 0);\n\t\t\tassert.strictEqual(getOrThrow(await reader.read(3, 0, context)).length, 0);\n\t\t\tassert.strictEqual(errorCode(await reader.read(-1, 1, context)), \"invalid\");\n\t\t\tassert.strictEqual(errorCode(await reader.read(0, 1.5, context)), \"invalid\");\n\t\t\tassert.strictEqual(errorCode(await reader.read(0, 1, abortedContext())), \"aborted\");\n\t\t\tawait reader.close(context);\n\t\t\tawait reader.close(context);\n\t\t\tassert.strictEqual(errorCode(await reader.read(0, 1, context)), \"invalid\");\n\t\t\tassert.strictEqual(errorCode(await reader.info(context)), \"invalid\");\n\t\t}),\n\n\t\tcreateCase(\"binary reader scans lines like decoding the whole file\", async (env) => {\n\t\t\t// A byte-order mark, an invalid sequence before a newline, an empty line, a later U+FEFF, and no final newline.\n\t\t\tconst bytes = Uint8Array.from([\n\t\t\t\t0xef, 0xbb, 0xbf, 0x61, 0x0a, 0xe2, 0x82, 0x0a, 0x0a, 0xef, 0xbb, 0xbf, 0x62, 0x0a, 0xc3, 0xa9,\n\t\t\t]);\n\t\t\tgetOrThrow(await env.writeFile(\"lines.txt\", bytes, context));\n\t\t\tconst lines = new TextDecoder().decode(bytes).split(\"\\n\");\n\t\t\tconst reader = getOrThrow(await env.openBinaryReader(\"lines.txt\", undefined, context));\n\t\t\ttry {\n\t\t\t\tconst ranges: [number, number | undefined][] = [\n\t\t\t\t\t[0, undefined],\n\t\t\t\t\t[0, 1],\n\t\t\t\t\t[1, 3],\n\t\t\t\t\t[2, 3],\n\t\t\t\t\t[3, undefined],\n\t\t\t\t\t[4, 9],\n\t\t\t\t];\n\t\t\t\tfor (const [startLine, endLine] of ranges) {\n\t\t\t\t\tconst scan = getOrThrow(\n\t\t\t\t\t\tawait reader.scanLines({ startLine, ...(endLine === undefined ? {} : { endLine }) }, context),\n\t\t\t\t\t);\n\t\t\t\t\tconst selected = lines.slice(startLine, endLine);\n\t\t\t\t\tconst range = (from: number, to: number) =>\n\t\t\t\t\t\tnew TextDecoder(\"utf-8\", { ignoreBOM: from > 0 }).decode(bytes.subarray(from, to));\n\t\t\t\t\tassert.strictEqual(scan.newlines, lines.length - 1);\n\t\t\t\t\tassert.strictEqual(range(scan.start, scan.end), selected.join(\"\\n\"));\n\t\t\t\t\tassert.strictEqual(scan.selectedBytes, encoder.encode(selected.join(\"\\n\")).length);\n\t\t\t\t\tassert.strictEqual(range(scan.start, scan.firstLineEnd), lines[startLine]);\n\t\t\t\t\tassert.strictEqual(scan.firstLineBytes, encoder.encode(lines[startLine]).length);\n\t\t\t\t}\n\t\t\t\tassert.partialDeepEqual(getOrThrow(await reader.scanLines({ startLine: 9 }, context)), {\n\t\t\t\t\tstart: bytes.length,\n\t\t\t\t\tend: bytes.length,\n\t\t\t\t\tselectedBytes: 0,\n\t\t\t\t});\n\t\t\t\tassert.strictEqual(errorCode(await reader.scanLines({ startLine: 2, endLine: 2 }, context)), \"invalid\");\n\t\t\t} finally {\n\t\t\t\tawait reader.close(context);\n\t\t\t}\n\t\t}),\n\n\t\tcreateCase(\"binary reader keeps reading the file it opened after a rename\", async (env) => {\n\t\t\tgetOrThrow(await env.writeFile(\"a.txt\", \"one\", context));\n\t\t\tconst reader = getOrThrow(await env.openBinaryReader(\"a.txt\", undefined, context));\n\t\t\ttry {\n\t\t\t\tgetOrThrow(await env.renameFile(\"a.txt\", \"b.txt\", context));\n\t\t\t\tgetOrThrow(await env.writeFile(\"a.txt\", \"two\", context));\n\t\t\t\tassert.strictEqual(decoder.decode(getOrThrow(await reader.read(0, 10, context))), \"one\");\n\t\t\t} finally {\n\t\t\t\tawait reader.close(context);\n\t\t\t}\n\t\t}),\n\n\t\tcreateCase(\"binary reader refuses directories, missing files and aborted opens\", async (env) => {\n\t\t\tgetOrThrow(await env.createDir(\"dir\", undefined, context));\n\t\t\tgetOrThrow(await env.writeFile(\"file.txt\", \"x\", context));\n\t\t\tassert.strictEqual(errorCode(await env.openBinaryReader(\"dir\", undefined, context)), \"is_directory\");\n\t\t\tassert.strictEqual(errorCode(await env.openBinaryReader(\"missing.txt\", undefined, context)), \"not_found\");\n\t\t\tassert.strictEqual(errorCode(await env.openBinaryReader(\"file.txt\", undefined, abortedContext())), \"aborted\");\n\t\t}),\n\n\t\tcreateCase(\"directory reader pages every entry exactly once\", async (env) => {\n\t\t\tconst names = [\"a.txt\", \"b.txt\", \"c.txt\", \"d.txt\", \"e.txt\"];\n\t\t\tfor (const name of names) getOrThrow(await env.writeFile(name, name, context));\n\t\t\tgetOrThrow(await env.createDir(\"sub\", undefined, context));\n\t\t\tconst { pages, done } = await readAll(env, \".\", 2);\n\t\t\tassert.ok(done, \"directory reader reached the end\");\n\t\t\tfor (const page of pages) assert.ok(page.length <= 2, \"page within maxEntries\");\n\t\t\tconst entries = pages.flat();\n\t\t\tassert.deepEqual(entries.map((entry) => entry.name).sort(), [...names, \"sub\"].sort());\n\t\t\tassert.strictEqual(entries.find((entry) => entry.name === \"sub\")?.kind, \"directory\");\n\t\t\tassert.partialDeepEqual(\n\t\t\t\tentries.find((entry) => entry.name === \"a.txt\"),\n\t\t\t\t{ kind: \"file\", size: 5 },\n\t\t\t);\n\t\t}),\n\n\t\tcreateCase(\"directory reader reports the end and refuses use after close\", async (env) => {\n\t\t\tgetOrThrow(await env.createDir(\"empty\", undefined, context));\n\t\t\tconst reader = getOrThrow(await env.openDirReader(\"empty\", context));\n\t\t\tassert.deepEqual(getOrThrow(await reader.next(10, context)), { entries: [], done: true });\n\t\t\tassert.deepEqual(getOrThrow(await reader.next(10, context)), { entries: [], done: true });\n\t\t\tassert.strictEqual(errorCode(await reader.next(0, context)), \"invalid\");\n\t\t\tassert.strictEqual(errorCode(await reader.next(1, abortedContext())), \"aborted\");\n\t\t\tawait reader.close(context);\n\t\t\tawait reader.close(context);\n\t\t\tassert.strictEqual(errorCode(await reader.next(1, context)), \"invalid\");\n\t\t}),\n\n\t\tcreateCase(\"directory reader refuses missing paths and files\", async (env) => {\n\t\t\tgetOrThrow(await env.writeFile(\"file.txt\", \"x\", context));\n\t\t\tassert.strictEqual(errorCode(await env.openDirReader(\"missing\", context)), \"not_found\");\n\t\t\tconst file = await env.openDirReader(\"file.txt\", context);\n\t\t\tassert.strictEqual(errorCode(file), \"not_directory\");\n\t\t\tassert.strictEqual(errorCode(await env.openDirReader(\".\", abortedContext())), \"aborted\");\n\t\t}),\n\n\t\tcreateCase(\"directory reader skips entries removed during enumeration\", async (env) => {\n\t\t\tgetOrThrow(await env.createDir(\"dir\", undefined, context));\n\t\t\tfor (const name of [\"x\", \"y\", \"z\"]) getOrThrow(await env.writeFile(`dir/${name}`, name, context));\n\t\t\tconst reader = getOrThrow(await env.openDirReader(\"dir\", context));\n\t\t\ttry {\n\t\t\t\tfor (const name of [\"x\", \"y\", \"z\"]) getOrThrow(await env.remove(`dir/${name}`, undefined, context));\n\t\t\t\tconst entries: FileInfo[] = [];\n\t\t\t\tfor (let page = 0; page < 10; page++) {\n\t\t\t\t\tconst next = getOrThrow(await reader.next(10, context));\n\t\t\t\t\tentries.push(...next.entries);\n\t\t\t\t\tif (next.done) break;\n\t\t\t\t}\n\t\t\t\tassert.deepEqual(entries, []);\n\t\t\t} finally {\n\t\t\t\tawait reader.close(context);\n\t\t\t}\n\t\t}),\n\n\t\twatchCase(\"watch reports a missing file's creation, changes, replacement and removal\", async (env) => {\n\t\t\tawait watching(env, [{ path: \"AGENTS.md\" }], async ({ expectChange }) => {\n\t\t\t\tawait expectChange(\"AGENTS.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"AGENTS.md\", \"one\", context));\n\t\t\t\t});\n\t\t\t\tawait expectChange(\"AGENTS.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"AGENTS.md\", \"two!\", context));\n\t\t\t\t});\n\t\t\t\t// Editors replace a file by renaming a new one over it.\n\t\t\t\tawait expectChange(\"AGENTS.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"AGENTS.md.tmp\", \"three\", context));\n\t\t\t\t\tgetOrThrow(await env.renameFile(\"AGENTS.md.tmp\", \"AGENTS.md\", context));\n\t\t\t\t});\n\t\t\t\tawait expectChange(\"AGENTS.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"AGENTS.md\", \"four\", context));\n\t\t\t\t});\n\t\t\t\tawait expectChange(\"AGENTS.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.remove(\"AGENTS.md\", undefined, context));\n\t\t\t\t});\n\t\t\t});\n\t\t}),\n\n\t\twatchCase(\"watch reports a missing target whose ancestors are created\", async (env) => {\n\t\t\tawait watching(env, [{ path: \"a/b/c/AGENTS.md\" }], async ({ expectChange }) => {\n\t\t\t\tawait expectChange(\"a/b/c/AGENTS.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"a/b/c/AGENTS.md\", \"x\", context));\n\t\t\t\t});\n\t\t\t});\n\t\t}),\n\n\t\twatchCase(\"watch follows directories created together with their contents\", async (env) => {\n\t\t\tgetOrThrow(await env.createDir(\"skills\", undefined, context));\n\t\t\tawait watching(env, [{ path: \"skills\", recursive: true }], async ({ expectChange }) => {\n\t\t\t\t// Written before any watcher on the new directories can exist.\n\t\t\t\tawait expectChange(\"skills/a/b/SKILL.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"skills/a/b/SKILL.md\", \"one\", context));\n\t\t\t\t});\n\t\t\t\tawait expectChange(\"skills/a/b/SKILL.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"skills/a/b/SKILL.md\", \"two!\", context));\n\t\t\t\t});\n\t\t\t\tawait expectChange(\"skills/a/b/c/SKILL.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"skills/a/b/c/SKILL.md\", \"deeper\", context));\n\t\t\t\t});\n\t\t\t});\n\t\t}),\n\n\t\twatchCase(\"watch keeps watching a path whose parent is renamed and recreated\", async (env) => {\n\t\t\tgetOrThrow(await env.writeFile(\"proj/.nyan/skills/x.md\", \"x\", context));\n\t\t\tawait watching(env, [{ path: \"proj/.nyan/skills\", recursive: true }], async ({ expectChange }) => {\n\t\t\t\tawait expectChange(\"proj/.nyan/skills\", async () => {\n\t\t\t\t\tgetOrThrow(await env.renameFile(\"proj/.nyan\", \"proj/old\", context));\n\t\t\t\t});\n\t\t\t\tawait expectChange(\"proj/.nyan/skills/y.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"proj/.nyan/skills/y.md\", \"y\", context));\n\t\t\t\t});\n\t\t\t\tawait expectChange(\"proj/.nyan/skills/y.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"proj/.nyan/skills/y.md\", \"yy\", context));\n\t\t\t\t});\n\t\t\t});\n\t\t}),\n\n\t\twatchCase(\"watch skips excluded entries and reports a rename out of them\", async (env) => {\n\t\t\tgetOrThrow(await env.createDir(\"skills\", undefined, context));\n\t\t\tconst targets: WatchTarget[] = [\n\t\t\t\t{ path: \"skills\", recursive: true, exclude: { hidden: true, names: [\"node_modules\"] } },\n\t\t\t];\n\t\t\tawait watching(env, targets, async ({ changes, expectChange, absolute }) => {\n\t\t\t\tgetOrThrow(await env.writeFile(\"skills/node_modules/dep/SKILL.md\", \"dep\", context));\n\t\t\t\tgetOrThrow(await env.writeFile(\"skills/.SKILL.md.tmp\", \"draft\", context));\n\t\t\t\tawait expectChange(\"skills/SKILL.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.renameFile(\"skills/.SKILL.md.tmp\", \"skills/SKILL.md\", context));\n\t\t\t\t});\n\t\t\t\tconst hidden = [await absolute(\"skills/node_modules\"), await absolute(\"skills/.SKILL.md.tmp\")];\n\t\t\t\tfor (const change of changes) {\n\t\t\t\t\tif (!(\"paths\" in change)) continue;\n\t\t\t\t\tfor (const path of change.paths) {\n\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t!hidden.some((excluded) => path === excluded || path.startsWith(excluded)),\n\t\t\t\t\t\t\t`excluded ${path}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}),\n\n\t\twatchCase(\"watch keeps recursive coverage where a non-recursive target overlaps\", async (env) => {\n\t\t\tgetOrThrow(await env.writeFile(\"skills/a/one.md\", \"one\", context));\n\t\t\tconst targets: WatchTarget[] = [{ path: \"skills\" }, { path: \"skills\", recursive: true }];\n\t\t\tawait watching(env, targets, async ({ expectChange }) => {\n\t\t\t\tawait expectChange(\"skills/a/two.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"skills/a/two.md\", \"two\", context));\n\t\t\t\t});\n\t\t\t});\n\t\t}),\n\n\t\twatchCase(\"watch follows a directory replaced at the same path\", async (env) => {\n\t\t\tgetOrThrow(await env.writeFile(\"skills/a/x.md\", \"x\", context));\n\t\t\tawait watching(env, [{ path: \"skills\", recursive: true }], async ({ expectChange }) => {\n\t\t\t\tawait expectChange(\"skills/a\", async () => {\n\t\t\t\t\tgetOrThrow(await env.renameFile(\"skills/a\", \"skills-old\", context));\n\t\t\t\t\tgetOrThrow(await env.createDir(\"skills/a\", undefined, context));\n\t\t\t\t});\n\t\t\t\tawait expectChange(\"skills/a/y.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"skills/a/y.md\", \"y\", context));\n\t\t\t\t});\n\t\t\t\tawait expectChange(\"skills/a/y.md\", async () => {\n\t\t\t\t\tgetOrThrow(await env.writeFile(\"skills/a/y.md\", \"yy\", context));\n\t\t\t\t});\n\t\t\t});\n\t\t}),\n\n\t\twatchCase(\"watch stops reporting once closed\", async (env) => {\n\t\t\tconst changes: WatchChange[] = [];\n\t\t\tconst watcher = getOrThrow(await env.watch([{ path: \"file.txt\" }], (change) => changes.push(change), context));\n\t\t\tassert.ok(watcher.mode === \"native\" || watcher.mode === \"polling\", \"watcher reports its mode\");\n\t\t\tawait watcher.close(context);\n\t\t\tawait watcher.close(context);\n\t\t\tgetOrThrow(await env.writeFile(\"file.txt\", \"x\", context));\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 300));\n\t\t\tassert.deepEqual(changes, []);\n\t\t}),\n\n\t\tcreateCase(\"argv exec passes arguments to the program without shell parsing\", async (env) => {\n\t\t\tconst hostile = \"it's $(touch pwned) `touch pwned` *; touch pwned\";\n\t\t\tconst { result, stdout } = await execCollect(env, [\n\t\t\t\t...shell,\n\t\t\t\t'printf \"%s|%s\" \"$1\" \"$2\"',\n\t\t\t\t\"argv0\",\n\t\t\t\thostile,\n\t\t\t\t\"a b\",\n\t\t\t]);\n\t\t\tassert.strictEqual(getOrThrow(result).exitCode, 0);\n\t\t\tassert.strictEqual(stdout, `${hostile}|a b`);\n\t\t\tassert.strictEqual(getOrThrow(await env.exists(\"pwned\", context)), false);\n\t\t}),\n\n\t\tcreateCase(\"exec reports the stream of every chunk in both forms\", async (env) => {\n\t\t\tconst script = \"printf out; printf err >&2; printf more\";\n\t\t\tconst argv = await execCollect(env, [...shell, script]);\n\t\t\tassert.strictEqual(getOrThrow(argv.result).exitCode, 0);\n\t\t\tassert.strictEqual(argv.stdout, \"outmore\");\n\t\t\tassert.strictEqual(argv.stderr, \"err\");\n\t\t\tconst string = await execCollect(env, script);\n\t\t\tassert.strictEqual(getOrThrow(string.result).exitCode, 0);\n\t\t\tassert.strictEqual(string.stdout, \"outmore\");\n\t\t\tassert.strictEqual(string.stderr, \"err\");\n\t\t}),\n\n\t\tcreateCase(\"argv exec honors cwd and exit codes\", async (env) => {\n\t\t\tgetOrThrow(await env.createDir(\"sub\", undefined, context));\n\t\t\tconst made = await execCollect(env, [...shell, \"printf x > made.txt; exit 3\"], \"sub\");\n\t\t\tassert.strictEqual(getOrThrow(made.result).exitCode, 3);\n\t\t\tassert.strictEqual(getOrThrow(await env.readTextFile(\"sub/made.txt\", context)), \"x\");\n\t\t}),\n\n\t\tcreateCase(\"argv exec reports missing programs and empty argv as spawn errors\", async (env) => {\n\t\t\tassert.strictEqual(\n\t\t\t\terrorCode(await env.exec([\"nyan-durable-conformance-missing-program\"], undefined, context)),\n\t\t\t\t\"spawn_error\",\n\t\t\t);\n\t\t\tassert.strictEqual(errorCode(await env.exec([], undefined, context)), \"spawn_error\");\n\t\t}),\n\n\t\tcreateCase(\"windowed exec keeps the exact tail and counts what it skips\", async (env) => {\n\t\t\tconst lines = 2000;\n\t\t\tconst window = { maxBytes: 200, maxLines: 5, minIntervalMs: 0, bytesPerSecond: 1_000_000_000 };\n\t\t\tlet bytes = 0;\n\t\t\tlet newlines = 0;\n\t\t\tlet tail = \"\";\n\t\t\tconst result = await env.exec(\n\t\t\t\t[...shell, `i=0; while [ $i -lt ${lines} ]; do echo line-$i; i=$((i+1)); done`],\n\t\t\t\t{\n\t\t\t\t\twindow,\n\t\t\t\t\tonOutput: (text, _context, info) => {\n\t\t\t\t\t\tif (info.skipped !== undefined) {\n\t\t\t\t\t\t\tbytes += info.skipped.bytes;\n\t\t\t\t\t\t\tnewlines += info.skipped.newlines;\n\t\t\t\t\t\t\tconst after = encoder.encode(text).length;\n\t\t\t\t\t\t\tconst afterNewlines = text.split(\"\\n\").length - 1;\n\t\t\t\t\t\t\tassert.ok(\n\t\t\t\t\t\t\t\tafter > window.maxBytes || afterNewlines > window.maxLines,\n\t\t\t\t\t\t\t\t\"a skip is followed by more than the window\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\ttail = \"\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbytes += encoder.encode(text).length;\n\t\t\t\t\t\tnewlines += text.split(\"\\n\").length - 1;\n\t\t\t\t\t\ttail += text;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tassert.strictEqual(getOrThrow(result).exitCode, 0);\n\t\t\tconst expected = Array.from({ length: lines }, (_, index) => `line-${index}\\n`);\n\t\t\tassert.strictEqual(bytes, expected.join(\"\").length);\n\t\t\tassert.strictEqual(newlines, lines);\n\t\t\tassert.ok(tail.endsWith(expected.slice(-window.maxLines).join(\"\")), \"the delivered output ends with the tail\");\n\t\t}),\n\n\t\tcreateCase(\"argv exec distinguishes timeout from abort\", async (env) => {\n\t\t\tconst timedOut = await env.exec([...shell, \"sleep 2\"], { timeout: 0.1 }, context);\n\t\t\tassert.strictEqual(errorCode(timedOut), \"timeout\");\n\t\t\tconst controller = new AbortController();\n\t\t\tconst running = env.exec([...shell, \"sleep 2\"], undefined, withAbortSignal(controller.signal, context));\n\t\t\tsetTimeout(() => controller.abort(), 100);\n\t\t\tassert.strictEqual(errorCode(await running), \"aborted\");\n\t\t}),\n\t];\n\n\tif (symlinks) {\n\t\tcases.push(\n\t\t\tcreateCase(\"binary reader follows symlinks unless noFollow refuses the final one\", async (env) => {\n\t\t\t\tgetOrThrow(await env.writeFile(\"target.txt\", \"target\", context));\n\t\t\t\tgetOrThrow(await env.createDir(\"sub\", undefined, context));\n\t\t\t\tgetOrThrow(await env.writeFile(\"sub/inner.txt\", \"inner\", context));\n\t\t\t\tconst linked = await env.exec(\n\t\t\t\t\t[...shell, \"ln -s target.txt link.txt && ln -s sub dirlink\"],\n\t\t\t\t\tundefined,\n\t\t\t\t\tcontext,\n\t\t\t\t);\n\t\t\t\tassert.strictEqual(getOrThrow(linked).exitCode, 0);\n\n\t\t\t\tconst followed = getOrThrow(await env.openBinaryReader(\"link.txt\", undefined, context));\n\t\t\t\tassert.strictEqual(decoder.decode(getOrThrow(await followed.read(0, 10, context))), \"target\");\n\t\t\t\tawait followed.close(context);\n\n\t\t\t\tassert.strictEqual(\n\t\t\t\t\terrorCode(await env.openBinaryReader(\"link.txt\", { noFollow: true }, context)),\n\t\t\t\t\t\"invalid\",\n\t\t\t\t);\n\n\t\t\t\t// Only the final component is refused; earlier symlinked directories still resolve.\n\t\t\t\tconst inner = getOrThrow(await env.openBinaryReader(\"dirlink/inner.txt\", { noFollow: true }, context));\n\t\t\t\tassert.strictEqual(decoder.decode(getOrThrow(await inner.read(0, 10, context))), \"inner\");\n\t\t\t\tawait inner.close(context);\n\t\t\t}),\n\n\t\t\twatchCase(\"watch reports changes to the file a watched symbolic link points to\", async (env) => {\n\t\t\t\tgetOrThrow(await env.writeFile(\"data/real.md\", \"one\", context));\n\t\t\t\tgetOrThrow(await env.createDir(\"config\", undefined, context));\n\t\t\t\tconst linked = await env.exec([...shell, \"ln -s ../data/real.md config/AGENTS.md\"], undefined, context);\n\t\t\t\tassert.strictEqual(getOrThrow(linked).exitCode, 0);\n\t\t\t\tawait watching(env, [{ path: \"config/AGENTS.md\" }], async ({ expectChange }) => {\n\t\t\t\t\tawait expectChange(\"config/AGENTS.md\", async () => {\n\t\t\t\t\t\tgetOrThrow(await env.writeFile(\"data/real.md\", \"two!\", context));\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn cases;\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createExpectAssertions, type ExpectLike } from "./assertions.ts";
|
|
2
|
+
export { createEnvConformance } from "./env-conformance.ts";
|
|
3
|
+
export { registerEnvConformance, registerStorageConformance, type StorageConformanceRunner } from "./runner.ts";
|
|
4
|
+
export { STORAGE_MEMORY_SCALES, STORAGE_READ_BENCHMARKS, STORAGE_WRITE_BENCHMARKS, type StorageBenchmarkDataset, type StorageBenchmarkScale, type StorageReadBenchmark, type StorageWriteBenchmark, seedStorageBenchmark, seedStorageWriteBenchmark, storageBenchmarkPrimaryRecordCount, TIMING_SCALE, } from "./storage-benchmark.ts";
|
|
5
|
+
export { createStorageConformance } from "./storage-conformance.ts";
|
|
6
|
+
export type { EnvConformanceAssertions, EnvConformanceCase, EnvConformanceOptions, EnvConformanceProvider, StorageConformanceAssertions, StorageConformanceCase, StorageConformanceOptions, StorageConformanceProvider, } from "./types.ts";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAChH,OAAO,EACN,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,yBAAyB,EACzB,kCAAkC,EAClC,YAAY,GACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,YAAY,EACX,wBAAwB,EACxB,kBAAkB,EAClB,qBAAqB,EACrB,sBAAsB,EACtB,4BAA4B,EAC5B,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,GAC1B,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createExpectAssertions } from "./assertions.js";
|
|
2
|
+
export { createEnvConformance } from "./env-conformance.js";
|
|
3
|
+
export { registerEnvConformance, registerStorageConformance } from "./runner.js";
|
|
4
|
+
export { STORAGE_MEMORY_SCALES, STORAGE_READ_BENCHMARKS, STORAGE_WRITE_BENCHMARKS, seedStorageBenchmark, seedStorageWriteBenchmark, storageBenchmarkPrimaryRecordCount, TIMING_SCALE, } from "./storage-benchmark.js";
|
|
5
|
+
export { createStorageConformance } from "./storage-conformance.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAmB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,0BAA0B,EAAiC,MAAM,aAAa,CAAC;AAChH,OAAO,EACN,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EAKxB,oBAAoB,EACpB,yBAAyB,EACzB,kCAAkC,EAClC,YAAY,GACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["export { createExpectAssertions, type ExpectLike } from \"./assertions.ts\";\nexport { createEnvConformance } from \"./env-conformance.ts\";\nexport { registerEnvConformance, registerStorageConformance, type StorageConformanceRunner } from \"./runner.ts\";\nexport {\n\tSTORAGE_MEMORY_SCALES,\n\tSTORAGE_READ_BENCHMARKS,\n\tSTORAGE_WRITE_BENCHMARKS,\n\ttype StorageBenchmarkDataset,\n\ttype StorageBenchmarkScale,\n\ttype StorageReadBenchmark,\n\ttype StorageWriteBenchmark,\n\tseedStorageBenchmark,\n\tseedStorageWriteBenchmark,\n\tstorageBenchmarkPrimaryRecordCount,\n\tTIMING_SCALE,\n} from \"./storage-benchmark.ts\";\nexport { createStorageConformance } from \"./storage-conformance.ts\";\nexport type {\n\tEnvConformanceAssertions,\n\tEnvConformanceCase,\n\tEnvConformanceOptions,\n\tEnvConformanceProvider,\n\tStorageConformanceAssertions,\n\tStorageConformanceCase,\n\tStorageConformanceOptions,\n\tStorageConformanceProvider,\n} from \"./types.ts\";\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ExpectLike } from "./assertions.ts";
|
|
2
|
+
import type { EnvConformanceOptions, EnvConformanceProvider, StorageConformanceProvider } from "./types.ts";
|
|
3
|
+
export interface StorageConformanceRunner {
|
|
4
|
+
readonly describe: (name: string, suite: () => void) => unknown;
|
|
5
|
+
readonly expect: ExpectLike;
|
|
6
|
+
readonly it: (name: string, test: () => Promise<void>, timeoutMs?: number) => unknown;
|
|
7
|
+
}
|
|
8
|
+
/** Registers the runner-independent cases with a Vitest/Jest-compatible test runner. */
|
|
9
|
+
export declare function registerStorageConformance(runner: StorageConformanceRunner, name: string, withStorage: StorageConformanceProvider): void;
|
|
10
|
+
/** Registers the runner-independent `ExecutionEnv` cases with a Vitest/Jest-compatible test runner. */
|
|
11
|
+
export declare function registerEnvConformance(runner: StorageConformanceRunner, name: string, withEnv: EnvConformanceProvider, options?: Pick<EnvConformanceOptions, "shell" | "symlinks">): void;
|
|
12
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/testing/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE5G,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,OAAO,CAAC;IAChE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;CACtF;AAED,wFAAwF;AACxF,wBAAgB,0BAA0B,CACzC,MAAM,EAAE,wBAAwB,EAChC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,0BAA0B,GACrC,IAAI,CAQN;AAED,uGAAuG;AACvG,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,wBAAwB,EAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,sBAAsB,EAC/B,OAAO,GAAE,IAAI,CAAC,qBAAqB,EAAE,OAAO,GAAG,UAAU,CAAM,GAC7D,IAAI,CAKN"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createExpectAssertions } from "./assertions.js";
|
|
2
|
+
import { createEnvConformance } from "./env-conformance.js";
|
|
3
|
+
import { createStorageConformance } from "./storage-conformance.js";
|
|
4
|
+
/** Registers the runner-independent cases with a Vitest/Jest-compatible test runner. */
|
|
5
|
+
export function registerStorageConformance(runner, name, withStorage) {
|
|
6
|
+
const cases = createStorageConformance({
|
|
7
|
+
assertions: createExpectAssertions(runner.expect),
|
|
8
|
+
withStorage,
|
|
9
|
+
});
|
|
10
|
+
runner.describe(name, () => {
|
|
11
|
+
for (const testCase of cases)
|
|
12
|
+
runner.it(testCase.name, testCase.run);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** Registers the runner-independent `ExecutionEnv` cases with a Vitest/Jest-compatible test runner. */
|
|
16
|
+
export function registerEnvConformance(runner, name, withEnv, options = {}) {
|
|
17
|
+
const cases = createEnvConformance({ ...options, assertions: createExpectAssertions(runner.expect), withEnv });
|
|
18
|
+
runner.describe(name, () => {
|
|
19
|
+
for (const testCase of cases)
|
|
20
|
+
runner.it(testCase.name, testCase.run, testCase.timeoutMs);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/testing/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAmB,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AASpE,wFAAwF;AACxF,MAAM,UAAU,0BAA0B,CACzC,MAAgC,EAChC,IAAY,EACZ,WAAuC;IAEvC,MAAM,KAAK,GAAG,wBAAwB,CAAC;QACtC,UAAU,EAAE,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC;QACjD,WAAW;KACX,CAAC,CAAC;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE;QAC1B,KAAK,MAAM,QAAQ,IAAI,KAAK;YAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,sBAAsB,CACrC,MAAgC,EAChC,IAAY,EACZ,OAA+B,EAC/B,OAAO,GAAsD,EAAE;IAE/D,MAAM,KAAK,GAAG,oBAAoB,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/G,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE;QAC1B,KAAK,MAAM,QAAQ,IAAI,KAAK;YAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { createExpectAssertions, type ExpectLike } from \"./assertions.ts\";\nimport { createEnvConformance } from \"./env-conformance.ts\";\nimport { createStorageConformance } from \"./storage-conformance.ts\";\nimport type { EnvConformanceOptions, EnvConformanceProvider, StorageConformanceProvider } from \"./types.ts\";\n\nexport interface StorageConformanceRunner {\n\treadonly describe: (name: string, suite: () => void) => unknown;\n\treadonly expect: ExpectLike;\n\treadonly it: (name: string, test: () => Promise<void>, timeoutMs?: number) => unknown;\n}\n\n/** Registers the runner-independent cases with a Vitest/Jest-compatible test runner. */\nexport function registerStorageConformance(\n\trunner: StorageConformanceRunner,\n\tname: string,\n\twithStorage: StorageConformanceProvider,\n): void {\n\tconst cases = createStorageConformance({\n\t\tassertions: createExpectAssertions(runner.expect),\n\t\twithStorage,\n\t});\n\trunner.describe(name, () => {\n\t\tfor (const testCase of cases) runner.it(testCase.name, testCase.run);\n\t});\n}\n\n/** Registers the runner-independent `ExecutionEnv` cases with a Vitest/Jest-compatible test runner. */\nexport function registerEnvConformance(\n\trunner: StorageConformanceRunner,\n\tname: string,\n\twithEnv: EnvConformanceProvider,\n\toptions: Pick<EnvConformanceOptions, \"shell\" | \"symlinks\"> = {},\n): void {\n\tconst cases = createEnvConformance({ ...options, assertions: createExpectAssertions(runner.expect), withEnv });\n\trunner.describe(name, () => {\n\t\tfor (const testCase of cases) runner.it(testCase.name, testCase.run, testCase.timeoutMs);\n\t});\n}\n"]}
|