@demlik/tea 0.20.0 → 0.22.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/README.md +11 -2
- package/dist/agent/index.d.ts +36 -23
- package/dist/agent/index.js +12 -10
- package/dist/{chunk-UHAQ4PI6.js → chunk-2A5XTKQQ.js} +205 -59
- package/dist/chunk-2A5XTKQQ.js.map +1 -0
- package/dist/{chunk-JAKM53JF.js → chunk-7K7SEYCH.js} +16 -15
- package/dist/chunk-7K7SEYCH.js.map +1 -0
- package/dist/{chunk-RKVUVALV.js → chunk-A3QPMX5M.js} +31 -9
- package/dist/chunk-A3QPMX5M.js.map +1 -0
- package/dist/{chunk-DVNXEB7V.js → chunk-BVFS3ICC.js} +3 -3
- package/dist/chunk-BVFS3ICC.js.map +1 -0
- package/dist/{chunk-FTCYY2DO.js → chunk-CPE5ZQXQ.js} +3 -3
- package/dist/{chunk-FTCYY2DO.js.map → chunk-CPE5ZQXQ.js.map} +1 -1
- package/dist/chunk-DEAPNMKO.js +11 -0
- package/dist/chunk-DEAPNMKO.js.map +1 -0
- package/dist/{chunk-XWIE5V7R.js → chunk-ETRNSJCR.js} +4 -4
- package/dist/{chunk-XWIE5V7R.js.map → chunk-ETRNSJCR.js.map} +1 -1
- package/dist/chunk-FXWDDNGD.js +98 -0
- package/dist/chunk-FXWDDNGD.js.map +1 -0
- package/dist/chunk-GFGOQIEC.js +10 -0
- package/dist/chunk-GFGOQIEC.js.map +1 -0
- package/dist/{chunk-2R62Q5UK.js → chunk-IAHEYPYJ.js} +3 -3
- package/dist/{chunk-2R62Q5UK.js.map → chunk-IAHEYPYJ.js.map} +1 -1
- package/dist/{chunk-NGYFBD5G.js → chunk-J5JNLXO7.js} +3 -3
- package/dist/{chunk-NGYFBD5G.js.map → chunk-J5JNLXO7.js.map} +1 -1
- package/dist/{chunk-72CN3VPY.js → chunk-NFOGYDRS.js} +5 -5
- package/dist/{chunk-72CN3VPY.js.map → chunk-NFOGYDRS.js.map} +1 -1
- package/dist/{chunk-A73IJ27X.js → chunk-R2YNDRGL.js} +3 -3
- package/dist/{chunk-A73IJ27X.js.map → chunk-R2YNDRGL.js.map} +1 -1
- package/dist/{chunk-5HVKDHY6.js → chunk-R5NXOPL2.js} +3 -3
- package/dist/{chunk-5HVKDHY6.js.map → chunk-R5NXOPL2.js.map} +1 -1
- package/dist/chunk-RMBZ7YH7.js +52 -0
- package/dist/chunk-RMBZ7YH7.js.map +1 -0
- package/dist/{chunk-AIXC33SE.js → chunk-SHXUSTHM.js} +62 -15
- package/dist/chunk-SHXUSTHM.js.map +1 -0
- package/dist/{chunk-TAKPC6SD.js → chunk-UGTC33VH.js} +4 -4
- package/dist/{chunk-TAKPC6SD.js.map → chunk-UGTC33VH.js.map} +1 -1
- package/dist/{chunk-EA47MI5Q.js → chunk-VH5MYJ2K.js} +4 -4
- package/dist/{chunk-EA47MI5Q.js.map → chunk-VH5MYJ2K.js.map} +1 -1
- package/dist/{chunk-2774I242.js → chunk-WZEDIUWR.js} +21 -20
- package/dist/chunk-WZEDIUWR.js.map +1 -0
- package/dist/{chunk-TGJLXZAU.js → chunk-ZRRET3RP.js} +11 -11
- package/dist/chunk-ZRRET3RP.js.map +1 -0
- package/dist/{core-CtvAyPpH.d.ts → core-DNDAEhO2.d.ts} +9 -12
- package/dist/devtools/index.d.ts +4 -4
- package/dist/do/index.d.ts +103 -38
- package/dist/do/index.js +40 -21
- package/dist/do/index.js.map +1 -1
- package/dist/{drive-loop-Dw1sGSsW.d.ts → drive-loop-DlOEv49c.d.ts} +2 -2
- package/dist/{durable-effects-Ump5lSdO.d.ts → durable-effects-wLWEdy9H.d.ts} +1 -1
- package/dist/effect/index.d.ts +121 -28
- package/dist/effect/index.js +45 -31
- package/dist/effect/index.js.map +1 -1
- package/dist/extension/index.d.ts +3 -3
- package/dist/extension/index.js +2 -2
- package/dist/extension/index.js.map +1 -1
- package/dist/flow/index.d.ts +17 -13
- package/dist/flow/index.js +15 -12
- package/dist/flow/index.js.map +1 -1
- package/dist/idempotency/index.d.ts +3 -3
- package/dist/idempotency/index.js +3 -3
- package/dist/{index-CrcoVfd_.d.ts → index-9P1PaXIV.d.ts} +2 -2
- package/dist/{index-D3NY3VCN.d.ts → index-BBLI2WWN.d.ts} +1 -1
- package/dist/{index-Dl71V7kt.d.ts → index-C1WhBZ6a.d.ts} +4 -5
- package/dist/{index-CKtEKGyp.d.ts → index-C92SgNWz.d.ts} +3 -3
- package/dist/{index-CZZ3HI-Z.d.ts → index-CAsUE_d1.d.ts} +4 -4
- package/dist/{index-EIgfl83J.d.ts → index-CO34esWT.d.ts} +2 -2
- package/dist/{index-Bk2hBUdK.d.ts → index-COeX9Bmz.d.ts} +11 -9
- package/dist/{index-CVU7SK3a.d.ts → index-Cg0QXnzf.d.ts} +2 -2
- package/dist/index.d.ts +9 -9
- package/dist/index.js +2 -2
- package/dist/jev/index.d.ts +15 -15
- package/dist/jev/index.js +5 -5
- package/dist/jev/index.js.map +1 -1
- package/dist/laws-DTtCDDlU.d.ts +135 -0
- package/dist/loop-DReKGV1X.d.ts +70 -0
- package/dist/{machine-Dtre2TmN.d.ts → machine-C0WhOAyB.d.ts} +7 -7
- package/dist/machine-viz/index.d.ts +1 -1
- package/dist/machine-viz/index.js +2 -2
- package/dist/mem/index.d.ts +4 -3
- package/dist/mem/index.js +2 -2
- package/dist/mem/index.js.map +1 -1
- package/dist/node/index.d.ts +57 -9
- package/dist/node/index.js +175 -68
- package/dist/node/index.js.map +1 -1
- package/dist/otel/index.d.ts +28 -12
- package/dist/otel/index.js +25 -21
- package/dist/otel/index.js.map +1 -1
- package/dist/paginate/index.d.ts +4 -4
- package/dist/paginate/index.js +5 -5
- package/dist/parity/index.d.ts +11 -15
- package/dist/parity/index.js +4 -9
- package/dist/parity/index.js.map +1 -1
- package/dist/pbt/index.d.ts +279 -67
- package/dist/pbt/index.js +299 -58
- package/dist/pbt/index.js.map +1 -1
- package/dist/persistence/index.d.ts +10 -12
- package/dist/persistence/index.js +4 -4
- package/dist/promise/index.d.ts +118 -21
- package/dist/promise/index.js +6 -4
- package/dist/react/index.d.ts +34 -21
- package/dist/react/index.js +271 -42
- package/dist/react/index.js.map +1 -1
- package/dist/resilience/index.d.ts +6 -6
- package/dist/resilience/index.js +5 -5
- package/dist/{run-fiBe7pOG.d.ts → run-Cnw4qXj2.d.ts} +105 -23
- package/dist/{runtime-types-DEluQgGO.d.ts → runtime-types-CP-ykwgf.d.ts} +157 -29
- package/dist/testing/effect/index.d.ts +162 -9
- package/dist/testing/effect/index.js +110 -10
- package/dist/testing/effect/index.js.map +1 -1
- package/dist/testing/index.d.ts +6 -8
- package/dist/testing/index.js +4 -24
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/promise/index.d.ts +4 -4
- package/dist/testing/promise/index.js +5 -5
- package/dist/timing/index.d.ts +4 -4
- package/dist/timing/index.js +4 -4
- package/dist/{types-CXNzlbAg.d.ts → types-S53WDy_h.d.ts} +1 -1
- package/dist/work-queue/index.d.ts +4 -4
- package/dist/work-queue/index.js +2 -2
- package/package.json +19 -17
- package/dist/chunk-2774I242.js.map +0 -1
- package/dist/chunk-AIXC33SE.js.map +0 -1
- package/dist/chunk-DVNXEB7V.js.map +0 -1
- package/dist/chunk-HX3C6LDK.js +0 -34
- package/dist/chunk-HX3C6LDK.js.map +0 -1
- package/dist/chunk-JAKM53JF.js.map +0 -1
- package/dist/chunk-RKVUVALV.js.map +0 -1
- package/dist/chunk-TGJLXZAU.js.map +0 -1
- package/dist/chunk-UHAQ4PI6.js.map +0 -1
package/dist/pbt/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pbt/arbitraries/ctx.ts","../../src/pbt/arbitraries/msg.ts","../../src/pbt/arbitraries/sequence.ts","../../src/pbt/runners/replay-fold.ts","../../src/pbt/runners/property.ts","../../src/pbt/util/msg-keys.ts"],"names":["fc2","fc3"],"mappings":"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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// `stubCtxThrowingProxy<Ctx>()` — a Proxy that satisfies the Ctx type-shape\n// at compile time but throws on every property access at runtime.\n//\n// Why this exists: PBT runs the pure reducer surface ONLY — `replay` never\n// invokes `interpret` and never starts a Sub, so Ctx should never be\n// dereferenced during property generation. But the substrate's `replay`\n// signature requires a `ctx: Ctx` value at compile time (it forwards ctx\n// into `init`, which on some machines IS pure but on others reads ctx). A\n// hand-rolled `{} as Ctx` would silently pass through any accidental ctx\n// access — exactly the silent footgun this proxy catches.\n//\n// Open question 2 (design doc): we ship this as the default. Tests that\n// genuinely need a working ctx (because their `init` reads from it) can\n// pass a real ctx value instead — the proxy is opt-in, not load-bearing.\n//\n// Strengthens invariant 6 (no silent failures — accidental ctx reads\n// produce a useful error, not a `undefined.foo` confusion later).\n// ---------------------------------------------------------------------------\n\n/**\n * Build a Ctx value whose every property access throws. Use as the `ctx`\n * argument to PBT runners when the reducer under test is pure (does not\n * read from ctx). Accidental ctx access during property generation\n * surfaces as a labelled error instead of a silent `undefined` or\n * downstream type-error.\n *\n * Returns a typed `Ctx` (via `as unknown as Ctx`) — the proxy satisfies the\n * type-shape requirement at the call site; the runtime contract is\n * \"throw on first read.\" The `as unknown` cast is the one boundary cast\n * this helper requires; the proxy is structurally a `Ctx` for every\n * property accessor.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import {\n * arbMsgSequence,\n * propertyTerminates,\n * stubCtxThrowingProxy,\n * } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type State = { type: \"idle\" } | { type: \"done\" };\n * type Msg = { type: \"go\" };\n * interface MyCtx { readonly someField: string }\n * declare const machine: Machine<State, Msg, never, never, MyCtx>;\n * declare const msgArb: fc.Arbitrary<Msg>;\n *\n * const ctx = stubCtxThrowingProxy<MyCtx>();\n * propertyTerminates(machine, ctx, arbMsgSequence(msgArb), (s) => s.type === \"done\");\n * // If anything reads ctx.someField during the run, an Error throws:\n * // \"@demlik/tea/pbt: ctx access denied (PBT operates on the pure\n * // reducer; ctx field 'someField' was read)\"\n * ```\n */\nexport function stubCtxThrowingProxy<Ctx>(): Ctx {\n const handler: ProxyHandler<object> = {\n get(_target, prop: string | symbol): unknown {\n // Allow vitest / fast-check internals to introspect via Symbol-keyed\n // checks (e.g. Symbol.toStringTag, Symbol.iterator). Returning\n // undefined on symbol keys keeps property-tree walks cheap; the\n // throw fires on real domain field reads.\n if (typeof prop === \"symbol\") return undefined;\n // Some shrinkers / pretty-printers in fast-check probe a small set\n // of conventional field names (`toJSON`, `toString`, `valueOf`,\n // `then` — the last for thenable-check). Return undefined for those\n // so the proxy doesn't fail a probe before the property body runs.\n if (\n prop === \"toJSON\" ||\n prop === \"toString\" ||\n prop === \"valueOf\" ||\n prop === \"then\"\n ) {\n return undefined;\n }\n throw new Error(\n `@demlik/tea/pbt: ctx access denied (PBT operates on the pure reducer; ` +\n `ctx field '${prop}' was read). Pass a real ctx value if the reducer's ` +\n `init reads from ctx.`,\n );\n },\n has(): boolean {\n return false;\n },\n ownKeys(): ArrayLike<string | symbol> {\n return [];\n },\n };\n return new Proxy({}, handler) as unknown as Ctx;\n}\n","// ---------------------------------------------------------------------------\n// Msg arbitrary builders.\n//\n// `MsgArbitraryTable<M>` is a strict mapped type — every Msg variant must\n// have an arbitrary entry, keyed by `M[\"type\"]`, narrowing the arbitrary's\n// `T` to `Extract<M, { type: K }>`. This mirrors the load-bearing discipline\n// of `Reducer<S, M, C>` and `Transitions<S, M, C>` in the substrate: adding\n// a Msg variant without an arbitrary fails to compile, not at runtime.\n//\n// `arbMsg(table, opts?)` collapses the table into a single `fc.Arbitrary<M>`\n// via `fc.oneof`. Optional per-variant weighting for sequence-skew tests.\n//\n// `arbConstantMsg(type)` / `arbRecordMsg(type, fields)` are sugar over\n// `fc.constant({ type })` / `fc.record({ type: fc.constant(type), ...fields })`.\n// They exist because every Msg variant arbitrary either (a) carries no\n// payload — `arbConstantMsg(\"stop\")` — or (b) carries a record of fields —\n// `arbRecordMsg(\"tick\", { now: fc.integer() })`. Naming the two shapes\n// prevents callers from hand-rolling `fc.record({ type: fc.constant(\"...\") })`\n// inconsistently across test files.\n//\n// Open question 1 from the design doc (strict vs Partial table): strict by\n// default. The mapped type catches drift at compile time; `arbMsgPartial`\n// sugar is a future addition iff friction becomes real.\n// ---------------------------------------------------------------------------\n\nimport * as fc from \"fast-check\";\n\n/**\n * Strict per-variant Msg arbitrary table. Mapped type: every key in\n * `M[\"type\"]` MUST have an arbitrary entry whose `T` is the narrowed\n * variant. Adding a new Msg variant without an entry is a compile error\n * (same load-bearing discipline as `Reducer<S, M, C>` in the substrate).\n *\n * @example\n * ```ts\n * import {\n * arbConstantMsg,\n * arbRecordMsg,\n * type MsgArbitraryTable,\n * } from \"@demlik/tea/pbt\";\n * import * as fc from \"fast-check\";\n *\n * type Msg = { type: \"start\" } | { type: \"tick\"; now: number };\n * const table: MsgArbitraryTable<Msg> = {\n * start: arbConstantMsg(\"start\"),\n * tick: arbRecordMsg(\"tick\", { now: fc.integer({ min: 0 }) }),\n * };\n * ```\n */\nexport type MsgArbitraryTable<M extends { type: string }> = {\n [K in M[\"type\"]]: fc.Arbitrary<Extract<M, { type: K }>>;\n};\n\n/**\n * Build a single `fc.Arbitrary<M>` from a `MsgArbitraryTable<M>`. Picks one\n * variant per generation via `fc.oneof`; optional per-variant `weights`\n * bias the distribution (default uniform).\n *\n * @param table Per-variant arbitrary table (strict — every variant required).\n * @param opts Optional bias: `weights` is a `Partial<Record<M[\"type\"], number>>`.\n * Variants without an explicit weight default to 1.\n *\n * @example\n * ```ts\n * import { arbConstantMsg, arbMsg, arbRecordMsg } from \"@demlik/tea/pbt\";\n * import * as fc from \"fast-check\";\n *\n * type Msg = { type: \"start\" } | { type: \"tick\"; now: number } | { type: \"stop\" };\n * const msgArb = arbMsg<Msg>({\n * start: arbConstantMsg(\"start\"),\n * tick: arbRecordMsg(\"tick\", { now: fc.integer() }),\n * stop: arbConstantMsg(\"stop\"),\n * }, { weights: { tick: 5, start: 1, stop: 1 } });\n * ```\n */\nexport function arbMsg<M extends { type: string }>(\n table: MsgArbitraryTable<M>,\n opts?: { weights?: Partial<Record<M[\"type\"], number>> },\n): fc.Arbitrary<M> {\n const weights = opts?.weights;\n const entries = Object.entries(table) as [M[\"type\"], fc.Arbitrary<M>][];\n if (entries.length === 0) {\n throw new Error(\"@demlik/tea/pbt: arbMsg table is empty\");\n }\n const weighted = entries.map(([key, arb]) => ({\n arbitrary: arb,\n weight: weights?.[key] ?? 1,\n }));\n return fc.oneof(...weighted);\n}\n\n/**\n * Build a payload-free Msg variant arbitrary. Sugar for\n * `fc.constant({ type })` that narrows the result to `{ type: T }` literal.\n *\n * @example\n * ```ts\n * import { arbConstantMsg } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * const stop: fc.Arbitrary<{ type: \"stop\" }> = arbConstantMsg(\"stop\");\n * ```\n */\nexport function arbConstantMsg<T extends string>(\n type: T,\n): fc.Arbitrary<{ type: T }> {\n return fc.constant({ type });\n}\n\n/**\n * Build a record-shaped Msg variant arbitrary. Sugar for\n * `fc.record({ type: fc.constant(type), ...fields })` that stamps the\n * discriminator literal and combines with the per-field arbitraries.\n *\n * @example\n * ```ts\n * import { arbRecordMsg } from \"@demlik/tea/pbt\";\n * import * as fc from \"fast-check\";\n *\n * const tick: fc.Arbitrary<{ type: \"tick\"; now: number }> =\n * arbRecordMsg(\"tick\", { now: fc.integer({ min: 0 }) });\n * ```\n */\nexport function arbRecordMsg<T extends string, R>(\n type: T,\n fields: { [K in keyof R]: fc.Arbitrary<R[K]> },\n): fc.Arbitrary<{ type: T } & R> {\n // `fc.record` builds an arbitrary of the merged-shape record. The\n // `type: fc.constant(type)` field stamps the literal discriminator;\n // the spread of `fields` provides every payload field's arbitrary.\n //\n // The intermediate `as Record<string, fc.Arbitrary<unknown>>` is the\n // single load-bearing cast — fc.record's parameter type is invariant in\n // its inferred field shape, and the merged literal-and-generic spread\n // doesn't unify with the strict `{ [K in keyof R]: Arbitrary<R[K]> }`\n // shape TS computes for us. The outer cast to `fc.Arbitrary<{ type: T }\n // & R>` restates the result shape we know to be correct at this\n // boundary. Mirrors the same discipline `fc.record` callers use\n // throughout the prior-art branch.\n const recordArbitraries: Record<string, fc.Arbitrary<unknown>> = {\n type: fc.constant(type),\n ...(fields as Record<string, fc.Arbitrary<unknown>>),\n };\n return fc.record(recordArbitraries) as unknown as fc.Arbitrary<\n { type: T } & R\n >;\n}\n","// ---------------------------------------------------------------------------\n// Msg-sequence arbitraries.\n//\n// `arbMsgSequence(msgArb, opts)` produces `fc.Arbitrary<readonly M[]>` —\n// fast-check's default-shrinker handles the array; the per-element shrinker\n// inside `msgArb` shrinks each variant. Optional `prefix` is prepended\n// verbatim (used by Test A: \"the bug-triggering Msg leads the sequence\").\n//\n// `arbGuidedSequence` is the state-aware sequence builder. In v1 it throws\n// — Phase 6 of the design doc lands the `fc.commands`-backed implementation.\n// The export exists today so the surface is forward-compatible: consumers\n// that need state-aware generation get a useful error pointing at the\n// design doc instead of \"is not a function.\"\n// ---------------------------------------------------------------------------\n\nimport * as fc from \"fast-check\";\nimport type { Cmd, Machine, Sub } from \"../../index\";\n\n/**\n * Build an arbitrary of Msg sequences over a per-Msg arbitrary. Optionally\n * bounded in length and / or prefixed with a fixed sequence.\n *\n * - `minLength` (default 0) and `maxLength` (default 30) bound the random\n * array length. `prefix` is prepended AFTER the random tail is built; the\n * final length is `prefix.length + tail.length`.\n * - `prefix` is the load-bearing escape hatch for \"the first msg is the\n * one that exercises the cell I care about\" (e.g. Test A's\n * `[{ type: \"ws:client:hello\" }]` prefix to land in the\n * `auditing.ws:client:hello` cell on the first transition).\n *\n * @example\n * ```ts\n * import { arbMsgSequence } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type Msg = { type: \"ws:client:hello\" } | { type: \"tick\" };\n * declare const msgArb: fc.Arbitrary<Msg>;\n *\n * arbMsgSequence(msgArb, { minLength: 1, maxLength: 30 });\n * arbMsgSequence(msgArb, { prefix: [{ type: \"ws:client:hello\" }], maxLength: 20 });\n * ```\n */\nexport function arbMsgSequence<M extends { type: string }>(\n msgArb: fc.Arbitrary<M>,\n opts?: {\n readonly minLength?: number;\n readonly maxLength?: number;\n readonly prefix?: readonly NoInfer<M>[];\n },\n): fc.Arbitrary<readonly M[]> {\n const minLength = opts?.minLength ?? 0;\n const maxLength = opts?.maxLength ?? 30;\n const prefix = opts?.prefix ?? [];\n const tail = fc.array(msgArb, { minLength, maxLength });\n if (prefix.length === 0) return tail;\n return tail.map((arr) => [...prefix, ...arr]);\n}\n\n/**\n * State-aware Msg sequence arbitrary — generates only msgs whose\n * precondition holds in the current state. Backed by `fc.commands`\n * internally. Planned for a later phase.\n *\n * **NOT IMPLEMENTED IN v1.** The export exists today so the surface is\n * forward-compatible; this ships in a later phase alongside\n * shrinkers. Calling it throws.\n */\nexport function arbGuidedSequence<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n _machine: Machine<S, M, C, U, Ctx>,\n _ctx: Ctx,\n _cells: {\n [K in M[\"type\"]]?: (\n state: S,\n ) => fc.Arbitrary<Extract<M, { type: K }>> | null;\n },\n _opts?: { maxLength?: number },\n): fc.Arbitrary<readonly M[]> {\n throw new Error(\n \"@demlik/tea/pbt: arbGuidedSequence is not implemented in v1. \" +\n \"Use arbMsgSequence with \" +\n \"an unguided arbMsg table for now; states that don't accept a given \" +\n \"msg should noop in the reducer (mapped-type discipline).\",\n );\n}\n","// ---------------------------------------------------------------------------\n// `foldEvents` — pure deterministic fold over a Msg sequence.\n//\n// PBT needs the per-step (prev/msg/next/cmds) structure to assert\n// step-shaped invariants. The substrate's `replay(machine, opts)` returns\n// only final state + cumulative cmds — insufficient. We use `replay` for\n// the boot step (calls `machine.init(loaded, ctx)` faithfully) then walk\n// msgs ourselves through `applyCell`, the substrate's own cell dispatch.\n//\n// Why walk cells directly instead of looping `replay` once per msg with\n// `loaded: prevState`? Because `init(loaded)` runs every call, and on\n// machines with non-trivial init transforms (e.g. the audit machine's\n// `auditing → attach_debugger` resume cell) that would either (a) re-fire\n// the transform on every step, or (b) require fragile cmd-subtraction to\n// isolate this step's cmds. Calling the cells directly keeps the fold pure\n// and the cmds per-step exactly.\n//\n// Dispatches through `applyCell` — the single primitive `run`/`replay` use —\n// so form classification agrees with production by construction (#275).\n//\n// `foldEvents` does NOT call `interpret`, does NOT touch a Store, does NOT\n// start Subs. The reducer surface is the contract.\n// ---------------------------------------------------------------------------\n\nimport {\n applyCell,\n type Cmd,\n type Machine,\n replay,\n type Sub,\n} from \"../../index\";\n\n/**\n * One unit in a fold trace — the four pieces of data a property\n * invariant typically asserts on. `prev` is the state before the msg\n * applied; `next` is the state after; `cmds` is the cmds emitted by this\n * single step (NOT the running total).\n */\nexport interface Step<S, M, C> {\n readonly prev: S;\n readonly msg: M;\n readonly next: S;\n readonly cmds: readonly C[];\n}\n\n/**\n * Fold a Msg sequence through a machine's pure reducer, returning the\n * per-step trace, every state in order, and the final state. Initial\n * state comes from `replay(machine, { msgs: [], ctx, loaded })` — i.e.\n * `machine.init(loaded ?? null, ctx)` faithfully. Init's emitted cmds\n * are NOT included in any `Step` (they're the boot moment, not a Msg).\n *\n * @param machine The machine under test.\n * @param ctx The Ctx — typically `stubCtxThrowingProxy<Ctx>()` when\n * init is pure. Pass a real ctx if init reads from it.\n * @param loaded Optional pre-init snapshot (the `loaded` arg to\n * `machine.init`). Defaults to `null` (fresh boot).\n * @param msgs The Msg sequence to fold.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import { foldEvents } from \"@demlik/tea/pbt\";\n *\n * type State = { type: \"idle\" } | { type: \"counting\"; n: number } | { type: \"stopped\" };\n * type Msg = { type: \"start\" } | { type: \"tick\" } | { type: \"stop\" };\n * interface Ctx { readonly tabId: number }\n * declare const machine: Machine<State, Msg, never, never, Ctx>;\n * declare const ctx: Ctx;\n *\n * const { states, steps, finalState } = foldEvents(machine, ctx, null, [\n * { type: \"start\" }, { type: \"tick\" }, { type: \"stop\" },\n * ]);\n * // steps[1] = { prev: { type: \"counting\", n: 0 }, msg: { type: \"tick\" }, ... }\n * ```\n */\nexport function foldEvents<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n loaded: NoInfer<S> | null,\n msgs: readonly NoInfer<M>[],\n): {\n readonly states: readonly S[];\n readonly steps: readonly Step<S, M, C>[];\n readonly finalState: S;\n} {\n // Boot through `replay` so `init` runs faithfully and we honor the\n // substrate's loaded-snapshot contract (init may transform the loaded\n // value before the first Msg lands — e.g. the audit machine flips\n // `debuggerAttached: false` on resume).\n const { state: initialState } = replay(machine, {\n msgs: [],\n ctx,\n loaded: loaded ?? null,\n });\n\n // Per-msg dispatch goes through `applyCell` — the same primitive `run` and\n // `replay` use — so this runner classifies the update form identically to\n // production (`formOf` honors the `__form` tag; #275).\n const states: S[] = [initialState];\n const steps: Step<S, M, C>[] = [];\n let current = initialState;\n\n for (const msg of msgs) {\n const [next, emitted] = applyCell<S, M, C>(machine, current, msg);\n steps.push({ prev: current, msg, next, cmds: [...emitted] });\n states.push(next);\n current = next;\n }\n\n return { states, steps, finalState: current };\n}\n","// ---------------------------------------------------------------------------\n// Property runners — three thin wrappers over `fc.assert(fc.property(...))`\n// that fold a generated Msg sequence through the machine via `foldEvents`\n// and pipe the result into a TEA-shaped predicate.\n//\n// - `propertyTerminates` — every sequence ends in a terminal state.\n// - `propertyInvariant` — predicate holds on every (prev, msg, next, cmds) step.\n// - `propertyTrace` — predicate holds on the full (trace, final) shape.\n//\n// Each runner accepts `numRuns` (passed through to fast-check), an optional\n// `loaded` snapshot (forwarded to `foldEvents`), and the Msg-sequence\n// arbitrary. They return `void`; failures throw via fast-check's standard\n// shrinking output.\n//\n// Default shrinker is fast-check's built-in (open question 4): drop-msg /\n// prefix shrinkers can be layered later but are opt-in, not default. The\n// built-in shrinker handles array-shape shrinking + per-element variant\n// shrinking automatically.\n// ---------------------------------------------------------------------------\n\nimport * as fc from \"fast-check\";\nimport type { Cmd, Machine, Sub } from \"../../index\";\nimport { foldEvents, type Step } from \"./replay-fold\";\n\n/** Common options shared by every property runner. */\ninterface PropertyOpts<S> {\n /** Pre-init snapshot — passed to `machine.init(loaded, ctx)`. Default `null`. */\n readonly loaded?: S | null;\n /** Number of generated sequences to test. Forwarded to `fc.assert`. */\n readonly numRuns?: number;\n /** Optional fast-check seed for reproducibility. */\n readonly seed?: number;\n}\n\n/** The `foldEvents` result a runner's `check` inspects. */\ntype FoldResult<S, M, C> = {\n readonly states: readonly S[];\n readonly steps: readonly Step<S, M, C>[];\n readonly finalState: S;\n};\n\n/**\n * Build the fast-check parameters shared by every runner: the 200-default\n * `numRuns` plus an optional `seed`. Single source of truth so a change to\n * the default lands in one place.\n */\nfunction resolveFcParams<M>(\n opts: PropertyOpts<unknown> | undefined,\n): fc.Parameters<[readonly M[]]> {\n return {\n numRuns: opts?.numRuns ?? 200,\n ...(opts?.seed !== undefined ? { seed: opts.seed } : {}),\n };\n}\n\n/**\n * The fold+assert core every runner shares: assert `check` over each\n * generated Msg sequence, folded once through the machine via `foldEvents`.\n * `check` throws on failure so the error message carries the offending\n * shape into fast-check's shrunk counter-example.\n */\nfunction runProperty<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: Ctx,\n seqArb: fc.Arbitrary<readonly M[]>,\n opts: PropertyOpts<S> | undefined,\n check: (result: FoldResult<S, M, C>) => void,\n): void {\n fc.assert(\n fc.property(seqArb, (msgs) => {\n check(foldEvents(machine, ctx, opts?.loaded ?? null, msgs));\n }),\n resolveFcParams<M>(opts),\n );\n}\n\n/**\n * Assert every generated Msg sequence ends in a terminal state. `terminal`\n * is the predicate that names the acceptable final states (often\n * `(s) => s.type === \"done\" || s.type === \"idle\"`).\n *\n * The \"every sequence\" property catches stuck states (machine gets to a\n * non-terminal state and no further msg can escape) AND silent loops\n * (every msg variant is a noop from a given state).\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import {\n * arbMsgSequence,\n * propertyTerminates,\n * stubCtxThrowingProxy,\n * } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type State = { type: \"idle\" } | { type: \"auditing\" } | { type: \"done\" };\n * type Msg = { type: \"start_audit\" } | { type: \"ws:audit:done\" };\n * interface BackgroundCtx { readonly tabId: number }\n * declare const auditBackgroundMachine: Machine<State, Msg, never, never, BackgroundCtx>;\n * declare const msgArb: fc.Arbitrary<Msg>;\n * const auditingState: State = { type: \"auditing\" };\n *\n * propertyTerminates(\n * auditBackgroundMachine,\n * stubCtxThrowingProxy<BackgroundCtx>(),\n * arbMsgSequence(msgArb, { minLength: 1, maxLength: 30 }),\n * (s) => s.type === \"done\" || s.type === \"idle\",\n * { loaded: auditingState, numRuns: 500 },\n * );\n * ```\n */\nexport function propertyTerminates<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n seqArb: fc.Arbitrary<readonly NoInfer<M>[]>,\n terminal: (state: NoInfer<S>) => boolean,\n opts?: NoInfer<PropertyOpts<S>>,\n): void {\n runProperty(machine, ctx, seqArb, opts, ({ finalState }) => {\n if (!terminal(finalState)) {\n // Throwing here (vs returning false) lets the error message carry\n // the offending final state — fast-check pretty-prints it inside\n // the shrunk counter-example.\n throw new Error(\n `propertyTerminates: final state did not match terminal predicate. ` +\n `Final: ${JSON.stringify(finalState)}`,\n );\n }\n });\n}\n\n/**\n * Assert a predicate holds on EVERY transition step in a generated\n * sequence. The predicate sees `{ prev, msg, next, cmds }` per step;\n * return true to accept, false (or throw) to fail.\n *\n * Use to pin a \"when X happens in state Y, cmd Z must be emitted\" rule.\n * The shrinker minimizes the offending sequence to the smallest Msg list\n * that still violates the predicate.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import { arbMsgSequence, propertyInvariant } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type State = { type: \"idle\" } | { type: \"auditing\" };\n * type Msg = { type: \"start_audit\" } | { type: \"ws:client:hello\" };\n * type Cmd = { type: \"send_ws\"; msg: { type: \"client:ready\" } };\n * interface BackgroundCtx { readonly tabId: number }\n * declare const auditBackgroundMachine: Machine<State, Msg, Cmd, never, BackgroundCtx>;\n * declare const ctx: BackgroundCtx;\n * declare const msgArb: fc.Arbitrary<Msg>;\n * const auditingState: State = { type: \"auditing\" };\n *\n * // Pin the resume-handshake fix: `ws:client:hello` in auditing must\n * // emit `send_ws { client:ready }`.\n * propertyInvariant(\n * auditBackgroundMachine, ctx,\n * arbMsgSequence(msgArb, { prefix: [{ type: \"ws:client:hello\" }] }),\n * ({ prev, msg, cmds }) =>\n * !(prev.type === \"auditing\" && msg.type === \"ws:client:hello\") ||\n * cmds.some((c) => c.type === \"send_ws\" && c.msg.type === \"client:ready\"),\n * { loaded: auditingState, numRuns: 200 },\n * );\n * ```\n */\nexport function propertyInvariant<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n seqArb: fc.Arbitrary<readonly NoInfer<M>[]>,\n invariant: (step: Step<NoInfer<S>, NoInfer<M>, NoInfer<C>>) => boolean,\n opts?: NoInfer<PropertyOpts<S>>,\n): void {\n runProperty(machine, ctx, seqArb, opts, ({ steps }) => {\n for (const step of steps) {\n if (!invariant(step)) {\n throw new Error(\n `propertyInvariant: failed at step msg=${JSON.stringify(step.msg)} ` +\n `prev=${JSON.stringify(step.prev)} next=${JSON.stringify(step.next)} ` +\n `cmds=${JSON.stringify(step.cmds)}`,\n );\n }\n }\n });\n}\n\n/**\n * Assert a predicate over the full trace plus the final state. Use when\n * the property is \"some Msg appeared in the trace before reaching the\n * final state\" or \"no two consecutive steps had the same state.type\" —\n * predicates that need the full sequence, not a single step.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import { propertyTrace } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type State = { type: \"auditing\" } | { type: \"done\" };\n * type Msg = { type: \"ws:audit:progress\" } | { type: \"ws:audit:done\" };\n * interface Ctx { readonly tabId: number }\n * declare const machine: Machine<State, Msg, never, never, Ctx>;\n * declare const ctx: Ctx;\n * declare const seqArb: fc.Arbitrary<readonly Msg[]>;\n * const auditingState: State = { type: \"auditing\" };\n *\n * propertyTrace(\n * machine, ctx, seqArb,\n * (steps, final) =>\n * final.type !== \"done\" ||\n * steps.some((step) => step.msg.type === \"ws:audit:done\"),\n * { loaded: auditingState, numRuns: 500 },\n * );\n * ```\n */\nexport function propertyTrace<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n seqArb: fc.Arbitrary<readonly NoInfer<M>[]>,\n predicate: (\n steps: readonly Step<NoInfer<S>, NoInfer<M>, NoInfer<C>>[],\n finalState: NoInfer<S>,\n ) => boolean,\n opts?: NoInfer<PropertyOpts<S>>,\n): void {\n runProperty(machine, ctx, seqArb, opts, ({ steps, finalState }) => {\n if (!predicate(steps, finalState)) {\n throw new Error(\n `propertyTrace: predicate failed on trace length=${steps.length}, ` +\n `final=${JSON.stringify(finalState)}`,\n );\n }\n });\n}\n","// ---------------------------------------------------------------------------\n// msgTypeKeys — read the Msg discriminant set at runtime from a machine's\n// Reducer record or Transitions table.\n//\n// TS Msg unions only exist at the type level; the discriminant strings are\n// erased at runtime. The reducer record / transitions table is the load-\n// bearing artifact that DOES carry the discriminants at runtime — its keys\n// (for Reducer) or any one cell's inner keys (for Transitions) are exactly\n// the set we need to build an `arbMsg` table without hand-listing them.\n//\n// Standalone helper: hand it a machine and it returns the Msg variants the\n// reducer/transitions record carries at runtime. `arbMsg` does NOT call it —\n// `MsgArbitraryTable<M>` enforces table coverage at compile time (adding a\n// variant without an arbitrary fails to compile, not at runtime). This helper\n// is for callers that want the discriminant set explicitly at runtime — e.g.\n// a test asserting a table covers every variant.\n// ---------------------------------------------------------------------------\n\nimport {\n type Cmd,\n msgKeysOf,\n type Reducer,\n type Transitions,\n type UpdateForm,\n} from \"../../index\";\n\n/**\n * Extract the Msg discriminant set at runtime from a machine. Useful for\n * asserting that a user-supplied `MsgArbitraryTable<M>` covers every variant\n * the reducer can handle.\n *\n * Takes the MACHINE (not the bare `update` record) so the reducer-vs-\n * transitions branch honors the authoritative `__form` tag via `msgKeysOf` —\n * the same classification production `run` uses (#275).\n *\n * - **Reducer form** — keys of the top-level record ARE the Msg variants.\n * - **Transitions form** — keys of any single state's inner record. Every\n * state's inner record must cover every Msg variant (mapped-type\n * discipline), so reading the first state's keys is equivalent to reading\n * the union.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import { msgTypeKeys } from \"@demlik/tea/pbt\";\n *\n * type State = { type: \"idle\" } | { type: \"auditing\" };\n * type Msg = { type: \"start_audit\" } | { type: \"stop_audit\" } | { type: \"window_created\" };\n * declare const auditBackgroundMachine: Machine<State, Msg, never, never, unknown>;\n *\n * msgTypeKeys(auditBackgroundMachine);\n * // → [\"start_audit\", \"stop_audit\", \"window_created\"]\n * ```\n */\nexport function msgTypeKeys<\n S,\n M extends { type: string },\n C extends Cmd,\n>(machine: {\n update:\n | Reducer<S, M, C>\n | ([S] extends [{ type: string }] ? Transitions<S, M, C> : never);\n __form?: UpdateForm;\n}): readonly string[] {\n return msgKeysOf(machine);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/pbt/arbitraries/ctx.ts","../../src/pbt/arbitraries/msg.ts","../../src/pbt/arbitraries/sequence.ts","../../src/pbt/runners/msg-tally.ts","../../src/pbt/runners/property.ts","../../src/pbt/util/msg-keys.ts"],"names":["fc","fc2","record","check"],"mappings":"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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// `stubCtxThrowingProxy<Ctx>()` — a Proxy that satisfies the Ctx type-shape\n// at compile time but throws on every property access at runtime.\n//\n// Why this exists: PBT runs the pure reducer surface ONLY — `replay` never\n// invokes `interpret` and never starts a Sub, so Ctx should never be\n// dereferenced during property generation. But the substrate's `replay`\n// signature requires a `ctx: Ctx` value at compile time (it forwards ctx\n// into `init`, which on some machines IS pure but on others reads ctx). A\n// hand-rolled `{} as Ctx` would silently pass through any accidental ctx\n// access — exactly the silent footgun this proxy catches.\n//\n// Open question 2 (design doc): we ship this as the default. Tests that\n// genuinely need a working ctx (because their `init` reads from it) can\n// pass a real ctx value instead — the proxy is opt-in, not load-bearing.\n//\n// Strengthens invariant 6 (no silent failures — accidental ctx reads\n// produce a useful error, not a `undefined.foo` confusion later).\n// ---------------------------------------------------------------------------\n\n/**\n * Build a Ctx value whose every property access throws. Use as the `ctx`\n * argument to PBT runners when the reducer under test is pure (does not\n * read from ctx). Accidental ctx access during property generation\n * surfaces as a labelled error instead of a silent `undefined` or\n * downstream type-error.\n *\n * Returns a typed `Ctx` (via `as unknown as Ctx`) — the proxy satisfies the\n * type-shape requirement at the call site; the runtime contract is\n * \"throw on first read.\" The `as unknown` cast is the one boundary cast\n * this helper requires; the proxy is structurally a `Ctx` for every\n * property accessor.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import {\n * arbMsgSequence,\n * propertyTerminates,\n * stubCtxThrowingProxy,\n * } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type State = { type: \"idle\" } | { type: \"done\" };\n * type Msg = { type: \"go\" };\n * interface MyCtx { readonly someField: string }\n * declare const machine: Machine<State, Msg, never, never, MyCtx>;\n * declare const msgArb: fc.Arbitrary<Msg>;\n *\n * const ctx = stubCtxThrowingProxy<MyCtx>();\n * propertyTerminates(machine, ctx, arbMsgSequence(msgArb), (s) => s.type === \"done\");\n * // If anything reads ctx.someField during the run, an Error throws:\n * // \"@demlik/tea/pbt: ctx access denied (PBT operates on the pure\n * // reducer; ctx field 'someField' was read)\"\n * ```\n */\nexport function stubCtxThrowingProxy<Ctx>(): Ctx {\n const handler: ProxyHandler<object> = {\n get(_target, prop: string | symbol): unknown {\n // Allow vitest / fast-check internals to introspect via Symbol-keyed\n // checks (e.g. Symbol.toStringTag, Symbol.iterator). Returning\n // undefined on symbol keys keeps property-tree walks cheap; the\n // throw fires on real domain field reads.\n if (typeof prop === \"symbol\") return undefined;\n // Some shrinkers / pretty-printers in fast-check probe a small set\n // of conventional field names (`toJSON`, `toString`, `valueOf`,\n // `then` — the last for thenable-check). Return undefined for those\n // so the proxy doesn't fail a probe before the property body runs.\n if (\n prop === \"toJSON\" ||\n prop === \"toString\" ||\n prop === \"valueOf\" ||\n prop === \"then\"\n ) {\n return undefined;\n }\n throw new Error(\n `@demlik/tea/pbt: ctx access denied (PBT operates on the pure reducer; ` +\n `ctx field '${prop}' was read). Pass a real ctx value if the reducer's ` +\n `init reads from ctx.`,\n );\n },\n has(): boolean {\n return false;\n },\n ownKeys(): ArrayLike<string | symbol> {\n return [];\n },\n };\n return new Proxy({}, handler) as unknown as Ctx;\n}\n","// ---------------------------------------------------------------------------\n// Msg arbitrary builders.\n//\n// `MsgArbitraryTable<M>` is a strict mapped type — every Msg variant must\n// have an arbitrary entry, keyed by `M[\"type\"]`, narrowing the arbitrary's\n// `T` to `Extract<M, { type: K }>`. This mirrors the load-bearing discipline\n// of `Reducer<S, M, C>` and `Transitions<S, M, C>` in the substrate: adding\n// a Msg variant without an arbitrary fails to compile, not at runtime.\n//\n// `arbMsg(table, opts?)` collapses the table into a single `fc.Arbitrary<M>`\n// via `fc.oneof`. Optional per-variant weighting for sequence-skew tests.\n//\n// `arbConstantMsg(type)` / `arbRecordMsg(type, fields)` are sugar over\n// `fc.constant({ type })` / `fc.record({ type: fc.constant(type), ...fields })`.\n// They exist because every Msg variant arbitrary either (a) carries no\n// payload — `arbConstantMsg(\"stop\")` — or (b) carries a record of fields —\n// `arbRecordMsg(\"tick\", { now: fc.integer() })`. Naming the two shapes\n// prevents callers from hand-rolling `fc.record({ type: fc.constant(\"...\") })`\n// inconsistently across test files.\n//\n// Open question 1 from the design doc (strict vs Partial table): strict by\n// default. The mapped type catches drift at compile time; `arbMsgPartial`\n// sugar is a future addition iff friction becomes real.\n// ---------------------------------------------------------------------------\n\nimport * as fc from \"fast-check\";\n\n/**\n * Strict per-variant Msg arbitrary table. Mapped type: every key in\n * `M[\"type\"]` MUST have an arbitrary entry whose `T` is the narrowed\n * variant. Adding a new Msg variant without an entry is a compile error\n * (same load-bearing discipline as `Reducer<S, M, C>` in the substrate).\n *\n * @example\n * ```ts\n * import {\n * arbConstantMsg,\n * arbRecordMsg,\n * type MsgArbitraryTable,\n * } from \"@demlik/tea/pbt\";\n * import * as fc from \"fast-check\";\n *\n * type Msg = { type: \"start\" } | { type: \"tick\"; now: number };\n * const table: MsgArbitraryTable<Msg> = {\n * start: arbConstantMsg(\"start\"),\n * tick: arbRecordMsg(\"tick\", { now: fc.integer({ min: 0 }) }),\n * };\n * ```\n */\nexport type MsgArbitraryTable<M extends { type: string }> = {\n [K in M[\"type\"]]: fc.Arbitrary<Extract<M, { type: K }>>;\n};\n\n/**\n * Build a single `fc.Arbitrary<M>` from a `MsgArbitraryTable<M>`. Picks one\n * variant per generation via `fc.oneof`; optional per-variant `weights`\n * bias the distribution (default uniform).\n *\n * @param table Per-variant arbitrary table (strict — every variant required).\n * @param opts Optional bias: `weights` is a `Partial<Record<M[\"type\"], number>>`.\n * Variants without an explicit weight default to 1.\n *\n * @example\n * ```ts\n * import { arbConstantMsg, arbMsg, arbRecordMsg } from \"@demlik/tea/pbt\";\n * import * as fc from \"fast-check\";\n *\n * type Msg = { type: \"start\" } | { type: \"tick\"; now: number } | { type: \"stop\" };\n * const msgArb = arbMsg<Msg>({\n * start: arbConstantMsg(\"start\"),\n * tick: arbRecordMsg(\"tick\", { now: fc.integer() }),\n * stop: arbConstantMsg(\"stop\"),\n * }, { weights: { tick: 5, start: 1, stop: 1 } });\n * ```\n */\nexport function arbMsg<M extends { type: string }>(\n table: MsgArbitraryTable<M>,\n opts?: { weights?: Partial<Record<M[\"type\"], number>> },\n): fc.Arbitrary<M> {\n const weights = opts?.weights;\n const entries = Object.entries(table) as [M[\"type\"], fc.Arbitrary<M>][];\n if (entries.length === 0) {\n throw new Error(\"@demlik/tea/pbt: arbMsg table is empty\");\n }\n const weighted = entries.map(([key, arb]) => ({\n arbitrary: arb,\n weight: weights?.[key] ?? 1,\n }));\n return fc.oneof(...weighted);\n}\n\n/**\n * Build a payload-free Msg variant arbitrary. Sugar for\n * `fc.constant({ type })` that narrows the result to `{ type: T }` literal.\n *\n * @example\n * ```ts\n * import { arbConstantMsg } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * const stop: fc.Arbitrary<{ type: \"stop\" }> = arbConstantMsg(\"stop\");\n * ```\n */\nexport function arbConstantMsg<T extends string>(\n type: T,\n): fc.Arbitrary<{ type: T }> {\n return fc.constant({ type });\n}\n\n/**\n * Build a record-shaped Msg variant arbitrary. Sugar for\n * `fc.record({ type: fc.constant(type), ...fields })` that stamps the\n * discriminator literal and combines with the per-field arbitraries.\n *\n * @example\n * ```ts\n * import { arbRecordMsg } from \"@demlik/tea/pbt\";\n * import * as fc from \"fast-check\";\n *\n * const tick: fc.Arbitrary<{ type: \"tick\"; now: number }> =\n * arbRecordMsg(\"tick\", { now: fc.integer({ min: 0 }) });\n * ```\n */\nexport function arbRecordMsg<T extends string, R>(\n type: T,\n fields: { [K in keyof R]: fc.Arbitrary<R[K]> },\n): fc.Arbitrary<{ type: T } & R> {\n // `fc.record` builds an arbitrary of the merged-shape record. The\n // `type: fc.constant(type)` field stamps the literal discriminator;\n // the spread of `fields` provides every payload field's arbitrary.\n //\n // The intermediate `as Record<string, fc.Arbitrary<unknown>>` is the\n // single load-bearing cast — fc.record's parameter type is invariant in\n // its inferred field shape, and the merged literal-and-generic spread\n // doesn't unify with the strict `{ [K in keyof R]: Arbitrary<R[K]> }`\n // shape TS computes for us. The outer cast to `fc.Arbitrary<{ type: T }\n // & R>` restates the result shape we know to be correct at this\n // boundary. Mirrors the same discipline `fc.record` callers use\n // throughout the prior-art branch.\n const recordArbitraries: Record<string, fc.Arbitrary<unknown>> = {\n type: fc.constant(type),\n ...(fields as Record<string, fc.Arbitrary<unknown>>),\n };\n return fc.record(recordArbitraries) as unknown as fc.Arbitrary<\n { type: T } & R\n >;\n}\n","// ---------------------------------------------------------------------------\n// Msg-sequence arbitraries.\n//\n// `arbMsgSequence(msgArb, opts)` produces `fc.Arbitrary<readonly M[]>` —\n// fast-check's default-shrinker handles the array; the per-element shrinker\n// inside `msgArb` shrinks each variant. Optional `prefix` is prepended\n// verbatim (used by Test A: \"the bug-triggering Msg leads the sequence\").\n//\n// `arbGuidedSequence(machine, ctx, cells, opts)` draws lists whose answers each\n// fit a Cmd really sent. It draws blind events first, then a `map` folds them\n// through the machine and gives each one its meaning, so a shrink can delete any\n// event and the rest still make a valid list. The fold is `./guided-fold.ts`,\n// which `@demlik/tea/testing/effect` shares. The route and its numbers are in\n// the ruling: https://github.com/kamp-us/demlik/issues/745#issuecomment-6552209764\n// ---------------------------------------------------------------------------\n\nimport * as fc from \"fast-check\";\nimport type { Cmd, Machine, Sub } from \"../../index\";\nimport {\n type Answerer,\n type BlindEvent,\n MAX_PICK,\n resolveGuided,\n} from \"./guided-fold\";\n\n/**\n * Build an arbitrary of Msg sequences over a per-Msg arbitrary. Optionally\n * bounded in length and / or prefixed with a fixed sequence.\n *\n * - `minLength` (default 0) and `maxLength` (default 30) bound the random\n * array length. `prefix` is prepended AFTER the random tail is built; the\n * final length is `prefix.length + tail.length`. `size` decides how much of\n * that bound the draws actually use.\n * - `prefix` is the load-bearing escape hatch for \"the first msg is the\n * one that exercises the cell I care about\" (e.g. Test A's\n * `[{ type: \"ws:client:hello\" }]` prefix to land in the\n * `auditing.ws:client:hello` cell on the first transition).\n *\n * @example\n * ```ts\n * import { arbMsgSequence } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type Msg = { type: \"ws:client:hello\" } | { type: \"tick\" };\n * declare const msgArb: fc.Arbitrary<Msg>;\n *\n * arbMsgSequence(msgArb, { minLength: 1, maxLength: 30 });\n * arbMsgSequence(msgArb, { prefix: [{ type: \"ws:client:hello\" }], maxLength: 20 });\n * arbMsgSequence(msgArb, { maxLength: 60, size: \"max\" });\n * ```\n */\nexport function arbMsgSequence<M extends { type: string }>(\n msgArb: fc.Arbitrary<M>,\n opts?: {\n readonly minLength?: number;\n readonly maxLength?: number;\n readonly prefix?: readonly NoInfer<M>[];\n /**\n * Forwarded to `fc.array`. Left out, fast-check's default size applies:\n * `\"small\"` unless `fc.configureGlobal` changed it, which draws at most\n * `2 * minLength + 10` Msgs however large `maxLength` is. So `maxLength: 60`\n * comes out about 10 long, and a state that takes more steps than that is\n * never reached. Pass `\"max\"` to draw up to `maxLength`.\n */\n readonly size?: fc.SizeForArbitrary;\n },\n): fc.Arbitrary<readonly M[]> {\n const minLength = opts?.minLength ?? 0;\n const maxLength = opts?.maxLength ?? 30;\n const prefix = opts?.prefix ?? [];\n const tail = fc.array(msgArb, { minLength, maxLength, size: opts?.size });\n if (prefix.length === 0) return tail;\n return tail.map((arr) => [...prefix, ...arr]);\n}\n\n/**\n * How one type of Cmd gets answered. `draw` runs before the fold, so a shrink\n * can shrink its value, and it cannot see the state. `make` runs in the fold and\n * gets the Cmd really sent, so an answer is `make: (cmd, v) => fetch.ok(cmd, v)`.\n * To draw both outcomes, have `draw` choose between them.\n */\nexport interface AnswerCell<K extends Cmd, V, M> {\n readonly draw: fc.Arbitrary<V>;\n readonly make: (cmd: K, value: V) => NoInfer<M>;\n}\n\n/**\n * What {@link arbGuidedSequence} draws from. `outside` holds the Msgs nothing\n * sends a Cmd for, by Msg type. `answers` holds an {@link AnswerCell} by Cmd type,\n * and `A` is the value each one draws, inferred from its `draw`. A Cmd type with no\n * cell is never answered. A Msg type in neither table is never drawn.\n */\nexport interface GuidedCells<\n M extends { type: string },\n C extends Cmd,\n A extends { readonly [K in C[\"type\"]]?: unknown },\n> {\n readonly outside?: {\n readonly [K in M[\"type\"]]?: fc.Arbitrary<Extract<M, { type: K }>>;\n };\n readonly answers?: {\n readonly [K in keyof A]: K extends C[\"type\"]\n ? AnswerCell<Extract<C, { type: K }>, A[K], M>\n : never;\n };\n}\n\n/**\n * Draw Msg lists in which every answer answers a Cmd that was sent and not yet\n * answered, so a law that something must happen can be checked on random lists.\n * The result is a plain list for any runner, such as `propertyTerminates`.\n *\n * It draws blind events, then folds them through the machine. An answer event\n * holds a pick and one value per answer cell, and the fold reads the pick modulo\n * the Cmds waiting at that point. With nothing waiting, the event is dropped.\n *\n * **Refusals.** An event whose Msg the state at that point has no cell for is\n * dropped too, in either update form, so a transitions machine can leave a Msg out\n * of a state and no list holds it there. A Cmd whose answer was refused keeps\n * waiting, and a later event can still answer it. A list therefore never tests\n * that a state refuses a Msg: send that Msg by hand for that.\n *\n * **Waiting** holds every Cmd that `init` and the steps have sent, that has a cell\n * in `answers`, and that has no answer yet. A Cmd sent twice waits twice. The fold\n * reads it off what the reducer returned, not `machine.cmds`, so a machine with no\n * `cmds` field gets the same lists, and a hand-written Cmd is answered like a\n * defined one. No answer is matched by its `cmd` field: the pick names the Cmd.\n *\n * A shrink deletes events and shrinks picks and values, and the fold reads what\n * is left, so a shrunk list holds to the same rule. Blind picks find a narrow\n * order less often than a guided draw would, which more runs pay for.\n *\n * A step that throws ends its list on the Msg that threw, so a runner's fold meets\n * the same throw and reports it as that list's failure.\n *\n * - `maxLength` (default 60) caps the events drawn, and dropped ones count, so a\n * list comes out shorter. `size` (default `\"max\"`) is forwarded to `fc.array`:\n * with most answers dropped, fast-check's `\"small\"` rarely gets a run anywhere.\n * - `loaded` starts the fold from a saved state. Give a runner the same one, or\n * its fold reads the list from a different start.\n *\n * Throws when `cells` has no cell at all, since that could only draw empty lists.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import {\n * arbConstantMsg,\n * arbGuidedSequence,\n * propertyTrace,\n * stubCtxThrowingProxy,\n * } from \"@demlik/tea/pbt\";\n * import * as fc from \"fast-check\";\n *\n * type Merge = { readonly type: \"merge\"; readonly pr: number };\n * type State = { type: \"idle\" } | { type: \"merging\" } | { type: \"landed\" };\n * type Msg = { type: \"start\" } | { type: \"merged\"; cmd: Merge; sha: string };\n * declare const machine: Machine<State, Msg, Merge, never, object>;\n * const ctx = stubCtxThrowingProxy<object>();\n *\n * const lists = arbGuidedSequence(machine, ctx, {\n * outside: { start: arbConstantMsg(\"start\") },\n * answers: {\n * merge: {\n * draw: fc.string(),\n * make: (cmd, sha) => ({ type: \"merged\", cmd, sha }),\n * },\n * },\n * });\n *\n * propertyTrace(\n * machine,\n * ctx,\n * lists,\n * (steps, final) =>\n * !steps.some((step) => step.msg.type === \"merged\") ||\n * final.type === \"landed\",\n * );\n * ```\n */\nexport function arbGuidedSequence<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n A extends { readonly [K in C[\"type\"]]?: unknown },\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n cells: GuidedCells<M, C, A>,\n opts?: {\n readonly maxLength?: number;\n readonly size?: fc.SizeForArbitrary;\n readonly loaded?: NoInfer<S> | null;\n },\n): fc.Arbitrary<readonly M[]> {\n type Cell = AnswerCell<C, unknown, M>;\n const outside = Object.values(cells.outside ?? {}) as fc.Arbitrary<M>[];\n const answers = Object.entries(cells.answers ?? {}) as [string, Cell][];\n const answerers = answers.map(\n ([cmdType, cell]): Answerer<C, M> => ({ cmdType, make: cell.make }),\n );\n\n const arms: fc.WeightedArbitrary<BlindEvent<M>>[] = [];\n if (outside.length > 0) {\n arms.push({\n weight: 1,\n arbitrary: fc\n .oneof(...outside)\n .map((msg): BlindEvent<M> => ({ kind: \"outside\", msg })),\n });\n }\n if (answers.length > 0) {\n arms.push({\n weight: 2,\n arbitrary: fc\n .tuple(\n fc.nat(MAX_PICK),\n fc.tuple(...answers.map(([, cell]) => cell.draw)),\n )\n .map(\n ([pick, values]): BlindEvent<M> => ({\n kind: \"answer\",\n pick,\n values,\n }),\n ),\n });\n }\n if (arms.length === 0) {\n throw new Error(\n \"@demlik/tea/pbt: arbGuidedSequence got no cells, in `outside` or `answers`, so it could only draw empty lists.\",\n );\n }\n\n const loaded = opts?.loaded ?? null;\n return fc\n .array(fc.oneof(...arms), {\n maxLength: opts?.maxLength ?? 60,\n size: opts?.size ?? \"max\",\n })\n .map((events) => resolveGuided(machine, ctx, loaded, answerers, events));\n}\n","// ---------------------------------------------------------------------------\n// `MsgTally` — what a property run's lists did, per Msg type.\n//\n// A property holds whether or not its lists reach anything, so a change can cut a\n// whole path with every law still true. The tally counts how often each Msg type\n// was drawn and how often it acted, so a run can say what it never reached.\n//\n// fast-check re-runs the property while it shrinks, so the tally closes at the\n// first failure: past that point the lists are shrink attempts, not lists the run\n// drew, and `numRuns` does not count them either.\n// ---------------------------------------------------------------------------\n\nimport { type Cmd, structuralHash } from \"../../index\";\nimport type { Step } from \"./replay-fold\";\n\n/** What a property run's lists did with one Msg type. */\nexport interface MsgCount {\n /** How many of this Msg type the run's lists held. */\n readonly drawn: number;\n /**\n * How many of those acted: the step's `next` differs from its `prev` by\n * `structuralHash`, or the step sent a Cmd.\n */\n readonly acted: number;\n}\n\n/**\n * What `structuralHash` renders a value to, with a list or plain object cut at its\n * opening bracket. No other rendering is a lone bracket, since a string is quoted,\n * and anything that isn't plain data throws here with `structuralHash`'s own words.\n */\nfunction outline(value: unknown): string {\n if (Array.isArray(value)) return \"[\";\n if (typeof value === \"object\" && value !== null) {\n const proto = Object.getPrototypeOf(value) as object | null;\n if (proto === Object.prototype || proto === null) return \"{\";\n }\n return structuralHash(value);\n}\n\n/**\n * Whether two values would differ by `structuralHash`, without building either\n * string. It stops at the first difference and never looks inside one object\n * reached from both sides, which is what keeps a `{ ...prev, n }` cheap however\n * much it shares with `prev`.\n */\nfunction differs(a: unknown, b: unknown): boolean {\n if (a === b) return false;\n const shape = outline(a);\n if (shape !== outline(b)) return true;\n if (shape === \"[\") return listsDiffer(a as unknown[], b as unknown[]);\n if (shape === \"{\") {\n return recordsDiffer(\n a as Record<string, unknown>,\n b as Record<string, unknown>,\n );\n }\n return false;\n}\n\n/** A hole renders as nothing, so a list that is one hole reads as an empty list. */\nconst slotsOf = (list: readonly unknown[]): readonly unknown[] =>\n list.length === 1 && !(0 in list) ? [] : list;\n\nfunction listsDiffer(a: readonly unknown[], b: readonly unknown[]): boolean {\n const left = slotsOf(a);\n const right = slotsOf(b);\n if (left.length !== right.length) return true;\n for (let i = 0; i < left.length; i++) {\n if (i in left !== i in right || differs(left[i], right[i])) return true;\n }\n return false;\n}\n\n/** A key the hash can't see reads as absent, and an absent key reads as `undefined`. */\nconst seenAt = (record: Record<string, unknown>, key: string): unknown =>\n Object.prototype.propertyIsEnumerable.call(record, key)\n ? record[key]\n : undefined;\n\nfunction recordsDiffer(\n a: Record<string, unknown>,\n b: Record<string, unknown>,\n): boolean {\n for (const key of new Set([...Object.keys(a), ...Object.keys(b)])) {\n if (differs(seenAt(a, key), seenAt(b, key))) return true;\n }\n return false;\n}\n\n/** The two ways a step acts. A cell that returns an equal new object does neither. */\nfunction classify<S, M, C extends Cmd>(\n step: Step<S, M, C>,\n): { readonly acted: boolean; readonly refusal: unknown } {\n if (step.cmds.length > 0) return { acted: true, refusal: null };\n try {\n return { acted: differs(step.prev, step.next), refusal: null };\n } catch (refusal) {\n return { acted: true, refusal };\n }\n}\n\nexport class MsgTally {\n readonly #counts = new Map<string, MsgCount>();\n #closed = false;\n #refusal: unknown = null;\n\n /** @param msgTypes The machine's Msg types, so one never drawn still reads zero. */\n constructor(msgTypes: readonly string[]) {\n for (const type of msgTypes) this.#counts.set(type, { drawn: 0, acted: 0 });\n }\n\n /** Count one list's steps, unless the run has already failed. */\n add<S, M extends { type: string }, C extends Cmd>(\n steps: readonly Step<S, M, C>[],\n ): void {\n if (this.#closed) return;\n for (const step of steps) {\n const { acted, refusal } = classify(step);\n const count = this.#counts.get(step.msg.type) ?? { drawn: 0, acted: 0 };\n this.#counts.set(step.msg.type, {\n drawn: count.drawn + 1,\n acted: count.acted + (acted ? 1 : 0),\n });\n this.#refusal ??= refusal;\n }\n }\n\n /** The run has failed. Whatever comes next is shrinking. */\n close(): void {\n this.#closed = true;\n }\n\n /** Each Msg type's count: the machine's types first, then any other drawn. */\n get counts(): ReadonlyMap<string, MsgCount> {\n return new Map(this.#counts);\n }\n\n /**\n * What `structuralHash` threw on the first value the comparison reached and\n * couldn't read. Such a step counts as acted, since nothing shows it didn't, so\n * \"never acted\" can't be trusted here.\n */\n get refusal(): unknown {\n return this.#refusal;\n }\n\n /** The Msg types at zero acted, each with what it drew. */\n get neverActed(): readonly (readonly [string, MsgCount])[] {\n return [...this.#counts].filter(([, count]) => count.acted === 0);\n }\n}\n","// ---------------------------------------------------------------------------\n// Property runners — three thin wrappers over `fc.check(fc.property(...))`\n// that fold a generated Msg sequence through the machine via `foldEvents`\n// and pipe the result into a TEA-shaped predicate.\n//\n// - `propertyTerminates` — every sequence ends in a terminal state.\n// - `propertyInvariant` — predicate holds on every (prev, msg, next, cmds) step.\n// - `propertyTrace` — predicate holds on the full (trace, final) shape.\n//\n// Each law is a plain function in `../laws.ts` (`lawTerminates`, `lawInvariant`,\n// `lawTrace`) that answers what broke, and the runner throws that with its own\n// name in front. The laws import no fast-check, so Effect's checker runs them too.\n//\n// Each runner takes the Msg-sequence arbitrary, an optional `loaded` saved\n// state (a value, or an arbitrary that draws one per run), and fast-check's\n// settings. A passing run returns a `PropertyRun`; a failing run throws a\n// `PropertyFailure` carrying that same `PropertyRun` under fast-check's own\n// report, plus a check's reason when fast-check filed that reason in `cause`.\n// `failOnNeverActed` throws one too, naming the Msg types a clean run never acted on.\n//\n// Before any check runs, every Cmd `init` and the steps sent is held to two rules:\n// a value with no string `type` always fails, and with `failOnUndeclaredCmd` on, so\n// does a `type` outside `machine.cmds`. Each check also gets `init`'s Cmds as its\n// last argument, since no `Step` holds them.\n//\n// Default shrinker is fast-check's built-in (open question 4): drop-msg /\n// prefix shrinkers can be layered later but are opt-in, not default. The\n// built-in shrinker handles array-shape shrinking + per-element variant\n// shrinking automatically.\n// ---------------------------------------------------------------------------\n\nimport * as fc from \"fast-check\";\nimport { type Cmd, type Machine, msgKeysOf, type Sub } from \"../../index\";\nimport { type LawBreak, lawInvariant, lawTerminates, lawTrace } from \"../laws\";\nimport { type MsgCount, MsgTally } from \"./msg-tally\";\nimport { type Fold, foldEvents, type Step } from \"./replay-fold\";\n\n/**\n * What every property runner takes: the saved state to start from, the two checks\n * below, and fast-check's `Parameters` (`numRuns` defaults to 200 here). Five of\n * those settings are left out, each because it would break what a runner promises:\n *\n * - `reporter` and `asyncReporter` stop a failing run from throwing. One set\n * through `fc.configureGlobal` still applies, as it does for `fc.assert`.\n * - `examples` has the shape of the drawn tuple, which an arbitrary `loaded`\n * changes.\n * - `timeout` applies only to async properties, and these runners are sync.\n * `interruptAfterTimeLimit` with `markInterruptAsFailure` is the limit that\n * works here.\n * - `plugins` is left out on every fast-check. Only the newest 4s have it, so a\n * call using it would compile and run on some of the versions tea promises and\n * not on others. `fc.timeout`, which it can carry, has the same limit as\n * `timeout`. fast-check 4.10.2 marks `interruptAfterTimeLimit`,\n * `markInterruptAsFailure`, `skipEqualValues`, `ignoreEqualValues` and `unbiased`\n * as deprecated in favor of plugins, and they still work on every version tea\n * promises, so they stay.\n */\nexport interface PropertyOpts<S>\n extends Omit<\n fc.Parameters,\n \"reporter\" | \"asyncReporter\" | \"examples\" | \"timeout\" | \"plugins\"\n > {\n /**\n * Saved state passed to `machine.init(loaded, ctx)`. Default `null`. An\n * arbitrary draws one per run, and a failure's counterexample shows the drawn\n * state beside the shrunk list. It can draw `null` as well, the nothing storage\n * hands back on a fresh start, which boots the machine fresh.\n */\n readonly loaded?: S | null | fc.Arbitrary<S | null>;\n /**\n * Fail a run whose laws all held when a Msg type never acted, naming each such\n * type, so a cut path can't stay green. Default `false`.\n *\n * A deep path needs a `maxLength` well past its depth as well as `size: \"max\"`\n * on `arbMsgSequence`. Lists too short to reach a healthy machine's deep state\n * name that healthy path as never acted, in the same report a cut path gives. At\n * the default `maxLength` of 30, a state 20 steps deep on 6 Msg types drawn evenly\n * fails this way, since only one draw in 6 climbs. Allow about the depth times the\n * number of Msg types, here `maxLength: 120`.\n *\n * This stays out of any run with a non-empty `path`, so drop `path` to have it\n * check again. A path with a colon, such as `\"0:0\"`, replays one shrunk failure. A\n * path with no colon, such as `\"3\"`, is what a failure with no shrinks reports, and\n * it is no replay once that failure is gone: fast-check draws a full `numRuns` of\n * lists starting from that one, and a cut path reads green. `path: \"\"` is no path\n * to fast-check, so this applies there.\n *\n * A value `structuralHash` refuses (a `Date`, a `Map`, a class instance) leaves\n * \"acted\" unreadable when a step's comparison reaches it, so this fails the run\n * saying so. The comparison stops at the first difference and doesn't look inside\n * an object `prev` and `next` share, so a refused value stays unread, and fails\n * nothing, when it sits in a shared object or past a difference. The answer is\n * right either way: a shared object didn't change, and a difference means the\n * step acted. Fields are compared in the order the state's keys were set.\n */\n readonly failOnNeverActed?: boolean;\n /**\n * Fail a run when `init` or a step sends a Cmd whose `type` isn't in\n * `machine.cmds`, naming that Cmd. Default `false`, because tea lets a machine\n * keep a hand-written Cmd beside its defined ones. A machine with no `cmds` field\n * has no list to check against, so this adds nothing there. `cmds: []` is a list\n * with nothing on it, so every Cmd fails.\n *\n * A value with no string `type` is never a Cmd, so it fails with this off too, on\n * every machine.\n */\n readonly failOnUndeclaredCmd?: boolean;\n}\n\n/**\n * What a property run did. A passing runner returns it, and a failing one throws\n * it on {@link PropertyFailure.run}, so a caller reads one shape on both arms and\n * never parses error text.\n */\nexport interface PropertyRun {\n /** Lists tried. On a failure, this counts up to and including the failing one. */\n readonly numRuns: number;\n /** Shrink steps a failure took. `0` on a pass. */\n readonly numShrinks: number;\n /** The seed this run drew from. Pass it back as `seed` to draw the same lists. */\n readonly seed: number;\n /**\n * Where the shrunk failure sits. Pass it back as `path`, with `seed`, to replay\n * that one failure. `null` when no list failed: on a pass, on an interrupt\n * marked as a failure, when too many `fc.pre` skips fail the run, or when\n * `failOnNeverActed` fails it.\n */\n readonly path: string | null;\n /**\n * What the run's lists did, per Msg type: every type the machine has a cell for,\n * then any other that was drawn. A type never drawn reads zero. On a failure this\n * stops at the failing list, so shrinking adds nothing, and a list skipped with\n * `fc.pre` adds nothing either. A list whose reducer threw adds nothing as well,\n * though `numRuns` includes it: its fold hands back no steps to count.\n *\n * A step counts as acted when its comparison reaches a value `structuralHash`\n * refuses (a `Date`, a `Map`, a class instance). One in an object `prev` and `next`\n * share, or past a difference, is never reached, so it changes no count.\n */\n readonly msgCounts: ReadonlyMap<string, MsgCount>;\n}\n\n/**\n * Raised when a property fails. The message is fast-check's report, and\n * {@link PropertyFailure.run} holds the same {@link PropertyRun} a pass returns.\n * What the check threw sits where `fc.assert` puts it on the installed fast-check:\n * on 4, in `cause` by default and in the message with `includeErrorInReport`; on 3,\n * in the message by default and in `cause` with `errorWithCause`, on the 3s that have\n * that setting. A reason the check returned is always in the message: when it went to\n * `cause`, the message is the report plus a `reason:` line. A run that `failOnNeverActed` fails broke no list,\n * so fast-check has no report: the message names the Msg types instead.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import { PropertyFailure, propertyInvariant } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type State = { type: \"idle\" } | { type: \"done\" };\n * type Msg = { type: \"finish\" };\n * declare const machine: Machine<State, Msg, never, never, object>;\n * declare const seqArb: fc.Arbitrary<readonly Msg[]>;\n *\n * try {\n * propertyInvariant(machine, {}, seqArb, ({ next }) => next.type === \"idle\");\n * } catch (error) {\n * if (error instanceof PropertyFailure) {\n * // Replay this one failure with `{ seed, path }`.\n * const { numShrinks, seed, path } = error.run;\n * console.log(numShrinks, seed, path);\n * }\n * }\n * ```\n */\nexport class PropertyFailure extends Error {\n override readonly name = \"PropertyFailure\";\n readonly _tag = \"PropertyFailure\" as const;\n constructor(\n report: string,\n /** The run that failed. */\n public readonly run: PropertyRun,\n options?: ErrorOptions,\n ) {\n super(report, options);\n }\n}\n\n/**\n * A check's failure that says why. The reason is text fast-check would otherwise\n * bury where it files what a check threw, so {@link settle} reads it back here\n * rather than out of that text.\n *\n * Every fast-check prints the stack, which opens with the message. The oldest 3s also\n * print `String(error)` ahead of it, so that leaves the reason out to say it once.\n */\nclass BrokenLaw extends Error {\n constructor(\n private readonly report: string,\n readonly reason: string,\n ) {\n super(`${report}\\nreason: ${reason}`);\n this.name = \"Error\";\n }\n\n override toString(): string {\n return `${this.name}: ${this.report}`;\n }\n}\n\n/**\n * Read what a check answered: `true` and `null` hold, `false` and any string fail.\n * An empty string fails with nothing to say, so it reads as `false`. `report` is\n * what the runner prints today, built only when there is a failure to print.\n */\nfunction assertHolds(\n answer: boolean | string | null,\n report: () => string,\n): void {\n if (answer === true || answer === null) return;\n if (answer === false || answer === \"\") throw new Error(report());\n throw new BrokenLaw(report(), answer);\n}\n\n/**\n * Throw what a law said broke, its report led by the runner's name, the way\n * {@link assertHolds} throws a check's answer. A held law throws nothing.\n */\nfunction assertLaw(runner: string, broken: LawBreak | null): void {\n if (broken === null) return;\n const report = `${runner}: ${broken.report}`;\n throw broken.reason === null\n ? new Error(report)\n : new BrokenLaw(report, broken.reason);\n}\n\n/**\n * What is wrong with a sent Cmd, naming it, or `null` when nothing is. `declared`\n * is the list to hold it to, or `null` when there is none to read. Types can't\n * rule these out: a reducer's `as` or a mutated line reaches here at run time.\n */\nfunction cmdFault(\n cmd: unknown,\n declared: ReadonlySet<string> | null,\n): string | null {\n const { type } = Object(cmd) as { readonly type?: unknown };\n if (typeof cmd !== \"object\" || typeof type !== \"string\") {\n return `${fc.stringify(cmd)} isn't a Cmd: it has no string \\`type\\``;\n }\n if (declared === null || declared.has(type)) return null;\n return `${fc.stringify(cmd)} isn't in machine.cmds: [${[...declared].join(\", \")}]`;\n}\n\n/** Hold every Cmd one fold sent, `init`'s first, to {@link cmdFault}. */\nfunction assertSentCmds<S, M, C>(\n { initCmds, steps }: Fold<S, M, C>,\n declared: ReadonlySet<string> | null,\n): void {\n for (const cmd of initCmds) {\n assertHolds(cmdFault(cmd, declared), () => \"init sent a bad Cmd\");\n }\n for (const { msg, cmds } of steps) {\n for (const cmd of cmds) {\n assertHolds(\n cmdFault(cmd, declared),\n () => `step msg=${fc.stringify(msg)} sent a bad Cmd`,\n );\n }\n }\n}\n\n/**\n * Tell a drawn `loaded` from a fixed one. A saved state is plain data that can\n * come back through JSON, so it never holds these functions, and testing for them\n * keeps working where `instanceof` breaks: two copies of fast-check installed.\n */\nfunction isArbitrary<S>(\n loaded: S | null | fc.Arbitrary<S | null> | undefined,\n): loaded is fc.Arbitrary<S | null> {\n if (typeof loaded !== \"object\" || loaded === null) return false;\n const { generate, shrink, canShrinkWithoutContext } = loaded as Partial<\n fc.Arbitrary<S | null>\n >;\n return (\n typeof generate === \"function\" &&\n typeof shrink === \"function\" &&\n typeof canShrinkWithoutContext === \"function\"\n );\n}\n\n/**\n * Drop the settings set to `undefined`. fast-check lays what it is handed over the\n * settings from `fc.configureGlobal` key by key, so a key holding `undefined` would\n * throw the global one away, and a global `seed` would turn into a random one.\n */\nfunction withoutUndefined<T extends object>(settings: T): Partial<T> {\n return Object.fromEntries(\n Object.entries(settings).filter(([, value]) => value !== undefined),\n ) as Partial<T>;\n}\n\n/**\n * The settings that decide whether `fc.assert` puts a `cause` on its error. tea\n * promises two majors of fast-check, and each knows only one of these: 3 reads\n * `errorWithCause` (off by default) and 4 reads `includeErrorInReport` (a `cause`\n * unless it is on). The oldest 3s have neither, and never attach a `cause`.\n */\ninterface CauseSettings {\n readonly errorWithCause?: boolean;\n readonly includeErrorInReport?: boolean;\n}\n\n/**\n * The `cause` `fc.assert` would attach to this failure. The loaded major picks the\n * rule: a missing `errorWithCause` can't, because the oldest 3s lack it as 4 does.\n */\nfunction causeOf<Ts>(details: fc.RunDetails<Ts>): ErrorOptions | undefined {\n const { errorWithCause, includeErrorInReport } =\n details.runConfiguration as CauseSettings;\n const major = Number(fc.__version.split(\".\")[0]);\n return (major >= 4 ? !includeErrorInReport : errorWithCause)\n ? { cause: details.errorInstance }\n : undefined;\n}\n\n/**\n * Turn fast-check's details into what the caller sees: the {@link PropertyRun}\n * on a pass, a thrown {@link PropertyFailure} on a failure. `fc.check` never\n * throws by itself, and this keeps the report and the `cause` `fc.assert` gives\n * on either major. fast-check's report is `undefined` exactly when nothing failed.\n *\n * A report leaves out what the check threw exactly when that went to the `cause`,\n * so a check's reason is added to the report there. The thrown error is the one\n * from the shrunk list, so the reason always describes the list printed beside it.\n *\n * A reporter set through `fc.configureGlobal` takes the details instead, on a pass\n * and on a failure, and nothing throws: what `fc.assert` does. It is read from the\n * global settings, not `details.runConfiguration`, which also holds one passed per\n * call, so a `reporter` that slips past the types still can't hide a failure.\n */\nfunction settle<Ts>(details: fc.RunDetails<Ts>, tally: MsgTally): PropertyRun {\n const run: PropertyRun = {\n numRuns: details.numRuns,\n numShrinks: details.numShrinks,\n seed: details.seed,\n path: details.counterexamplePath,\n msgCounts: tally.counts,\n };\n const { reporter } = fc.readConfigureGlobal();\n if (reporter) {\n // A global reporter takes any property's details, so `Ts` is nothing to it.\n reporter(details as fc.RunDetails<unknown>);\n return run;\n }\n const report = fc.defaultReportMessage(details);\n if (report === undefined) return run;\n const cause = causeOf(details);\n const broken = details.errorInstance;\n throw new PropertyFailure(\n cause && broken instanceof BrokenLaw\n ? `${report}\\n\\nreason: ${broken.reason}`\n : report,\n run,\n cause,\n );\n}\n\n/**\n * Fail a run whose laws all held when a Msg type never acted. No single list broke\n * this, so there is no shrunk list to show: the report names the types, and the\n * seed draws the same lists again.\n */\nfunction assertEveryTypeActed(run: PropertyRun, tally: MsgTally): void {\n const { refusal, neverActed } = tally;\n const ranOver = `after ${run.numRuns} runs, seed ${run.seed}`;\n if (refusal !== null) {\n const why = refusal instanceof Error ? refusal.message : String(refusal);\n throw new PropertyFailure(\n `Property failed ${ranOver}: failOnNeverActed can't tell what acted, because a state isn't plain data.\\n\\n${why}`,\n run,\n );\n }\n if (neverActed.length === 0) return;\n const named = neverActed.map(\n ([type, { drawn }]) => ` ${type} (drawn ${drawn})`,\n );\n throw new PropertyFailure(\n `Property failed ${ranOver}: ${neverActed.length} Msg type(s) never acted:\\n${named.join(\"\\n\")}`,\n run,\n );\n}\n\n/**\n * The fold+check core every runner shares: run `check` over each generated Msg\n * sequence, folded once through the machine via `foldEvents`. `check` throws on\n * failure so the error message carries the offending shape into fast-check's\n * shrunk counter-example.\n *\n * A fixed `loaded` keeps a one-argument property, so a seed draws what it always\n * drew. An arbitrary `loaded` draws as a second argument.\n *\n * A list counts once its fold has finished. A `fc.pre` skip adds nothing, as it\n * adds nothing to `numRuns`. Any other throw is the run's failure, so nothing after\n * that list is counted. A check's throw still counts its list, but a reducer's adds\n * nothing, though `numRuns` includes that list: `foldEvents` hands back no steps.\n *\n * The sent Cmds are held to their rules ahead of `check`, so a law that reads a junk\n * Cmd can't throw first and hide what is wrong.\n */\nfunction runProperty<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: Ctx,\n seqArb: fc.Arbitrary<readonly M[]>,\n opts: PropertyOpts<S> | undefined,\n check: (result: Fold<S, M, C>) => void,\n): PropertyRun {\n const { loaded, failOnNeverActed, failOnUndeclaredCmd, ...settings } =\n opts ?? {};\n const params = {\n ...withoutUndefined(settings),\n numRuns: settings.numRuns ?? 200,\n };\n const tally = new MsgTally(msgKeysOf(machine));\n const declared =\n failOnUndeclaredCmd && machine.cmds\n ? new Set(machine.cmds.map((def) => def.cmdType))\n : null;\n const runList = (msgs: readonly M[], start: S | null): void => {\n let steps: readonly Step<S, M, C>[] = [];\n try {\n const result = foldEvents(machine, ctx, start, msgs);\n steps = result.steps;\n assertSentCmds(result, declared);\n check(result);\n } catch (error) {\n if (!fc.PreconditionFailure.isFailure(error)) {\n tally.add(steps);\n tally.close();\n }\n throw error;\n }\n tally.add(steps);\n };\n const checkActed = failOnNeverActed && !settings.path;\n const finish = <Ts>(details: fc.RunDetails<Ts>): PropertyRun => {\n const run = settle(details, tally);\n // A failed run gets here only past a global reporter, and its laws didn't hold.\n if (checkActed && !details.failed) assertEveryTypeActed(run, tally);\n return run;\n };\n return isArbitrary(loaded)\n ? finish(\n fc.check(\n fc.property(seqArb, loaded, (msgs, drawn) => {\n runList(msgs, drawn);\n }),\n params,\n ),\n )\n : finish(\n fc.check(\n fc.property(seqArb, (msgs) => {\n runList(msgs, loaded ?? null);\n }),\n params,\n ),\n );\n}\n\n/**\n * Assert every generated Msg sequence ends in a terminal state. `terminal`\n * is the predicate that names the acceptable final states (often\n * `(s) => s.type === \"done\" || s.type === \"idle\"`).\n *\n * The \"every sequence\" property catches stuck states (machine gets to a\n * non-terminal state and no further msg can escape) AND silent loops\n * (every msg variant is a noop from a given state).\n *\n * `terminal` also gets the Cmds `init` sent on that list's start.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import {\n * arbMsgSequence,\n * propertyTerminates,\n * stubCtxThrowingProxy,\n * } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type State = { type: \"idle\" } | { type: \"auditing\" } | { type: \"done\" };\n * type Msg = { type: \"start_audit\" } | { type: \"ws:audit:done\" };\n * interface BackgroundCtx { readonly tabId: number }\n * declare const auditBackgroundMachine: Machine<State, Msg, never, never, BackgroundCtx>;\n * declare const msgArb: fc.Arbitrary<Msg>;\n * const auditingState: State = { type: \"auditing\" };\n *\n * propertyTerminates(\n * auditBackgroundMachine,\n * stubCtxThrowingProxy<BackgroundCtx>(),\n * arbMsgSequence(msgArb, { minLength: 1, maxLength: 30 }),\n * (s) => s.type === \"done\" || s.type === \"idle\",\n * { loaded: auditingState, numRuns: 500 },\n * );\n * ```\n */\nexport function propertyTerminates<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n seqArb: fc.Arbitrary<readonly NoInfer<M>[]>,\n terminal: (state: NoInfer<S>, initCmds: readonly NoInfer<C>[]) => boolean,\n opts?: NoInfer<PropertyOpts<S>>,\n): PropertyRun {\n // Throwing (vs returning false) lets the error message carry the offending\n // final state — fast-check pretty-prints it inside the shrunk counter-example.\n return runProperty(machine, ctx, seqArb, opts, (fold) => {\n assertLaw(\"propertyTerminates\", lawTerminates(fold, terminal));\n });\n}\n\n/**\n * Assert a predicate holds on EVERY transition step in a generated\n * sequence. The predicate sees `{ prev, msg, next, cmds }` per step. Return\n * `true` or `null` to accept. Return `false`, or a string saying why, to fail: the\n * thrown error's message names that string. An empty string fails too. A throw\n * still fails with its own message.\n *\n * The predicate's second argument is the Cmds `init` sent on that list's start. It\n * runs once per step, so an empty list never shows them to it: use `propertyTrace`\n * for a rule about `init` alone.\n *\n * Use to pin a \"when X happens in state Y, cmd Z must be emitted\" rule.\n * The shrinker minimizes the offending sequence to the smallest Msg list\n * that still violates the predicate.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import { arbMsgSequence, propertyInvariant } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type State = { type: \"idle\" } | { type: \"auditing\" };\n * type Msg = { type: \"start_audit\" } | { type: \"ws:client:hello\" };\n * type Cmd = { type: \"send_ws\"; msg: { type: \"client:ready\" } };\n * interface BackgroundCtx { readonly tabId: number }\n * declare const auditBackgroundMachine: Machine<State, Msg, Cmd, never, BackgroundCtx>;\n * declare const ctx: BackgroundCtx;\n * declare const msgArb: fc.Arbitrary<Msg>;\n * const auditingState: State = { type: \"auditing\" };\n *\n * // Pin the resume-handshake fix: `ws:client:hello` in auditing must\n * // emit `send_ws { client:ready }`.\n * propertyInvariant(\n * auditBackgroundMachine, ctx,\n * arbMsgSequence(msgArb, { prefix: [{ type: \"ws:client:hello\" }] }),\n * ({ prev, msg, cmds }) =>\n * !(prev.type === \"auditing\" && msg.type === \"ws:client:hello\") ||\n * cmds.some((c) => c.type === \"send_ws\" && c.msg.type === \"client:ready\"),\n * { loaded: auditingState, numRuns: 200 },\n * );\n * ```\n */\nexport function propertyInvariant<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n seqArb: fc.Arbitrary<readonly NoInfer<M>[]>,\n invariant: (\n step: Step<NoInfer<S>, NoInfer<M>, NoInfer<C>>,\n initCmds: readonly NoInfer<C>[],\n ) => boolean | string | null,\n opts?: NoInfer<PropertyOpts<S>>,\n): PropertyRun {\n return runProperty(machine, ctx, seqArb, opts, (fold) => {\n assertLaw(\"propertyInvariant\", lawInvariant(fold, invariant));\n });\n}\n\n/**\n * Assert a predicate over the full trace plus the final state. Use when\n * the property is \"some Msg appeared in the trace before reaching the\n * final state\" or \"no two consecutive steps had the same state.type\" —\n * predicates that need the full sequence, not a single step.\n *\n * Return `true` or `null` to accept. Return `false`, or a string saying why, to\n * fail: the thrown error's message names that string. An empty string fails too.\n * A throw still fails with its own message. The last argument is the Cmds `init`\n * sent on that list's start.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import { propertyTrace } from \"@demlik/tea/pbt\";\n * import type * as fc from \"fast-check\";\n *\n * type State = { type: \"auditing\" } | { type: \"done\" };\n * type Msg = { type: \"ws:audit:progress\" } | { type: \"ws:audit:done\" };\n * interface Ctx { readonly tabId: number }\n * declare const machine: Machine<State, Msg, never, never, Ctx>;\n * declare const ctx: Ctx;\n * declare const seqArb: fc.Arbitrary<readonly Msg[]>;\n * const auditingState: State = { type: \"auditing\" };\n *\n * propertyTrace(\n * machine, ctx, seqArb,\n * (steps, final) =>\n * final.type !== \"done\" ||\n * steps.some((step) => step.msg.type === \"ws:audit:done\"),\n * { loaded: auditingState, numRuns: 500 },\n * );\n * ```\n */\nexport function propertyTrace<\n S,\n M extends { type: string },\n C extends Cmd,\n U extends Sub,\n Ctx,\n>(\n machine: Machine<S, M, C, U, Ctx>,\n ctx: NoInfer<Ctx>,\n seqArb: fc.Arbitrary<readonly NoInfer<M>[]>,\n predicate: (\n steps: readonly Step<NoInfer<S>, NoInfer<M>, NoInfer<C>>[],\n finalState: NoInfer<S>,\n initCmds: readonly NoInfer<C>[],\n ) => boolean | string | null,\n opts?: NoInfer<PropertyOpts<S>>,\n): PropertyRun {\n return runProperty(machine, ctx, seqArb, opts, (fold) => {\n assertLaw(\"propertyTrace\", lawTrace(fold, predicate));\n });\n}\n","// ---------------------------------------------------------------------------\n// msgTypeKeys — read the Msg discriminant set at runtime from a machine's\n// Reducer record or Transitions table.\n//\n// TS Msg unions only exist at the type level; the discriminant strings are\n// erased at runtime. The reducer record / transitions table is the load-\n// bearing artifact that DOES carry the discriminants at runtime — its keys\n// (for Reducer) or any one cell's inner keys (for Transitions) are exactly\n// the set we need to build an `arbMsg` table without hand-listing them.\n//\n// Standalone helper: hand it a machine and it returns the Msg variants the\n// reducer/transitions record carries at runtime. `arbMsg` does NOT call it —\n// `MsgArbitraryTable<M>` enforces table coverage at compile time (adding a\n// variant without an arbitrary fails to compile, not at runtime). This helper\n// is for callers that want the discriminant set explicitly at runtime — e.g.\n// a test asserting a table covers every variant.\n// ---------------------------------------------------------------------------\n\nimport {\n type Cmd,\n msgKeysOf,\n type Reducer,\n type Transitions,\n type UpdateForm,\n} from \"../../index\";\n\n/**\n * Extract the Msg discriminant set at runtime from a machine. Useful for\n * asserting that a user-supplied `MsgArbitraryTable<M>` covers every variant\n * the reducer can handle.\n *\n * Takes the MACHINE (not the bare `update` record) so the reducer-vs-\n * transitions branch honors the authoritative `__form` tag via `msgKeysOf` —\n * the same classification production `run` uses (#275).\n *\n * - **Reducer form** — keys of the top-level record ARE the Msg variants.\n * - **Transitions form** — keys of any single state's inner record. Every\n * state's inner record must cover every Msg variant (mapped-type\n * discipline), so reading the first state's keys is equivalent to reading\n * the union.\n *\n * @example\n * ```ts\n * import type { Machine } from \"@demlik/tea\";\n * import { msgTypeKeys } from \"@demlik/tea/pbt\";\n *\n * type State = { type: \"idle\" } | { type: \"auditing\" };\n * type Msg = { type: \"start_audit\" } | { type: \"stop_audit\" } | { type: \"window_created\" };\n * declare const auditBackgroundMachine: Machine<State, Msg, never, never, unknown>;\n *\n * msgTypeKeys(auditBackgroundMachine);\n * // → [\"start_audit\", \"stop_audit\", \"window_created\"]\n * ```\n */\nexport function msgTypeKeys<\n S,\n M extends { type: string },\n C extends Cmd,\n>(machine: {\n update:\n | Reducer<S, M, C>\n | ([S] extends [{ type: string }] ? Transitions<S, M, C> : never);\n __form?: UpdateForm;\n}): readonly string[] {\n return msgKeysOf(machine);\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { T as Trace } from '../index-
|
|
2
|
-
export { B as BreadcrumbsOptions, a as Recorder, R as RecorderOptions, b as TraceAttachment, c as TraceBreadcrumb, d as breadcrumbsFromTrace, p as parseJSONL, r as recorder, t as traceAttachment } from '../index-
|
|
3
|
-
export { c as SnapshotConfig, d as SnapshotFailedMsg, e as SnapshotKnob, f as SnapshotLoadCmd, g as SnapshotLoadFailedMsg, h as SnapshotLoadedMsg, b as SnapshotSavedMsg, a as SnapshotState, S as SnapshotWriteCmd, i as createSnapshot, s as snapshotLoadDef, j as snapshotWriteDef } from '../index-
|
|
4
|
-
import { C as Cmd,
|
|
5
|
-
import '../runtime-types-
|
|
1
|
+
import { T as Trace } from '../index-C1WhBZ6a.js';
|
|
2
|
+
export { B as BreadcrumbsOptions, a as Recorder, R as RecorderOptions, b as TraceAttachment, c as TraceBreadcrumb, d as breadcrumbsFromTrace, p as parseJSONL, r as recorder, t as traceAttachment } from '../index-C1WhBZ6a.js';
|
|
3
|
+
export { c as SnapshotConfig, d as SnapshotFailedMsg, e as SnapshotKnob, f as SnapshotLoadCmd, g as SnapshotLoadFailedMsg, h as SnapshotLoadedMsg, b as SnapshotSavedMsg, a as SnapshotState, S as SnapshotWriteCmd, i as createSnapshot, s as snapshotLoadDef, j as snapshotWriteDef } from '../index-COeX9Bmz.js';
|
|
4
|
+
import { C as Cmd, c as Sub, M as Machine } from '../core-DNDAEhO2.js';
|
|
5
|
+
import '../runtime-types-CP-ykwgf.js';
|
|
6
6
|
import '@standard-schema/spec';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -36,22 +36,20 @@ interface ReplayResult<S> {
|
|
|
36
36
|
* Replay a recorded {@link Trace} against `machine` and diff the recomputed
|
|
37
37
|
* final state against the recorded one.
|
|
38
38
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* supplied fresh by the caller: the trace is the INPUT (loaded + msgs); ctx is
|
|
43
|
-
* the ENVIRONMENT the reducer runs against now.
|
|
39
|
+
* Folds recorded messages from the captured post-init State, without calling
|
|
40
|
+
* init, Cmd handlers, Store operations or Sub runners. A trace without that
|
|
41
|
+
* anchor is refused; a fresh init cannot reconstruct its starting State.
|
|
44
42
|
*
|
|
45
43
|
* @param machine - the machine to replay against. Mutating its reducer (the
|
|
46
44
|
* whole point of a regression check) is what makes a recorded
|
|
47
45
|
* trace diverge.
|
|
48
46
|
* @param trace - a Trace from `../recorder` (live `dump()` or a re-hydrated
|
|
49
47
|
* `parseJSONL(...)`).
|
|
50
|
-
* @param
|
|
48
|
+
* @param _ctx - optional legacy argument; recorded replay needs no init context.
|
|
51
49
|
*/
|
|
52
50
|
declare function replayTrace<S, M extends {
|
|
53
51
|
type: string;
|
|
54
|
-
}, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, trace: Trace<S, M>,
|
|
52
|
+
}, C extends Cmd, U extends Sub, Ctx>(machine: Machine<S, M, C, U, Ctx>, trace: Trace<S, M>, _ctx?: Ctx): ReplayResult<S>;
|
|
55
53
|
/**
|
|
56
54
|
* Order-insensitive structural deep-equality — the comparator the
|
|
57
55
|
* record/replay lane owns, exposed so consumers (e.g. `@demlik/tea/parity`'s
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { breadcrumbsFromTrace, deepEqual, parseJSONL, recorder, replayTrace, traceAttachment } from '../chunk-
|
|
1
|
+
export { breadcrumbsFromTrace, deepEqual, parseJSONL, recorder, replayTrace, traceAttachment } from '../chunk-ZRRET3RP.js';
|
|
2
2
|
import '../chunk-HWVW4AXC.js';
|
|
3
|
-
export { createSnapshot, snapshotLoadDef, snapshotWriteDef } from '../chunk-
|
|
3
|
+
export { createSnapshot, snapshotLoadDef, snapshotWriteDef } from '../chunk-IAHEYPYJ.js';
|
|
4
4
|
import '../chunk-4LPY67E4.js';
|
|
5
|
-
import '../chunk-
|
|
6
|
-
import '../chunk-
|
|
5
|
+
import '../chunk-BVFS3ICC.js';
|
|
6
|
+
import '../chunk-A3QPMX5M.js';
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
package/dist/promise/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { P as Postable, T as TellAnswer } from '../loop-DReKGV1X.js';
|
|
2
|
+
import { C as Cmd, c as Sub, R as RunHandlers, M as Machine } from '../core-DNDAEhO2.js';
|
|
3
|
+
import { B as BootingRuntime, R as Runtime, C as CtxArg, S as StoreAndOnError, c as Supervision, T as TelemetrySink } from '../runtime-types-CP-ykwgf.js';
|
|
3
4
|
import '@standard-schema/spec';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -12,18 +13,12 @@ import '@standard-schema/spec';
|
|
|
12
13
|
* built from `run`'s options in the order both engines share.
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
* handlers, the Sub runners, `ctx` and an optional `Store`; the runtime is
|
|
18
|
-
* returned at once and every method waits for boot.
|
|
19
|
-
*/
|
|
20
|
-
declare function run<S, M extends {
|
|
16
|
+
/** The options of the Promise engine's `run`. */
|
|
17
|
+
type PromiseRunOptions<S, M extends {
|
|
21
18
|
type: string;
|
|
22
19
|
}, C extends Cmd, U extends Sub, Ctx, E extends {
|
|
23
20
|
type: string;
|
|
24
|
-
} =
|
|
25
|
-
store?: Store<S>;
|
|
26
|
-
onError?: OnError;
|
|
21
|
+
}> = CtxArg<Ctx> & NoInfer<RunHandlers<M, C, U, Ctx>> & StoreAndOnError<S> & {
|
|
27
22
|
/**
|
|
28
23
|
* The clock that stamps `at` on a `Cmd.define`d effect's settled Msg at
|
|
29
24
|
* the interpret edge, and on each `telemetry` event. Defaults to
|
|
@@ -47,6 +42,11 @@ declare function run<S, M extends {
|
|
|
47
42
|
/**
|
|
48
43
|
* The run-terminality predicate — makes the run's outcome first-class. Fed to
|
|
49
44
|
* `Runtime.result()` and `Runtime.done()`. PURE. Omit → never terminal.
|
|
45
|
+
*
|
|
46
|
+
* `done()` resolves with a saved terminal State. When a failure closes the
|
|
47
|
+
* run short of one, it rejects with that failure, for waiting and later
|
|
48
|
+
* callers: the save's own error, a `SubFailedError`, or what a reducer threw
|
|
49
|
+
* under the `stop` strategy. A `stop()` with no failure leaves it pending.
|
|
50
50
|
*/
|
|
51
51
|
terminal?: (state: S) => boolean;
|
|
52
52
|
/**
|
|
@@ -56,17 +56,31 @@ declare function run<S, M extends {
|
|
|
56
56
|
*/
|
|
57
57
|
telemetry?: TelemetrySink;
|
|
58
58
|
/**
|
|
59
|
-
* How long `stop()` waits for
|
|
60
|
-
*
|
|
61
|
-
*
|
|
59
|
+
* How long `stop()` waits for work it cannot interrupt before giving up
|
|
60
|
+
* on it. Defaults to 5_000ms. It bounds two waits, one after the other:
|
|
61
|
+
* the queue, where a Cmd handler may still be running, and then the
|
|
62
|
+
* teardown work that returned a Promise (an async `release` in
|
|
63
|
+
* `defineManagedResource`, an async Sub cleanup).
|
|
62
64
|
*
|
|
63
65
|
* `stop()` awaits those disposals so a host doing
|
|
64
66
|
* `await runtime.stop(); env.evict()` cannot drop the isolate mid-release —
|
|
65
67
|
* the leak the managed-resource battery exists to prevent, relocated to
|
|
66
|
-
* shutdown. The bound is what keeps a
|
|
67
|
-
* hanging the host: on expiry `stop()`
|
|
68
|
-
*
|
|
69
|
-
* because `stop()`
|
|
68
|
+
* shutdown. The bound is what keeps a handler that never returns, or a
|
|
69
|
+
* release that never settles, from hanging the host: on expiry `stop()`
|
|
70
|
+
* reports a `DrainTimeoutNotice` or a `DisposeTimeoutNotice` (warn-only,
|
|
71
|
+
* like every `RuntimeDiscardNotice`) and resolves anyway, because `stop()`
|
|
72
|
+
* resolving is a contract (ADR 0025). The Subs are still stopped and the
|
|
73
|
+
* final save still runs. A handler `stop()` gave up on keeps running;
|
|
74
|
+
* what it returns later is dropped.
|
|
75
|
+
*
|
|
76
|
+
* tea starts no store call after `stop()` has returned. A store call
|
|
77
|
+
* already under way when `stop()` gave up is not cancelled and can
|
|
78
|
+
* finish after `stop()` has returned. On a plain store that late save
|
|
79
|
+
* can overwrite what a newer run saved; a fenced store refuses it with
|
|
80
|
+
* `StoreConflictError`. So a host that starts a new run on the same
|
|
81
|
+
* store after a `stop()` that gave up needs a fenced store. A run
|
|
82
|
+
* stopped while its `store.load()` was pending saves nothing when the
|
|
83
|
+
* load returns: `ready` resolves with the loaded State, unsaved.
|
|
70
84
|
*/
|
|
71
85
|
disposeTimeoutMs?: number;
|
|
72
86
|
/**
|
|
@@ -76,7 +90,46 @@ declare function run<S, M extends {
|
|
|
76
90
|
* @internal test-only
|
|
77
91
|
*/
|
|
78
92
|
__idleCap?: number;
|
|
79
|
-
}
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* The handle `run` returns: a {@link BootingRuntime} whose `ready` resolves to a
|
|
96
|
+
* {@link Run}. It does not compile as `tell`'s argument: wait for `ready`.
|
|
97
|
+
*/
|
|
98
|
+
interface BootingRun<S, M extends {
|
|
99
|
+
type: string;
|
|
100
|
+
}, E extends {
|
|
101
|
+
type: string;
|
|
102
|
+
} = never> extends BootingRuntime<S, M, E> {
|
|
103
|
+
ready: Promise<Run<S, M, E>>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* What a {@link BootingRun}'s `ready` resolves to: a booted {@link Runtime} that
|
|
107
|
+
* also carries the run's line, which is what `tell` asks for. A spread keeps the
|
|
108
|
+
* line. An object built by hand cannot have one, because tea does not export its
|
|
109
|
+
* key, so a {@link Runtime} that is not one of these does not compile as `tell`'s
|
|
110
|
+
* argument. It fits anywhere a `Runtime` is asked for.
|
|
111
|
+
*/
|
|
112
|
+
interface Run<S, M extends {
|
|
113
|
+
type: string;
|
|
114
|
+
}, E extends {
|
|
115
|
+
type: string;
|
|
116
|
+
} = never> extends Runtime<S, M, E>, Postable<S, M> {
|
|
117
|
+
ready: Promise<Run<S, M, E>>;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Start a machine on the Promise engine and return its runtime. Pass the Cmd
|
|
121
|
+
* handlers, the Sub runners, `ctx` and an optional `Store`; the runtime is
|
|
122
|
+
* returned at once and every method waits for boot.
|
|
123
|
+
*
|
|
124
|
+
* Throws at once, before anything is loaded, when the store is fenced at runtime
|
|
125
|
+
* and no `onError` was passed, even when the store is typed plain `Store<S>`
|
|
126
|
+
* (ADR 0031).
|
|
127
|
+
*/
|
|
128
|
+
declare function run<S, M extends {
|
|
129
|
+
type: string;
|
|
130
|
+
}, C extends Cmd, U extends Sub, Ctx, E extends {
|
|
131
|
+
type: string;
|
|
132
|
+
} = never>(machine: Machine<S, M, C, U, Ctx>, opts: PromiseRunOptions<S, M, C, U, Ctx, E>): BootingRun<S, M, E>;
|
|
80
133
|
/**
|
|
81
134
|
* The outside-in stop, as a PAIR. A signal is only cancellation if something can
|
|
82
135
|
* turn "aborted" into a transition this machine understands, and the kernel has
|
|
@@ -139,6 +192,11 @@ type DriveToDoneOptions<S, M extends {
|
|
|
139
192
|
* observer is detached and `stop()` awaited on EVERY exit: resolve, `failed`,
|
|
140
193
|
* a stall, a boot or dispatch throw, the quiescence cap.
|
|
141
194
|
*
|
|
195
|
+
* Use this for a deliberate host `start` Msg after boot. Subs have already
|
|
196
|
+
* opened by then. A machine whose restored first move must precede their Msgs
|
|
197
|
+
* returns its resume Cmds and in-flight State from init instead; do not also
|
|
198
|
+
* use `start` to resume that same work.
|
|
199
|
+
*
|
|
142
200
|
* Rejections are typed, never silent:
|
|
143
201
|
* - `DriveFailedError<S>` when `opts.failed` marks the final State — the State
|
|
144
202
|
* rides on `error.state`.
|
|
@@ -171,7 +229,8 @@ type DriveToDoneOptions<S, M extends {
|
|
|
171
229
|
* throws later, since both leave the run with no State to settle on. A State the
|
|
172
230
|
* caller's `failed` predicate marks rejects on an abort exit exactly as it does
|
|
173
231
|
* anywhere else. In-flight effects are not recalled — a promise cannot be cancelled —
|
|
174
|
-
* so they settle to their own end and `stop()` drains them
|
|
232
|
+
* so they settle to their own end and `stop()` drains them, up to
|
|
233
|
+
* `disposeTimeoutMs`;
|
|
175
234
|
* keeping their results off the public channels is the machine's own business.
|
|
176
235
|
* Omit the pair and every path here behaves exactly as it did before.
|
|
177
236
|
*
|
|
@@ -188,4 +247,42 @@ declare function driveToDone<S, M extends {
|
|
|
188
247
|
type: string;
|
|
189
248
|
} = never>(handle: BootingRuntime<S, M, E>, start: M | ((booted: S) => M), isTerminal: (state: S) => boolean, opts?: DriveToDoneOptions<S, M>): Promise<S>;
|
|
190
249
|
|
|
191
|
-
|
|
250
|
+
/**
|
|
251
|
+
* Put `msg` on a run's line and return at once. The answer means queued, not
|
|
252
|
+
* handled: the receiver's step has not run yet, and any result comes back later
|
|
253
|
+
* as its own Msg. So two machines can tell each other from their Cmd handlers
|
|
254
|
+
* and neither stops. `machine` is the machine `runtime` runs.
|
|
255
|
+
*
|
|
256
|
+
* The answer is `"queued"`, `"stopped"` for a run that is stopping or has
|
|
257
|
+
* stopped, or `"no_cell"` when its current State has no cell for the Msg. Msgs
|
|
258
|
+
* already in line have not folded yet, so a Msg that only a queued one makes
|
|
259
|
+
* acceptable answers `"no_cell"`.
|
|
260
|
+
*
|
|
261
|
+
* `runtime` is what `ready` resolved to, or a spread of it. Those carry the run's
|
|
262
|
+
* line, and the type asks for it, so a `Runtime` built by hand, a fake or
|
|
263
|
+
* `noopRuntime` is a compile error. So is the handle `run` returned, with a store
|
|
264
|
+
* or without one: wait for `ready`. A hand-built object cast past the type has no
|
|
265
|
+
* line to put a Msg on, and `tell` throws there.
|
|
266
|
+
*
|
|
267
|
+
* What happens after `"queued"` is the receiver's. Two `tell`s from one sender
|
|
268
|
+
* fold in the order they were sent. A failed save or a handler's throw goes to
|
|
269
|
+
* the receiver's `onError`, never back to the sender. A Msg whose cell is gone
|
|
270
|
+
* by its turn is dropped without a word, and a `stop()` that gives up drops
|
|
271
|
+
* what is still in line and counts it, per Msg type, in its
|
|
272
|
+
* `DrainTimeoutNotice`.
|
|
273
|
+
*
|
|
274
|
+
* Two hand-written `await runtime.dispatch(...)` calls into each other, one from
|
|
275
|
+
* each machine's Cmd handler, still stop both machines: `dispatch` waits, and
|
|
276
|
+
* tea has no lock-up detector. Use `tell` there.
|
|
277
|
+
*
|
|
278
|
+
* ```ts
|
|
279
|
+
* const answer = tell(parent, parentRun, { type: "child_result", id });
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
declare function tell<S, M extends {
|
|
283
|
+
type: string;
|
|
284
|
+
}, C extends Cmd, U extends Sub, Ctx, E extends {
|
|
285
|
+
type: string;
|
|
286
|
+
} = never>(machine: Machine<S, M, C, U, Ctx>, runtime: Run<S, M, E>, msg: NoInfer<M>): TellAnswer;
|
|
287
|
+
|
|
288
|
+
export { type BootingRun, type DriveToDoneOptions, type PromiseRunOptions, type Run, TellAnswer, driveToDone, run, tell };
|
package/dist/promise/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
export { driveToDone, run } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
4
|
-
import '../chunk-
|
|
1
|
+
export { driveToDone, run, tell } from '../chunk-7K7SEYCH.js';
|
|
2
|
+
import '../chunk-GFGOQIEC.js';
|
|
3
|
+
import '../chunk-2A5XTKQQ.js';
|
|
4
|
+
import '../chunk-R2YNDRGL.js';
|
|
5
|
+
import '../chunk-DEAPNMKO.js';
|
|
6
|
+
import '../chunk-A3QPMX5M.js';
|
|
5
7
|
//# sourceMappingURL=index.js.map
|
|
6
8
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { E as EngineRun, S as
|
|
2
|
-
import { C as Cmd,
|
|
1
|
+
import { E as EngineRun, S as StoreAndOnError, a as BootedRunHandle } from '../runtime-types-CP-ykwgf.js';
|
|
2
|
+
import { C as Cmd, c as Sub, R as RunHandlers, M as Machine } from '../core-DNDAEhO2.js';
|
|
3
3
|
import '@standard-schema/spec';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -10,12 +10,11 @@ import '@standard-schema/spec';
|
|
|
10
10
|
* React component. Three rules (PRD §"React adapter"):
|
|
11
11
|
*
|
|
12
12
|
* 1. The runtime is the source of truth, NOT React state. We subscribe via
|
|
13
|
-
* `useSyncExternalStore
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* `README.md` for the ctx-identity footgun + fix.
|
|
13
|
+
* `useSyncExternalStore`, reading a snapshot of the run's state. React
|
|
14
|
+
* renders are driven by runtime state changes; React never owns the state.
|
|
15
|
+
* 2. The runtime is kept per component-mount, not per render. It is keyed on
|
|
16
|
+
* `[machine, opts.run, opts.ctx, opts.store]` — IDENTITY, not value. See
|
|
17
|
+
* `docs/how-to/drive-from-react.md` for the ctx-identity footgun + fix.
|
|
19
18
|
* 3. Concurrent React is the design target. `useSyncExternalStore` is the
|
|
20
19
|
* React-blessed primitive for tearing-free external state.
|
|
21
20
|
*
|
|
@@ -42,7 +41,7 @@ import '@standard-schema/spec';
|
|
|
42
41
|
* `subscribe` at the moment a Cmd runs or a Sub starts — a handler may close
|
|
43
42
|
* over props and state freely.
|
|
44
43
|
*
|
|
45
|
-
* **Identity matters.** The runtime is
|
|
44
|
+
* **Identity matters.** The runtime is keyed on `[machine, opts.run,
|
|
46
45
|
* opts.ctx, opts.store]`. A new `ctx` reference rebuilds the runtime, and so
|
|
47
46
|
* does a new `run` — pass the engine's own function, not an inline wrapper. Always `useMemo`
|
|
48
47
|
* (or otherwise stabilize) the ctx at the call site. See README.
|
|
@@ -56,22 +55,36 @@ type UseMachineOpts<S, M extends {
|
|
|
56
55
|
}, C extends Cmd, U extends Sub, Ctx> = {
|
|
57
56
|
run: EngineRun<S, M, C, U, Ctx>;
|
|
58
57
|
ctx: Ctx;
|
|
59
|
-
|
|
60
|
-
} & RunHandlers<M, C, U, Ctx>;
|
|
58
|
+
} & StoreAndOnError<S> & RunHandlers<M, C, U, Ctx>;
|
|
61
59
|
/**
|
|
62
60
|
* Build and own a run of `machine` for the lifetime of the component mount, on
|
|
63
61
|
* the engine whose `run` the caller hands in.
|
|
64
62
|
*
|
|
65
|
-
* -
|
|
66
|
-
* opts.ctx, opts.store]
|
|
67
|
-
* identities change.
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* `RuntimeDiscardedError` (`phase:
|
|
74
|
-
* silently.
|
|
63
|
+
* - Starts the run in an effect, never during render, and keys it on `[machine,
|
|
64
|
+
* opts.run, opts.ctx, opts.store]`: the run is replaced when ANY of those
|
|
65
|
+
* identities change. Until the run has its first State saved, `state` is
|
|
66
|
+
* `init(null, ctx)`'s.
|
|
67
|
+
* - Subscribes via `useSyncExternalStore` — tearing-free under React 18
|
|
68
|
+
* concurrent rendering.
|
|
69
|
+
* - On unmount (or any dep change), calls `stop()` — drains the queue, up to
|
|
70
|
+
* `disposeTimeoutMs`, runs every active sub cleanup, flushes state, and — if
|
|
71
|
+
* Cmds were still in flight — reports `RuntimeDiscardedError` (`phase:
|
|
72
|
+
* "discard"`) rather than dropping them silently. A handler that has not
|
|
73
|
+
* returned by the limit is given up on and reported as a `DrainTimeoutNotice`.
|
|
74
|
+
*
|
|
75
|
+
* **Strict Mode:** its extra effect run on mount does nothing. One run starts,
|
|
76
|
+
* `run` is called once, and each Sub starts once.
|
|
77
|
+
*
|
|
78
|
+
* **`<Activity>`** (React 19.2 and later): a hide stops the machine's Subs, and nothing else: their
|
|
79
|
+
* cleanups run, and they start again on show. It is the same run across the hide,
|
|
80
|
+
* so `state` keeps its value, `init` is not called again, and a Cmd in flight
|
|
81
|
+
* lands and its answer folds, whether that comes during the hide or after the
|
|
82
|
+
* show. A Msg dispatched while hidden folds at once, and any Cmd it emits runs.
|
|
83
|
+
*
|
|
84
|
+
* `dispatch` stays the same function across a hide and show. A Msg dispatched
|
|
85
|
+
* before the run has started, which on a tree first mounted hidden means before
|
|
86
|
+
* its first show, waits for the run and its promise settles once it is folded.
|
|
87
|
+
* After an unmount, `dispatch` rejects with `@demlik/tea: runtime stopped`.
|
|
75
88
|
*
|
|
76
89
|
* Returns `[state, dispatch]` shaped like `useReducer`.
|
|
77
90
|
*/
|