poe-code 4.0.12 → 4.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-code",
3
- "version": "4.0.12",
3
+ "version": "4.0.14",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -90,6 +90,7 @@
90
90
  "smoke:toolcraft-standalone": "node scripts/verify-toolcraft-standalone.mjs",
91
91
  "smoke:toolcraft-sdk-types": "node scripts/verify-toolcraft-published-sdk-types.mjs",
92
92
  "sync-skills": "tsx scripts/sync-skills.ts",
93
+ "record:fs-conformance": "tsx scripts/record-fs-conformance.ts",
93
94
  "sync:json-schema-test-suite": "node scripts/sync-json-schema-test-suite.mjs",
94
95
  "sync:uri-template-test": "node scripts/sync-uri-template-test.mjs",
95
96
  "postinstall": "node scripts/postinstall-sync-skills.mjs"
@@ -34,8 +34,8 @@ function countCompletedTasks(planPath, content) {
34
34
  total
35
35
  };
36
36
  }
37
- async function ensurePlanExists(fs, cwd, planPath) {
38
- const absolutePath = path.isAbsolute(planPath) ? planPath : path.resolve(cwd, planPath);
37
+ async function ensurePlanExists(fs, cwd, homeDir, planPath) {
38
+ const absolutePath = resolveAbsolutePlanPath(planPath, cwd, homeDir);
39
39
  const stat = await fs.stat(absolutePath).catch((error) => {
40
40
  if (isNotFound(error)) {
41
41
  return undefined;
@@ -97,7 +97,7 @@ export async function resolvePlanPaths(options) {
97
97
  .filter((planPath) => planPath.length > 0);
98
98
  if (explicitPlans.length > 0) {
99
99
  for (const planPath of explicitPlans) {
100
- await ensurePlanExists(fs, options.cwd, planPath);
100
+ await ensurePlanExists(fs, options.cwd, options.homeDir, planPath);
101
101
  }
102
102
  return explicitPlans;
103
103
  }
@@ -12,6 +12,7 @@ import { lint } from "./lint.js";
12
12
  import { createLintModulesFromRuntimeRegistry } from "./lint/runtime-modules.js";
13
13
  import { makeAgentModule } from "./modules/agent.js";
14
14
  import { makeFailModule } from "./modules/fail.js";
15
+ import { makeFsModule } from "./modules/fs.js";
15
16
  import { makeHarnessModule } from "./modules/harness.js";
16
17
  import { makeLogModule } from "./modules/log.js";
17
18
  import { makeMetricModule } from "./modules/metric.js";
@@ -80,7 +81,8 @@ function readCurrentWorkingDirectory() {
80
81
  }
81
82
  function parseArgs(argv) {
82
83
  const parsed = {
83
- fix: false
84
+ fix: false,
85
+ fs: false
84
86
  };
85
87
  for (let index = 0; index < argv.length; index += 1) {
86
88
  const arg = argv[index];
@@ -88,6 +90,15 @@ function parseArgs(argv) {
88
90
  parsed.fix = true;
89
91
  continue;
90
92
  }
93
+ if (arg === "--fs") {
94
+ parsed.fs = true;
95
+ continue;
96
+ }
97
+ if (arg === "--fs-root") {
98
+ parsed.fsRoot = readFlagValue(argv, index, arg);
99
+ index += 1;
100
+ continue;
101
+ }
91
102
  if (arg === "--snapshot") {
92
103
  parsed.snapshotPath = readFlagValue(argv, index, arg);
93
104
  index += 1;
@@ -116,6 +127,9 @@ function parseArgs(argv) {
116
127
  }
117
128
  parsed.filepath = arg;
118
129
  }
130
+ if (!parsed.fs && parsed.fsRoot !== undefined) {
131
+ throw new CliExitError("--fs-root requires --fs. Pass --fs to give the script a filesystem confined to that root, or drop --fs-root.", EXIT_RUNTIME);
132
+ }
119
133
  return parsed;
120
134
  }
121
135
  function readFlagValue(argv, index, flag) {
@@ -155,6 +169,9 @@ async function runScriptFile(filepath, parsed, options) {
155
169
  version: loaded.frontmatter.version
156
170
  };
157
171
  const runtime = createRuntime(loaded.frontmatter, meta, {
172
+ fs: parsed.fs
173
+ ? { root: path.resolve(options.cwd, parsed.fsRoot ?? dirname(filepath)) }
174
+ : undefined,
158
175
  modulesFor: options.modulesFor,
159
176
  stderr: options.stderr,
160
177
  stdout: options.stdout
@@ -348,6 +365,8 @@ function createRuntime(frontmatter, meta, options) {
348
365
  registry: {
349
366
  agent: toModuleExports(agent),
350
367
  fail: toModuleExports(new Map([["default", makeFailModule().default]])),
368
+ // The one module here that is not a stub, so it is registered only when asked for.
369
+ ...(options.fs === undefined ? {} : { fs: toModuleExports(makeFsModule(options.fs)) }),
351
370
  git: toModuleExports(new Map([
352
371
  ["checkpoint", git.checkpoint],
353
372
  ["commit", git.commit],
@@ -413,6 +432,10 @@ function createUsage() {
413
432
  "",
414
433
  "Options:",
415
434
  " --fix apply lint fixes before running",
435
+ " --fs register the fs module: a real filesystem, unlike the agent,",
436
+ " git, and metric stubs this runner bundles",
437
+ " --fs-root <path> directory --fs confines the script to (default: the script's",
438
+ " directory)",
416
439
  " --snapshot <path> write the final snapshot, and best-effort snapshot on SIGINT",
417
440
  " --restore <path> restore from a snapshot before running",
418
441
  " --max-steps <n> cap interpreter step budget",
@@ -29,6 +29,8 @@ export { createSpawnUsageAccumulator, makeAgentModule, runWithSpawnUsageAccumula
29
29
  export type { AgentModuleDefinition, AgentModuleOptions, AgentModuleParallelCall, AgentModuleRetryOptions, AgentModuleSpawnOptions, AgentSpawnEvent, SpawnAgent, SpawnAgentInput, SpawnAgentResult, SpawnUsageAccumulator, SpawnUsageTotal } from "./modules/agent.js";
30
30
  export { makeEnvModule } from "./modules/env.js";
31
31
  export { makeFailModule } from "./modules/fail.js";
32
+ export { makeFsModule } from "./modules/fs.js";
33
+ export type { FsImplementation, FsModule, FsModuleOptions, SandboxDirent, SandboxStats } from "./modules/fs.js";
32
34
  export { makeGitModule } from "./modules/git.js";
33
35
  export { makeHarnessModule } from "./modules/harness.js";
34
36
  export { makeLogModule } from "./modules/log.js";
@@ -20,6 +20,7 @@ export { splitFrontmatter } from "./loader/frontmatter.js";
20
20
  export { createSpawnUsageAccumulator, makeAgentModule, runWithSpawnUsageAccumulator } from "./modules/agent.js";
21
21
  export { makeEnvModule } from "./modules/env.js";
22
22
  export { makeFailModule } from "./modules/fail.js";
23
+ export { makeFsModule } from "./modules/fs.js";
23
24
  export { makeGitModule } from "./modules/git.js";
24
25
  export { makeHarnessModule } from "./modules/harness.js";
25
26
  export { makeLogModule } from "./modules/log.js";
@@ -1,4 +1,5 @@
1
1
  import { createSandboxClosure, createSandboxPromise, isSandboxClosure, isSandboxPromise } from "./values.js";
2
+ import { observeSandboxPromise } from "./promise-tracker.js";
2
3
  import { replaceErrorStack } from "../error/shape.js";
3
4
  export function wrapCancelableBindings(bindings, signal) {
4
5
  if (signal === undefined) {
@@ -32,6 +33,9 @@ function wrapCancelableValue(value, signal, seen) {
32
33
  return wrapped;
33
34
  }
34
35
  if (isSandboxPromise(value)) {
36
+ // The wrapper delivers this promise's rejection to the sandbox in its place, so
37
+ // the sandbox never awaits the original and it would otherwise read as unhandled.
38
+ observeSandboxPromise(value);
35
39
  const wrapped = createSandboxPromise(wrapCancelablePromise(value.promise, signal, seen), {
36
40
  ...(value.hostCall === undefined ? {} : { hostCall: value.hostCall }),
37
41
  ...(value.hostCallJournal === undefined ? {} : { hostCallJournal: value.hostCallJournal }),
@@ -7,6 +7,13 @@ import { digestHostCallArguments, HostCallResumabilityError } from "./host-call.
7
7
  import { createSandboxClosure, createSandboxMap, createSandboxPromise, createSandboxSet, deepCopyFromSandbox, deepCopyToSandbox, isSandboxClosure, isSandboxPromise, measureSandboxData } from "./values.js";
8
8
  import { enterRunningState } from "./running-state.js";
9
9
  const AsyncFunction = (async () => undefined).constructor;
10
+ const hostErrorMetadata = {
11
+ code: "string",
12
+ dest: "string",
13
+ errno: "number",
14
+ path: "string",
15
+ syscall: "string"
16
+ };
10
17
  const hostOperationPolicies = new WeakMap();
11
18
  export function declareHostOperation(operation, policy) {
12
19
  hostOperationPolicies.set(operation, policy);
@@ -190,9 +197,31 @@ function createHostErrorValue(reason, stackFrames, budget, span) {
190
197
  : createSubsetErrorValue("Error", describeThrownReason(reason), stackFrames, budget, {
191
198
  chargeBudget: false
192
199
  });
200
+ if (reason instanceof Error) {
201
+ copyHostErrorMetadata(error, reason, budget);
202
+ }
193
203
  attachErrorSpan(error, span);
194
204
  return error;
195
205
  }
206
+ function copyHostErrorMetadata(error, reason, budget) {
207
+ const resumeChecks = budget.suspendChecks();
208
+ try {
209
+ for (const [key, expectedType] of Object.entries(hostErrorMetadata)) {
210
+ const descriptor = Object.getOwnPropertyDescriptor(reason, key);
211
+ if (descriptor === undefined || "get" in descriptor || "set" in descriptor) {
212
+ continue;
213
+ }
214
+ const value = descriptor.value;
215
+ if (typeof value !== expectedType) {
216
+ continue;
217
+ }
218
+ error[key] = typeof value === "string" ? budget.allocateString(value) : value;
219
+ }
220
+ }
221
+ finally {
222
+ resumeChecks();
223
+ }
224
+ }
196
225
  function wrapSandboxClosureForHost(closure, stackFrames, budget) {
197
226
  return async (...args) => {
198
227
  const leaveRunning = enterRunningState(closure);
@@ -410,7 +439,9 @@ function copyHostValueToSandbox(value, stackFrames, options, state, path) {
410
439
  if ("get" in descriptor || "set" in descriptor) {
411
440
  throw new TypeError(`Unsupported sandbox value at ${joinPath(path, key)}: accessor property`);
412
441
  }
413
- Object.defineProperty(copy, key, {
442
+ // Charged like any other string the copy carries in: a key is as readable to the sandbox
443
+ // as the value under it, so the same limit answers for both.
444
+ Object.defineProperty(copy, budget.allocateString(key), {
414
445
  enumerable: true,
415
446
  configurable: true,
416
447
  writable: true,
@@ -10,3 +10,4 @@ export declare function getPromiseMember(target: SandboxPromise, property: strin
10
10
  export declare function resolveSandboxValue(value: SandboxValue | Promise<SandboxValue> | PromiseLike<SandboxValue>, options?: {
11
11
  budget?: Budget;
12
12
  }): Promise<SandboxValue>;
13
+ export declare function consumeSettledHostCall(value: SandboxPromise): void;
@@ -148,6 +148,14 @@ function budgetSandboxValue(value, budget) {
148
148
  export function resolveSandboxValue(value, options = {}) {
149
149
  return Promise.resolve().then(() => resolveSandboxValueNow(value, options, new WeakSet()));
150
150
  }
151
+ // A host call result is consumed once, when it settles. Observing the same promise
152
+ // again is ordinary JavaScript and must not be reported as a double consumption.
153
+ export function consumeSettledHostCall(value) {
154
+ if (value.hostCall?.lifecycle !== "settled") {
155
+ return;
156
+ }
157
+ value.hostCallJournal?.consume(value.hostCall);
158
+ }
151
159
  function resolveSandboxValueNow(value, options, seenThenables) {
152
160
  if (isPromiseLike(value)) {
153
161
  return Promise.resolve(value).then((resolved) => resolveSandboxValueNow(resolved, options, seenThenables), (reason) => Promise.reject(budgetIfNeeded(reason, options.budget)));
@@ -155,10 +163,10 @@ function resolveSandboxValueNow(value, options, seenThenables) {
155
163
  if (isSandboxPromise(value)) {
156
164
  observeSandboxPromise(value);
157
165
  return value.promise.then((resolved) => {
158
- value.hostCallJournal?.consume(value.hostCall);
166
+ consumeSettledHostCall(value);
159
167
  return resolveSandboxValueNow(resolved, options, seenThenables);
160
168
  }, (reason) => {
161
- value.hostCallJournal?.consume(value.hostCall);
169
+ consumeSettledHostCall(value);
162
170
  return Promise.reject(budgetIfNeeded(reason, options.budget));
163
171
  });
164
172
  }
@@ -0,0 +1,15 @@
1
+ type Realpath = (path: string) => Promise<string>;
2
+ type Readlink = (path: string) => Promise<string>;
3
+ export type CanonicalPathFs = {
4
+ realpath: Realpath;
5
+ readlink: Readlink;
6
+ };
7
+ type PathIdentity = {
8
+ dev: number;
9
+ ino: number;
10
+ };
11
+ export type Stat = (path: string) => Promise<PathIdentity>;
12
+ export declare function resolveCanonicalPath({ realpath, readlink }: CanonicalPathFs, path: string): Promise<string>;
13
+ export declare function containsPath(stat: Stat, canonicalRoot: string, canonicalPath: string): Promise<boolean>;
14
+ export declare function isSamePath(stat: Stat, canonicalLeft: string, canonicalRight: string): Promise<boolean>;
15
+ export {};
@@ -0,0 +1,161 @@
1
+ import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
2
+ import { getOwnErrorCode } from "../error-codes.js";
3
+ // realpath answers ENOENT for a missing segment and ENOTDIR when a parent is a
4
+ // file. Both mean the segment is not an existing directory, so both walk up.
5
+ const MISSING_PATH_CODES = ["ENOENT", "ENOTDIR"];
6
+ // What the filesystem answers for a path that has no link target to follow: EINVAL
7
+ // for a path that exists and is not a link, and the missing-path codes for one that
8
+ // is not there at all. Any other failure is the filesystem's own and is left to
9
+ // surface rather than read as "not a link", which would canonicalize a link the
10
+ // filesystem merely refused to read as though it named itself.
11
+ const NOT_A_SYMLINK_CODES = [...MISSING_PATH_CODES, "EINVAL"];
12
+ // A conforming realpath raises ELOOP for a cycle before the walk below can follow
13
+ // one, so this cap is only reached by a filesystem that reports a cycle as a
14
+ // missing path. Following one of those forever would hang the sandbox, so the walk
15
+ // gives up the way the platform does — at SYMLOOP_MAX, which is 32 on darwin and
16
+ // Linux alike.
17
+ const MAX_SYMLINK_FOLLOWS = 32;
18
+ // Canonicalizes as much of the path as exists and re-appends the segments that do
19
+ // not, so a path is checked after symlinks are followed rather than as written.
20
+ // The missing segments go back through resolve(), which collapses any `..` among
21
+ // them, so they cannot re-escape what realpath already pinned down.
22
+ //
23
+ // A dangling symlink is the case this cannot read off realpath alone: realpath
24
+ // refuses one with the same ENOENT it gives a path that was never there, yet the two
25
+ // are not the same place. node follows a dangling link and acts on its target — a
26
+ // write through one creates the target — so a dangling link canonicalizes to what it
27
+ // names, not to itself. Reading that ENOENT as "nothing here" would answer with the
28
+ // link's own path and call a link out of a caller's root contained.
29
+ export async function resolveCanonicalPath({ realpath, readlink }, path) {
30
+ const missingSegments = [];
31
+ let current = path;
32
+ let follows = 0;
33
+ while (true) {
34
+ try {
35
+ const canonicalCurrent = await realpath(current);
36
+ return resolve(canonicalCurrent, ...missingSegments.reverse());
37
+ }
38
+ catch (error) {
39
+ if (!MISSING_PATH_CODES.includes(getOwnErrorCode(error) ?? "")) {
40
+ throw error;
41
+ }
42
+ const target = await readSymlinkTarget(readlink, current);
43
+ if (target !== undefined) {
44
+ if (++follows > MAX_SYMLINK_FOLLOWS) {
45
+ throw createLoopError(path);
46
+ }
47
+ // node resolves a relative target against the link's own directory. Any
48
+ // segments already collected still hang off whatever the target resolves to,
49
+ // so they stay on the list.
50
+ current = isAbsolute(target) ? resolve(target) : resolve(dirname(current), target);
51
+ continue;
52
+ }
53
+ const parent = dirname(current);
54
+ if (parent === current) {
55
+ return resolve(path);
56
+ }
57
+ missingSegments.push(basename(current));
58
+ current = parent;
59
+ }
60
+ }
61
+ }
62
+ // The target of a link realpath could not resolve, or undefined when the path is not
63
+ // a link at all and the walk should climb past it instead.
64
+ async function readSymlinkTarget(readlink, path) {
65
+ try {
66
+ return await readlink(path);
67
+ }
68
+ catch (error) {
69
+ if (NOT_A_SYMLINK_CODES.includes(getOwnErrorCode(error) ?? "")) {
70
+ return undefined;
71
+ }
72
+ throw error;
73
+ }
74
+ }
75
+ // Shaped like the ELOOP a filesystem raises for a cycle it walked itself, so a
76
+ // caller that branches on the code reads the cap and the filesystem's own answer the
77
+ // same way.
78
+ function createLoopError(path) {
79
+ const error = new Error(`ELOOP: too many symbolic links encountered, realpath '${path}'`);
80
+ error.code = "ELOOP";
81
+ return error;
82
+ }
83
+ // Whether root contains path, root itself included. Both arguments must already be
84
+ // canonical: a `..` or a symlink left in either one would make the comparison lie.
85
+ //
86
+ // The rule: the filesystem has the deciding vote on containment, and comparing
87
+ // the canonical spellings is only a fast path that is trusted when it answers
88
+ // "contained". Two canonical paths can differ in spelling and still be the same
89
+ // place — on a case-insensitive filesystem (darwin's default) `/repo` and `/REPO`
90
+ // are one directory, and darwin's realpath echoes the spelling it was handed
91
+ // rather than the on-disk one, so canonicalization does not fold the case away.
92
+ // Comparing spellings can therefore only ever be wrong in one direction: it may
93
+ // answer "outside" for a path that resolves inside root, never the reverse. So
94
+ // "outside" is re-decided by walking the path's ancestors and asking the
95
+ // filesystem for each one's identity, which no spelling can lie about. Folding
96
+ // case here instead would be a hole: on a case-sensitive filesystem `/REPO/x` is
97
+ // genuinely a different file from `/repo/x`.
98
+ export async function containsPath(stat, canonicalRoot, canonicalPath) {
99
+ if (containsCanonicalSpelling(canonicalRoot, canonicalPath)) {
100
+ return true;
101
+ }
102
+ return descendsFromRoot(stat, canonicalRoot, canonicalPath);
103
+ }
104
+ // Whether two canonical paths name the same place. Two spellings can name one
105
+ // place on a case-insensitive filesystem, so identity decides here for the same
106
+ // reason it decides containment above.
107
+ export async function isSamePath(stat, canonicalLeft, canonicalRight) {
108
+ if (canonicalLeft === canonicalRight) {
109
+ return true;
110
+ }
111
+ const [left, right] = await Promise.all([
112
+ readIdentity(stat, canonicalLeft),
113
+ readIdentity(stat, canonicalRight)
114
+ ]);
115
+ return (left !== undefined && right !== undefined && left.dev === right.dev && left.ino === right.ino);
116
+ }
117
+ function containsCanonicalSpelling(canonicalRoot, canonicalPath) {
118
+ const relativePath = relative(canonicalRoot, canonicalPath);
119
+ // An empty relative path is root itself, which the root contains.
120
+ return !(relativePath === ".." ||
121
+ relativePath.startsWith(`..${sep}`) ||
122
+ isAbsolute(relativePath));
123
+ }
124
+ // Climbs the path's ancestors looking for root's own identity. A canonical path
125
+ // carries no symlinks and no `..`, so every ancestor it names is a real ancestor:
126
+ // finding root among them means the path descends from root whatever either one
127
+ // is spelled like.
128
+ async function descendsFromRoot(stat, canonicalRoot, canonicalPath) {
129
+ const root = await readIdentity(stat, canonicalRoot);
130
+ if (root === undefined) {
131
+ return false;
132
+ }
133
+ let current = canonicalPath;
134
+ while (true) {
135
+ const identity = await readIdentity(stat, current);
136
+ if (identity !== undefined && identity.dev === root.dev && identity.ino === root.ino) {
137
+ return true;
138
+ }
139
+ const parent = dirname(current);
140
+ if (parent === current) {
141
+ return false;
142
+ }
143
+ current = parent;
144
+ }
145
+ }
146
+ // A path that is not there has no identity to match, so the walk climbs past it
147
+ // rather than treating it as an answer. Any other failure is the filesystem's own
148
+ // and is left to surface: swallowing it would report a refusal for what node
149
+ // blames on something else.
150
+ async function readIdentity(stat, path) {
151
+ try {
152
+ const { dev, ino } = await stat(path);
153
+ return { dev, ino };
154
+ }
155
+ catch (error) {
156
+ if (MISSING_PATH_CODES.includes(getOwnErrorCode(error) ?? "")) {
157
+ return undefined;
158
+ }
159
+ throw error;
160
+ }
161
+ }
@@ -0,0 +1,99 @@
1
+ import { Volume } from "memfs";
2
+ import { makeFsModule, type FsImplementation } from "./fs.js";
3
+ export declare const STRING_ENCODINGS: readonly ["utf8", "utf-8", "utf16le", "utf-16le", "ucs2", "ucs-2", "latin1", "binary", "ascii", "base64", "base64url", "hex"];
4
+ type StringEncoding = (typeof STRING_ENCODINGS)[number];
5
+ export declare function encode(text: string, encoding: StringEncoding): string;
6
+ export type FsCaseDriver = Omit<ReturnType<typeof makeFsModule>, "constants" | "mkdir" | "rmdir"> & {
7
+ mkdir(path: string, options?: {
8
+ recursive?: boolean;
9
+ mode?: number;
10
+ }): Promise<string | undefined>;
11
+ rmdir(path: string, options?: {
12
+ recursive?: boolean;
13
+ }): Promise<void>;
14
+ };
15
+ type ObservedFailure = {
16
+ readonly name: string;
17
+ readonly message: string;
18
+ readonly code: string;
19
+ readonly path?: string;
20
+ };
21
+ type Observed = {
22
+ readonly result: unknown;
23
+ } | ObservedFailure;
24
+ export type RecordedTruth = {
25
+ readonly result: unknown;
26
+ } | (ObservedFailure & {
27
+ readonly errno: number;
28
+ readonly syscall: string;
29
+ readonly dest?: string;
30
+ });
31
+ export type RecordedCase = {
32
+ readonly title: string;
33
+ readonly resolved: boolean;
34
+ readonly node: RecordedTruth;
35
+ readonly gap?: string;
36
+ };
37
+ export type RecordedPlatform = {
38
+ readonly nodeVersion: string;
39
+ readonly cases: readonly RecordedCase[];
40
+ };
41
+ export type NodeTruthFixture = {
42
+ readonly platforms: Readonly<Record<string, RecordedPlatform>>;
43
+ };
44
+ export declare const HANGS = "hangs";
45
+ export type FsConformanceCase = {
46
+ readonly title: string;
47
+ readonly setup?: (volume: Volume) => void;
48
+ readonly invoke: (fs: FsCaseDriver) => Promise<unknown>;
49
+ readonly node: RecordedTruth;
50
+ readonly gap?: {
51
+ readonly reason: string;
52
+ readonly memfs: Observed | typeof HANGS;
53
+ };
54
+ readonly readsAnswer?: true;
55
+ };
56
+ export declare function readSystemError(code: string): {
57
+ errno: number;
58
+ description: string;
59
+ };
60
+ export declare function systemErrorMessage(code: string, syscall: string, path?: string, dest?: string): string;
61
+ export declare function createNodeTruthFs(truth: RecordedTruth): FsImplementation;
62
+ export declare function readRecordedOutcome(operation: Promise<unknown>): Promise<RecordedTruth>;
63
+ export declare function readObserved(operation: Promise<unknown>): Promise<Observed>;
64
+ export declare function toObserved(truth: RecordedTruth): Observed;
65
+ export declare function toRecordedObserved(entry: RecordedCase): Observed;
66
+ export declare function driveCase(testCase: FsConformanceCase): {
67
+ fs: FsCaseDriver;
68
+ reference: FsCaseDriver;
69
+ volume: Volume;
70
+ };
71
+ export declare const CASE_ROOT = "/repo";
72
+ export declare const CASE_VOLUME: Readonly<Record<string, string>>;
73
+ type SystemErrorCase = {
74
+ readonly title: string;
75
+ readonly syscall: string;
76
+ readonly code: string;
77
+ readonly path?: string;
78
+ readonly dest?: string;
79
+ readonly setup?: (volume: Volume) => void;
80
+ readonly invoke: (fs: FsCaseDriver) => Promise<unknown>;
81
+ readonly gap?: FsConformanceCase["gap"];
82
+ };
83
+ export declare function systemErrorTruth(entry: {
84
+ readonly code: string;
85
+ readonly syscall: string;
86
+ readonly path?: string;
87
+ readonly dest?: string;
88
+ }): RecordedTruth;
89
+ export declare function toConformanceCase(entry: SystemErrorCase): FsConformanceCase;
90
+ export declare const SYSTEM_ERROR_CASES: readonly SystemErrorCase[];
91
+ export declare const MKDIR_RM_RMDIR_CASES: readonly FsConformanceCase[];
92
+ export declare const WRITE_CASES: readonly FsConformanceCase[];
93
+ export declare const stageLoop: (volume: Volume) => void;
94
+ export declare const LOOP_HANGS = "memfs recurses through the cycle instead of answering ELOOP";
95
+ export declare const SYMLINK_CASES: readonly FsConformanceCase[];
96
+ export declare const COPY_RENAME_CASES: readonly FsConformanceCase[];
97
+ export declare const READ_AND_SHAPE_CASES: readonly FsConformanceCase[];
98
+ export declare const FS_CONFORMANCE_CASES: readonly FsConformanceCase[];
99
+ export {};