@mooloo-ai/eutaxy 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +16 -2
- package/dist/bin.d.mts +1 -0
- package/dist/bin.mjs +17 -0
- package/dist/bin.mjs.map +1 -0
- package/dist/cli-DTuDOgGM.mjs +2 -0
- package/dist/cli.d.mts +8 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +2 -0
- package/dist/cloudflare.d.mts +125 -0
- package/dist/cloudflare.d.mts.map +1 -0
- package/dist/cloudflare.mjs +815 -0
- package/dist/cloudflare.mjs.map +1 -0
- package/dist/explore-BB77_V0x.d.mts +302 -0
- package/dist/explore-BB77_V0x.d.mts.map +1 -0
- package/dist/index.d.mts +23 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/src-Bef00JwK.mjs +202 -0
- package/dist/src-Bef00JwK.mjs.map +1 -0
- package/dist/src-Dn3Lm5Us.mjs +1091 -0
- package/dist/src-Dn3Lm5Us.mjs.map +1 -0
- package/dist/src-dP-0Apun.mjs +310 -0
- package/dist/src-dP-0Apun.mjs.map +1 -0
- package/package.json +59 -3
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// packages/core/src/canonical-json.ts
|
|
2
|
+
function canonicalJson(value) {
|
|
3
|
+
if (value === null) return "null";
|
|
4
|
+
switch (typeof value) {
|
|
5
|
+
case "boolean": return value ? "true" : "false";
|
|
6
|
+
case "number":
|
|
7
|
+
if (!Number.isFinite(value)) throw new TypeError(`non-finite number: ${value}`);
|
|
8
|
+
return Object.is(value, -0) ? "0" : JSON.stringify(value);
|
|
9
|
+
case "string": return JSON.stringify(value);
|
|
10
|
+
case "object": {
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
const items = [];
|
|
13
|
+
for (let i = 0; i < value.length; i++) {
|
|
14
|
+
const v = value[i];
|
|
15
|
+
if (v === void 0) throw new TypeError(`hole or undefined at index ${i}`);
|
|
16
|
+
items.push(canonicalJson(v));
|
|
17
|
+
}
|
|
18
|
+
return `[${items.join(",")}]`;
|
|
19
|
+
}
|
|
20
|
+
const keys = Object.keys(value).sort();
|
|
21
|
+
const parts = [];
|
|
22
|
+
for (const k of keys) {
|
|
23
|
+
const v = value[k];
|
|
24
|
+
if (v === void 0) throw new TypeError(`undefined at key ${JSON.stringify(k)}`);
|
|
25
|
+
parts.push(`${JSON.stringify(k)}:${canonicalJson(v)}`);
|
|
26
|
+
}
|
|
27
|
+
return `{${parts.join(",")}}`;
|
|
28
|
+
}
|
|
29
|
+
default: throw new TypeError(`not JSON data: ${typeof value}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// packages/core/src/utf8.ts
|
|
33
|
+
/**
|
|
34
|
+
* UTF-8 encode a string, with no runtime dependency on TextEncoder. An unpaired
|
|
35
|
+
* surrogate becomes U+FFFD, exactly as TextEncoder does, so the output is
|
|
36
|
+
* always valid UTF-8.
|
|
37
|
+
*/
|
|
38
|
+
function utf8Encode(s) {
|
|
39
|
+
const out = [];
|
|
40
|
+
for (const ch of s) {
|
|
41
|
+
let cp = ch.codePointAt(0) ?? 0;
|
|
42
|
+
if (cp >= 55296 && cp <= 57343) cp = 65533;
|
|
43
|
+
if (cp < 128) out.push(cp);
|
|
44
|
+
else if (cp < 2048) out.push(192 | cp >> 6, 128 | cp & 63);
|
|
45
|
+
else if (cp < 65536) out.push(224 | cp >> 12, 128 | cp >> 6 & 63, 128 | cp & 63);
|
|
46
|
+
else out.push(240 | cp >> 18, 128 | cp >> 12 & 63, 128 | cp >> 6 & 63, 128 | cp & 63);
|
|
47
|
+
}
|
|
48
|
+
return Uint8Array.from(out);
|
|
49
|
+
}
|
|
50
|
+
// packages/core/src/effect-log.ts
|
|
51
|
+
/**
|
|
52
|
+
* The effect log: the complete, ordered record of what a simulated run did at
|
|
53
|
+
* its effect boundaries. It is plain data, appended immutably, and has one
|
|
54
|
+
* canonical serialisation, so two runs are identical exactly when their
|
|
55
|
+
* serialised logs are byte-identical.
|
|
56
|
+
*/
|
|
57
|
+
const emptyLog = Object.freeze({ entries: Object.freeze([]) });
|
|
58
|
+
/** Append one effect, assigning its sequence number. The input log is unchanged. */
|
|
59
|
+
function appendEffect(log, effect) {
|
|
60
|
+
const record = {
|
|
61
|
+
...effect,
|
|
62
|
+
seq: log.entries.length
|
|
63
|
+
};
|
|
64
|
+
return { entries: [...log.entries, record] };
|
|
65
|
+
}
|
|
66
|
+
/** One canonical JSON object per line, in sequence order. */
|
|
67
|
+
function serializeLog(log) {
|
|
68
|
+
return log.entries.map((e) => canonicalJson({
|
|
69
|
+
seq: e.seq,
|
|
70
|
+
kind: e.kind,
|
|
71
|
+
invocation: e.invocation,
|
|
72
|
+
target: e.target,
|
|
73
|
+
op: e.op,
|
|
74
|
+
payload: e.payload
|
|
75
|
+
})).join("\n");
|
|
76
|
+
}
|
|
77
|
+
/** A 64-bit FNV-1a digest of the serialised log, as 16 hex digits. Not cryptographic. */
|
|
78
|
+
function digestLog(log) {
|
|
79
|
+
const bytes = utf8Encode(serializeLog(log));
|
|
80
|
+
let h = 14695981039346656037n;
|
|
81
|
+
for (const b of bytes) {
|
|
82
|
+
h ^= BigInt(b);
|
|
83
|
+
h = h * 1099511628211n & 18446744073709551615n;
|
|
84
|
+
}
|
|
85
|
+
return h.toString(16).padStart(16, "0");
|
|
86
|
+
}
|
|
87
|
+
// packages/report/src/regression.ts
|
|
88
|
+
/**
|
|
89
|
+
* The Vitest regression emitter. Given a violation, produce a test file that
|
|
90
|
+
* replays its shrunk schedule on the restricted scenario. By default the
|
|
91
|
+
* test expects the invariants to hold, so it fails while the bug exists and
|
|
92
|
+
* passes once it is fixed. With `expect: "violates"` it asserts the recorded
|
|
93
|
+
* violation instead, which is how Eutaxy's own corpus exercises the emitter.
|
|
94
|
+
*
|
|
95
|
+
* The emitter knows nothing about any platform: the caller supplies the
|
|
96
|
+
* import lines and the expression that builds the platform.
|
|
97
|
+
*/
|
|
98
|
+
function emitRegressionTest(verdict, options) {
|
|
99
|
+
const engineModule = options.engineModule ?? "@mooloo-ai/eutaxy";
|
|
100
|
+
const mode = options.expect ?? "holds";
|
|
101
|
+
const schedule = canonicalJson(verdict.schedule);
|
|
102
|
+
const kept = canonicalJson([...verdict.keptEvents]);
|
|
103
|
+
const keys = canonicalJson([...verdict.violation.keys]);
|
|
104
|
+
const title = mode === "holds" ? `${verdict.scenario}: ${verdict.violation.invariant} holds on the recorded schedule` : `${verdict.scenario}: ${verdict.violation.invariant} is still violated on the recorded schedule`;
|
|
105
|
+
const assertion = mode === "holds" ? ` expect(violations.map((v) => \`\${v.invariant}: \${v.message}\`)).toEqual([]);` : ` expect(violations.map((v) => [v.invariant, v.keys])).toContainEqual([${JSON.stringify(verdict.violation.invariant)}, ${keys}]);`;
|
|
106
|
+
return [
|
|
107
|
+
"// Generated by Eutaxy. Replays a recorded violation; regenerate rather than edit.",
|
|
108
|
+
`// Scenario ${verdict.scenario}, seed ${String(verdict.seed)}: ${verdict.violation.message}`,
|
|
109
|
+
`import { checkInvariants, replay, restrictSpec } from ${JSON.stringify(engineModule)};`,
|
|
110
|
+
...options.imports,
|
|
111
|
+
"import { expect, test } from \"vitest\";",
|
|
112
|
+
"",
|
|
113
|
+
`const schedule = ${schedule} as const;`,
|
|
114
|
+
`const keptEvents = ${kept} as const;`,
|
|
115
|
+
"",
|
|
116
|
+
`test(${JSON.stringify(title)}, async () => {`,
|
|
117
|
+
` const platform = ${options.platform};`,
|
|
118
|
+
` const spec = restrictSpec(platform, ${options.scenario}, keptEvents);`,
|
|
119
|
+
" const log = await replay(spec, platform, schedule);",
|
|
120
|
+
" const violations = checkInvariants(log, spec.invariants);",
|
|
121
|
+
assertion,
|
|
122
|
+
"});",
|
|
123
|
+
""
|
|
124
|
+
].join("\n");
|
|
125
|
+
}
|
|
126
|
+
/** The JSON report: one object, canonical, with a schema version at the top. */
|
|
127
|
+
function renderJson(verdict) {
|
|
128
|
+
return canonicalJson({
|
|
129
|
+
schemaVersion: 1,
|
|
130
|
+
generator: "eutaxy",
|
|
131
|
+
verdict
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function clip(s, max) {
|
|
135
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}…`;
|
|
136
|
+
}
|
|
137
|
+
function pad(s, width) {
|
|
138
|
+
return s.length >= width ? s : s + " ".repeat(width - s.length);
|
|
139
|
+
}
|
|
140
|
+
/** The trace as a fixed-width table; evidence rows are marked with `*`. */
|
|
141
|
+
function renderTrace(log, evidence = []) {
|
|
142
|
+
const rows = log.entries.map((e) => [
|
|
143
|
+
evidence.includes(e.seq) ? "*" : " ",
|
|
144
|
+
String(e.seq),
|
|
145
|
+
e.invocation,
|
|
146
|
+
e.kind,
|
|
147
|
+
e.target,
|
|
148
|
+
e.op,
|
|
149
|
+
clip(canonicalJson(e.payload), 72)
|
|
150
|
+
]);
|
|
151
|
+
const header = [
|
|
152
|
+
" ",
|
|
153
|
+
"seq",
|
|
154
|
+
"invocation",
|
|
155
|
+
"kind",
|
|
156
|
+
"target",
|
|
157
|
+
"op",
|
|
158
|
+
"payload"
|
|
159
|
+
];
|
|
160
|
+
const widths = header.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
161
|
+
const line = (cells) => cells.map((c, i) => i === cells.length - 1 ? c : pad(c, widths[i])).join(" ").trimEnd();
|
|
162
|
+
return [line(header), ...rows.map(line)].join("\n");
|
|
163
|
+
}
|
|
164
|
+
/** A readable report: verdict, what it rests on, and for a violation the schedule and trace. */
|
|
165
|
+
function renderText(verdict) {
|
|
166
|
+
const out = [];
|
|
167
|
+
const r = verdict.reached;
|
|
168
|
+
out.push(`${verdict.kind} ${verdict.scenario} (seed ${String(verdict.seed)})`);
|
|
169
|
+
switch (verdict.kind) {
|
|
170
|
+
case "VIOLATED":
|
|
171
|
+
out.push("");
|
|
172
|
+
out.push(`Invariant ${verdict.violation.invariant} is violated: ${verdict.violation.message}`);
|
|
173
|
+
out.push(`Keys: ${verdict.violation.keys.join(", ")}`);
|
|
174
|
+
out.push("");
|
|
175
|
+
out.push(`Schedule (run seed ${verdict.schedule.seed}), events kept: ${verdict.keptEvents.join(", ")}`);
|
|
176
|
+
if (verdict.schedule.faults.length === 0) out.push(" no injected faults");
|
|
177
|
+
for (const f of verdict.schedule.faults) out.push(` ${f.fault} at ${f.invocation} effect #${f.ordinal} (${f.target} ${f.op})`);
|
|
178
|
+
out.push(`Shrunk: ${verdict.shrunk.eventsRemoved} event(s) and ${verdict.shrunk.faultsRemoved} fault(s) removed in ${verdict.shrunk.runs} run(s)`);
|
|
179
|
+
out.push("");
|
|
180
|
+
out.push("Trace:");
|
|
181
|
+
out.push(renderTrace(verdict.trace, verdict.violation.evidence));
|
|
182
|
+
break;
|
|
183
|
+
case "BOUNDED": {
|
|
184
|
+
const b = verdict.bounds;
|
|
185
|
+
out.push("");
|
|
186
|
+
out.push("No violation found within the stated bounds. This is not a proof.");
|
|
187
|
+
out.push(`Bounds: faults per run ≤ ${b.maxFaultsPerRun}, redeliveries ≤ ${b.maxRedeliveries}, concurrent invocations ≤ ${b.maxConcurrentInvocations}, budget ${b.budgetMs} ms`);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
case "UNSUPPORTED":
|
|
191
|
+
out.push("");
|
|
192
|
+
out.push(`Eutaxy could not decide this scenario (${verdict.cause}): ${verdict.reason}`);
|
|
193
|
+
}
|
|
194
|
+
out.push("");
|
|
195
|
+
out.push(`Explored: ${r.runs} run(s), ${r.effectPoints} effect point(s), ${r.faultsInjected} fault(s) injected, ${r.elapsedMs} ms`);
|
|
196
|
+
out.push("Assumptions:");
|
|
197
|
+
for (const a of verdict.assumptions) out.push(` - ${a}`);
|
|
198
|
+
return out.join("\n");
|
|
199
|
+
}
|
|
200
|
+
export { appendEffect as a, canonicalJson as c, emitRegressionTest as i, renderText as n, digestLog as o, renderTrace as r, emptyLog as s, renderJson as t };
|
|
201
|
+
|
|
202
|
+
//# sourceMappingURL=src-Bef00JwK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"src-Bef00JwK.mjs","names":[],"sources":["../../core/src/canonical-json.ts","../../core/src/utf8.ts","../../core/src/effect-log.ts","../../report/src/regression.ts","../../report/src/render.ts"],"sourcesContent":["/**\n * Canonical JSON: the one serialisation of a value, so that equal data always\n * produces equal bytes. Object keys are sorted; numbers must be finite; there\n * is no whitespace. Anything that is not plain JSON data is rejected.\n */\n\nexport type JsonValue =\n | null\n | boolean\n | number\n | string\n | readonly JsonValue[]\n | { readonly [key: string]: JsonValue };\n\nexport function canonicalJson(value: JsonValue): string {\n if (value === null) return \"null\";\n switch (typeof value) {\n case \"boolean\":\n return value ? \"true\" : \"false\";\n case \"number\":\n if (!Number.isFinite(value)) throw new TypeError(`non-finite number: ${value}`);\n return Object.is(value, -0) ? \"0\" : JSON.stringify(value);\n case \"string\":\n return JSON.stringify(value);\n case \"object\": {\n if (Array.isArray(value)) {\n const items: string[] = [];\n for (let i = 0; i < value.length; i++) {\n const v = value[i];\n if (v === undefined) throw new TypeError(`hole or undefined at index ${i}`);\n items.push(canonicalJson(v));\n }\n return `[${items.join(\",\")}]`;\n }\n const keys = Object.keys(value).sort();\n const parts: string[] = [];\n for (const k of keys) {\n const v = (value as { readonly [key: string]: JsonValue | undefined })[k];\n if (v === undefined) throw new TypeError(`undefined at key ${JSON.stringify(k)}`);\n parts.push(`${JSON.stringify(k)}:${canonicalJson(v)}`);\n }\n return `{${parts.join(\",\")}}`;\n }\n default:\n throw new TypeError(`not JSON data: ${typeof value}`);\n }\n}\n","/**\n * UTF-8 encode a string, with no runtime dependency on TextEncoder. An unpaired\n * surrogate becomes U+FFFD, exactly as TextEncoder does, so the output is\n * always valid UTF-8.\n */\nexport function utf8Encode(s: string): Uint8Array {\n const out: number[] = [];\n for (const ch of s) {\n let cp = ch.codePointAt(0) ?? 0;\n if (cp >= 0xd800 && cp <= 0xdfff) cp = 0xfffd;\n if (cp < 0x80) out.push(cp);\n else if (cp < 0x800) out.push(0xc0 | (cp >> 6), 0x80 | (cp & 0x3f));\n else if (cp < 0x10000)\n out.push(0xe0 | (cp >> 12), 0x80 | ((cp >> 6) & 0x3f), 0x80 | (cp & 0x3f));\n else\n out.push(\n 0xf0 | (cp >> 18),\n 0x80 | ((cp >> 12) & 0x3f),\n 0x80 | ((cp >> 6) & 0x3f),\n 0x80 | (cp & 0x3f),\n );\n }\n return Uint8Array.from(out);\n}\n\n/** True when two byte arrays are identical in length and content. */\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n","/**\n * The effect log: the complete, ordered record of what a simulated run did at\n * its effect boundaries. It is plain data, appended immutably, and has one\n * canonical serialisation, so two runs are identical exactly when their\n * serialised logs are byte-identical.\n */\n\nimport { canonicalJson, type JsonValue } from \"./canonical-json.ts\";\nimport { utf8Encode } from \"./utf8.ts\";\n\n/** `snapshot` records platform state (e.g. D1 tables) captured at the end of a run; it is not an effect point. */\nexport type EffectKind = \"call\" | \"result\" | \"fault\" | \"snapshot\";\n\nexport interface EffectRecord {\n /** Position in the log, dense from 0. */\n readonly seq: number;\n readonly kind: EffectKind;\n /** The invocation (request, message, alarm) this effect belongs to. */\n readonly invocation: string;\n /** The binding or global the effect went through, e.g. \"D1:DB\". */\n readonly target: string;\n /** The operation, e.g. \"run\", \"batch\", \"send\". */\n readonly op: string;\n /** Operation-specific data: arguments, results, or the injected fault. */\n readonly payload: JsonValue;\n}\n\nexport interface EffectLog {\n readonly entries: readonly EffectRecord[];\n}\n\nexport const emptyLog: EffectLog = Object.freeze({ entries: Object.freeze([]) });\n\n/** Append one effect, assigning its sequence number. The input log is unchanged. */\nexport function appendEffect(log: EffectLog, effect: Omit<EffectRecord, \"seq\">): EffectLog {\n const record: EffectRecord = { ...effect, seq: log.entries.length };\n return { entries: [...log.entries, record] };\n}\n\n/** One canonical JSON object per line, in sequence order. */\nexport function serializeLog(log: EffectLog): string {\n return log.entries\n .map((e) =>\n canonicalJson({\n seq: e.seq,\n kind: e.kind,\n invocation: e.invocation,\n target: e.target,\n op: e.op,\n payload: e.payload,\n }),\n )\n .join(\"\\n\");\n}\n\n/** A 64-bit FNV-1a digest of the serialised log, as 16 hex digits. Not cryptographic. */\nexport function digestLog(log: EffectLog): string {\n const bytes = utf8Encode(serializeLog(log));\n let h = 0xcbf29ce484222325n;\n for (const b of bytes) {\n h ^= BigInt(b);\n h = (h * 0x100000001b3n) & 0xffffffffffffffffn;\n }\n return h.toString(16).padStart(16, \"0\");\n}\n","/**\n * The Vitest regression emitter. Given a violation, produce a test file that\n * replays its shrunk schedule on the restricted scenario. By default the\n * test expects the invariants to hold, so it fails while the bug exists and\n * passes once it is fixed. With `expect: \"violates\"` it asserts the recorded\n * violation instead, which is how Eutaxy's own corpus exercises the emitter.\n *\n * The emitter knows nothing about any platform: the caller supplies the\n * import lines and the expression that builds the platform.\n */\n\nimport { canonicalJson, type JsonValue, type Violated } from \"@eutaxy/core\";\n\nexport interface RegressionTestOptions {\n /** Import lines for the scenario, the platform's dependencies, and anything the platform expression needs. */\n readonly imports: readonly string[];\n /** An expression evaluating to the `Platform` (may reference the imports). */\n readonly platform: string;\n /** An expression evaluating to the `ScenarioSpec`. */\n readonly scenario: string;\n /** Module that exports `replay`, `restrictSpec` and `checkInvariants`; the public facade by default. */\n readonly engineModule?: string;\n readonly expect?: \"holds\" | \"violates\";\n}\n\nexport function emitRegressionTest(verdict: Violated, options: RegressionTestOptions): string {\n const engineModule = options.engineModule ?? \"@mooloo-ai/eutaxy\";\n const mode = options.expect ?? \"holds\";\n const schedule = canonicalJson(verdict.schedule as unknown as JsonValue);\n const kept = canonicalJson([...verdict.keptEvents]);\n const keys = canonicalJson([...verdict.violation.keys]);\n const title =\n mode === \"holds\"\n ? `${verdict.scenario}: ${verdict.violation.invariant} holds on the recorded schedule`\n : `${verdict.scenario}: ${verdict.violation.invariant} is still violated on the recorded schedule`;\n const assertion =\n mode === \"holds\"\n ? ` expect(violations.map((v) => \\`\\${v.invariant}: \\${v.message}\\`)).toEqual([]);`\n : ` expect(violations.map((v) => [v.invariant, v.keys])).toContainEqual([${JSON.stringify(verdict.violation.invariant)}, ${keys}]);`;\n const lines = [\n \"// Generated by Eutaxy. Replays a recorded violation; regenerate rather than edit.\",\n `// Scenario ${verdict.scenario}, seed ${String(verdict.seed)}: ${verdict.violation.message}`,\n `import { checkInvariants, replay, restrictSpec } from ${JSON.stringify(engineModule)};`,\n ...options.imports,\n 'import { expect, test } from \"vitest\";',\n \"\",\n `const schedule = ${schedule} as const;`,\n `const keptEvents = ${kept} as const;`,\n \"\",\n `test(${JSON.stringify(title)}, async () => {`,\n ` const platform = ${options.platform};`,\n ` const spec = restrictSpec(platform, ${options.scenario}, keptEvents);`,\n \" const log = await replay(spec, platform, schedule);\",\n \" const violations = checkInvariants(log, spec.invariants);\",\n assertion,\n \"});\",\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n","/**\n * Rendering verdicts: a text form for people and a versioned JSON form for\n * CI and coding agents. Both are pure functions over the verdict data.\n */\n\nimport { canonicalJson, type EffectLog, type JsonValue, type ScenarioVerdict } from \"@eutaxy/core\";\n\nexport const reportSchemaVersion = 1;\n\n/** The JSON report: one object, canonical, with a schema version at the top. */\nexport function renderJson(verdict: ScenarioVerdict): string {\n return canonicalJson({\n schemaVersion: reportSchemaVersion,\n generator: \"eutaxy\",\n verdict: verdict as unknown as JsonValue,\n });\n}\n\nfunction clip(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 1)}…`;\n}\n\nfunction pad(s: string, width: number): string {\n return s.length >= width ? s : s + \" \".repeat(width - s.length);\n}\n\n/** The trace as a fixed-width table; evidence rows are marked with `*`. */\nexport function renderTrace(log: EffectLog, evidence: readonly number[] = []): string {\n const rows = log.entries.map((e) => [\n evidence.includes(e.seq) ? \"*\" : \" \",\n String(e.seq),\n e.invocation,\n e.kind,\n e.target,\n e.op,\n clip(canonicalJson(e.payload), 72),\n ]);\n const header = [\" \", \"seq\", \"invocation\", \"kind\", \"target\", \"op\", \"payload\"];\n const widths = header.map((h, i) =>\n Math.max(h.length, ...rows.map((r) => (r[i] as string).length)),\n );\n const line = (cells: readonly string[]) =>\n cells\n .map((c, i) => (i === cells.length - 1 ? c : pad(c, widths[i] as number)))\n .join(\" \")\n .trimEnd();\n return [line(header), ...rows.map(line)].join(\"\\n\");\n}\n\n/** A readable report: verdict, what it rests on, and for a violation the schedule and trace. */\nexport function renderText(verdict: ScenarioVerdict): string {\n const out: string[] = [];\n const r = verdict.reached;\n out.push(`${verdict.kind} ${verdict.scenario} (seed ${String(verdict.seed)})`);\n switch (verdict.kind) {\n case \"VIOLATED\": {\n out.push(\"\");\n out.push(\n `Invariant ${verdict.violation.invariant} is violated: ${verdict.violation.message}`,\n );\n out.push(`Keys: ${verdict.violation.keys.join(\", \")}`);\n out.push(\"\");\n out.push(\n `Schedule (run seed ${verdict.schedule.seed}), events kept: ${verdict.keptEvents.join(\", \")}`,\n );\n if (verdict.schedule.faults.length === 0) out.push(\" no injected faults\");\n for (const f of verdict.schedule.faults) {\n out.push(` ${f.fault} at ${f.invocation} effect #${f.ordinal} (${f.target} ${f.op})`);\n }\n out.push(\n `Shrunk: ${verdict.shrunk.eventsRemoved} event(s) and ${verdict.shrunk.faultsRemoved} fault(s) removed in ${verdict.shrunk.runs} run(s)`,\n );\n out.push(\"\");\n out.push(\"Trace:\");\n out.push(renderTrace(verdict.trace, verdict.violation.evidence));\n break;\n }\n case \"BOUNDED\": {\n const b = verdict.bounds;\n out.push(\"\");\n out.push(\"No violation found within the stated bounds. This is not a proof.\");\n out.push(\n `Bounds: faults per run ≤ ${b.maxFaultsPerRun}, redeliveries ≤ ${b.maxRedeliveries}, concurrent invocations ≤ ${b.maxConcurrentInvocations}, budget ${b.budgetMs} ms`,\n );\n break;\n }\n case \"UNSUPPORTED\": {\n out.push(\"\");\n out.push(`Eutaxy could not decide this scenario (${verdict.cause}): ${verdict.reason}`);\n break;\n }\n }\n out.push(\"\");\n out.push(\n `Explored: ${r.runs} run(s), ${r.effectPoints} effect point(s), ${r.faultsInjected} fault(s) injected, ${r.elapsedMs} ms`,\n );\n out.push(\"Assumptions:\");\n for (const a of verdict.assumptions) out.push(` - ${a}`);\n return out.join(\"\\n\");\n}\n"],"mappings":"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"}
|