aifsmjs 0.5.8 → 0.6.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.
Files changed (70) hide show
  1. package/README.md +14 -7
  2. package/README_ZHTW.md +14 -7
  3. package/dist/{chunk-ZLQ7HZCE.js → chunk-D6H64FSI.js} +66 -46
  4. package/dist/chunk-D6H64FSI.js.map +1 -0
  5. package/dist/{chunk-NEJYZAKR.js → chunk-Q45LGXHO.js} +3 -3
  6. package/dist/{chunk-NEJYZAKR.js.map → chunk-Q45LGXHO.js.map} +1 -1
  7. package/dist/{chunk-CDK25FTD.cjs → chunk-QCTA2X4J.cjs} +7 -3
  8. package/dist/chunk-QCTA2X4J.cjs.map +1 -0
  9. package/dist/{chunk-A7U7QQL5.js → chunk-SSNKGEVB.js} +7 -4
  10. package/dist/chunk-SSNKGEVB.js.map +1 -0
  11. package/dist/{chunk-I354FONA.cjs → chunk-VGLF5NQH.cjs} +69 -46
  12. package/dist/chunk-VGLF5NQH.cjs.map +1 -0
  13. package/dist/{chunk-FHTQ7LSQ.cjs → chunk-VV5TKFQO.cjs} +4 -4
  14. package/dist/{chunk-FHTQ7LSQ.cjs.map → chunk-VV5TKFQO.cjs.map} +1 -1
  15. package/dist/{chunk-VZCSHTOI.js → chunk-XA24A7VP.js} +156 -157
  16. package/dist/chunk-XA24A7VP.js.map +1 -0
  17. package/dist/{chunk-2MME5V4F.cjs → chunk-YK25NVFC.cjs} +160 -161
  18. package/dist/chunk-YK25NVFC.cjs.map +1 -0
  19. package/dist/effects/index.cjs +3 -4
  20. package/dist/effects/index.cjs.map +1 -1
  21. package/dist/effects/index.d.cts +1 -1
  22. package/dist/effects/index.d.ts +1 -1
  23. package/dist/effects/index.js +2 -3
  24. package/dist/effects/index.js.map +1 -1
  25. package/dist/guards/index.cjs +5 -6
  26. package/dist/guards/index.cjs.map +1 -1
  27. package/dist/guards/index.d.cts +1 -1
  28. package/dist/guards/index.d.ts +1 -1
  29. package/dist/guards/index.js +2 -3
  30. package/dist/guards/index.js.map +1 -1
  31. package/dist/index.cjs +31 -28
  32. package/dist/index.d.cts +66 -18
  33. package/dist/index.d.ts +66 -18
  34. package/dist/index.js +3 -4
  35. package/dist/inspect/index.cjs +0 -2
  36. package/dist/inspect/index.cjs.map +1 -1
  37. package/dist/inspect/index.d.cts +1 -1
  38. package/dist/inspect/index.d.ts +1 -1
  39. package/dist/inspect/index.js +0 -2
  40. package/dist/inspect/index.js.map +1 -1
  41. package/dist/pbt/index.cjs +69 -48
  42. package/dist/pbt/index.cjs.map +1 -1
  43. package/dist/pbt/index.d.cts +29 -16
  44. package/dist/pbt/index.d.ts +29 -16
  45. package/dist/pbt/index.js +61 -40
  46. package/dist/pbt/index.js.map +1 -1
  47. package/dist/replay/index.cjs +4 -5
  48. package/dist/replay/index.d.cts +1 -1
  49. package/dist/replay/index.d.ts +1 -1
  50. package/dist/replay/index.js +3 -4
  51. package/dist/timer/index.cjs +18 -5
  52. package/dist/timer/index.cjs.map +1 -1
  53. package/dist/timer/index.d.cts +6 -0
  54. package/dist/timer/index.d.ts +6 -0
  55. package/dist/timer/index.js +18 -5
  56. package/dist/timer/index.js.map +1 -1
  57. package/dist/{types-DIM7QTtf.d.ts → types-CrDxFfBx.d.cts} +64 -16
  58. package/dist/{types-DIM7QTtf.d.cts → types-CrDxFfBx.d.ts} +64 -16
  59. package/llms-full.txt +89 -17
  60. package/package.json +57 -22
  61. package/dist/chunk-2MME5V4F.cjs.map +0 -1
  62. package/dist/chunk-A7U7QQL5.js.map +0 -1
  63. package/dist/chunk-CDK25FTD.cjs.map +0 -1
  64. package/dist/chunk-I354FONA.cjs.map +0 -1
  65. package/dist/chunk-PZ5AY32C.js +0 -9
  66. package/dist/chunk-PZ5AY32C.js.map +0 -1
  67. package/dist/chunk-Q7SFCCGT.cjs +0 -11
  68. package/dist/chunk-Q7SFCCGT.cjs.map +0 -1
  69. package/dist/chunk-VZCSHTOI.js.map +0 -1
  70. package/dist/chunk-ZLQ7HZCE.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,6 +1,13 @@
1
- import { f as MachineDef, I as Implementations, m as RuntimeOptions, j as Runtime, M as MachineConfig, S as Snapshot, o as StateDef, p as StepResult, A as Action, e as GuardRef, G as Guard, T as TransitionConfig, r as TransitionDef } from './types-DIM7QTtf.js';
2
- export { a as ActionRef, E as Effect, b as EffectHandler, c as Enqueuer, d as GuardArgs, g as Middleware, h as MiddlewareContext, R as RESET_EVENT_TYPE, i as ResetEvent, k as RuntimeErrorEvent, l as RuntimeEventMap, n as RuntimeTransitionEvent, q as SubMachineDef } from './types-DIM7QTtf.js';
1
+ import { f as MachineDef, I as Implementations, m as RuntimeOptions, j as Runtime, M as MachineConfig, S as Snapshot, o as StateDef, p as StepResult, A as Action, e as GuardRef, G as Guard, T as TransitionConfig, r as TransitionDef } from './types-CrDxFfBx.js';
2
+ export { a as ActionRef, E as Effect, b as EffectHandler, c as Enqueuer, d as GuardArgs, g as Middleware, h as MiddlewareContext, R as RESET_EVENT_TYPE, i as ResetEvent, k as RuntimeErrorEvent, l as RuntimeEventMap, n as RuntimeTransitionEvent, q as SubMachineDef } from './types-CrDxFfBx.js';
3
3
 
4
+ /**
5
+ * Thrown for an invalid machine definition and, since 0.6.0, for argument
6
+ * misuse at the definition/runtime boundary (`defineMachine`,
7
+ * `setup().defineMachine`, `createMachine`, `createRuntime`, and the
8
+ * `send` / `reset` / `subscribe` / `on` / `onTransition` runtime methods).
9
+ * Messages read `aifsmjs: <subject> must be <constraint>`.
10
+ */
4
11
  declare class InvalidDefinitionError extends Error {
5
12
  constructor(message: string);
6
13
  }
@@ -26,8 +33,9 @@ declare function defineMachine<Ctx = Record<string, never>, Evt extends {
26
33
  }, States extends string = string>(def: MachineConfig<Ctx, Evt, States>): MachineDef<Ctx, Evt, States>;
27
34
  /**
28
35
  * Curried builder so `States` can be inferred from `keyof states` without
29
- * `initial` collapsing it to a single literal. Pass `Ctx` and `Evt` as the
30
- * type arguments; pass the def to the returned `defineMachine`.
36
+ * `initial` or a transition `target` collapsing it to a single literal. Pass
37
+ * `Ctx` and `Evt` as the type arguments; pass the def to the returned
38
+ * `defineMachine`.
31
39
  *
32
40
  * const machine = setup<MyCtx, MyEvt>().defineMachine({
33
41
  * id: "m",
@@ -44,7 +52,9 @@ declare function setup<Ctx = Record<string, never>, Evt extends {
44
52
  defineMachine: <const States extends string>(def: Readonly<{
45
53
  id: string;
46
54
  initial: NoInfer<States>;
47
- states: Readonly<Record<States, StateDef<Ctx, Evt, States>>>;
55
+ states: Readonly<{
56
+ [K in States]: StateDef<Ctx, Evt, NoInfer<States>>;
57
+ }>;
48
58
  }> & (Record<string, never> extends Ctx ? {
49
59
  readonly context?: Ctx;
50
60
  } : {
@@ -75,6 +85,21 @@ declare class UnknownActionError extends Error {
75
85
  readonly actionName: string;
76
86
  constructor(actionName: string);
77
87
  }
88
+ /**
89
+ * Thrown (from `step()`, and so from `send()` before anything is committed)
90
+ * when an action returns a non-nullish primitive (`false`, `0`, `""`, ...) as
91
+ * the patch for an object context. An object context accepts only a
92
+ * plain-object patch or `undefined`/`null` (no change).
93
+ *
94
+ * `actionName` is the string ref, the inline function's name, or `"<inline>"`
95
+ * for an anonymous inline action (mirrors guard naming).
96
+ *
97
+ * @since 0.6.0
98
+ */
99
+ declare class InvalidActionResultError extends Error {
100
+ readonly actionName: string;
101
+ constructor(actionName: string, patch: unknown);
102
+ }
78
103
  /**
79
104
  * Compute the next snapshot and collected effects from a single event.
80
105
  *
@@ -87,6 +112,7 @@ declare class UnknownActionError extends Error {
87
112
  * 6. return { snapshot, effects, changed }
88
113
  *
89
114
  * The function is pure: it never dispatches effects and never mutates inputs.
115
+ * Each guard on the path is evaluated at most once.
90
116
  */
91
117
  declare function step<Ctx, Evt extends {
92
118
  type: string;
@@ -98,11 +124,15 @@ declare class RuntimeDisposedError extends Error {
98
124
  /**
99
125
  * Thrown by `send()` / `reset()` when a sub-machine init or dispose throws.
100
126
  *
101
- * Invariants:
102
- * - `phase: "init"` — child constructor threw. Parent snapshot was rolled
103
- * back to `prev`; no middleware ran; no `'transition'` emitted; no effects.
104
- * - `phase: "dispose"` — previous child's `dispose()` threw during transition.
105
- * Parent snapshot was rolled back to `prev`; child reference is cleared.
127
+ * Prepare-then-commit: the new child is constructed before the previous one
128
+ * is disposed. Invariants:
129
+ * - `phase: "init"` — the new child's constructor threw. The parent snapshot
130
+ * is not committed and the previous child (if any) is untouched: still live
131
+ * and still returned by `subRuntime()`. No middleware ran, no `'transition'`
132
+ * was emitted, no effects were dispatched.
133
+ * - `phase: "dispose"` — the previous child's `dispose()` threw. The new
134
+ * child (if any) was discarded, the parent snapshot is not committed, and
135
+ * `subRuntime()` returns `undefined` until the sub state is re-entered.
106
136
  * - Never thrown from `runtime.dispose()` cascade (never-throws contract).
107
137
  *
108
138
  * @since 0.3.0
@@ -115,9 +145,18 @@ declare class SubMachineError extends Error {
115
145
  }
116
146
  /**
117
147
  * Build a thin stateful runtime around a machine. `send()` calls `step()`,
118
- * runs the read-only middleware pipeline, dispatches effects, and notifies
119
- * subscribers. The runtime owns an `AbortController`; `dispose()` aborts it
120
- * and clears all state.
148
+ * commits, runs the read-only middleware pipeline, dispatches effects, and
149
+ * notifies subscribers then `'transition'` listeners. `send()`/`reset()` are
150
+ * run-to-completion: a call made while the runtime is already dispatching
151
+ * (from middleware, an effect handler, a listener, or a child runtime) is
152
+ * queued FIFO and processed after the current event's last notification.
153
+ * The runtime owns an `AbortController`; `dispose()` aborts it and clears all
154
+ * state.
155
+ *
156
+ * Arguments are validated before anything is created: a non-object `def`,
157
+ * `def.states`, `impl` or `opts`, or a `middleware` option that is not an
158
+ * array of functions, throws `InvalidDefinitionError`. The rest of `def` is
159
+ * trusted (build it with `defineMachine` / `setup().defineMachine`).
121
160
  */
122
161
  declare function createRuntime<Ctx, Evt extends {
123
162
  type: string;
@@ -132,12 +171,21 @@ declare function assign<Ctx, Evt>(updater: (args: {
132
171
  event: Evt;
133
172
  }) => Partial<Ctx>): Action<Ctx, Evt>;
134
173
  /**
135
- * Merge a partial context update into the current context. Plain-object
136
- * contexts get a shallow merge; non-object contexts get replaced wholesale.
174
+ * Merge an action's result into the current context. The function never
175
+ * mutates either argument.
137
176
  *
138
- * The function never mutates either argument.
177
+ * - `undefined` / `null` patch: `current` is returned unchanged.
178
+ * - Object context (not an array or `ArrayBuffer` view) + plain-object patch:
179
+ * shallow merge into a new object that keeps `current`'s prototype, so a
180
+ * class-instance context keeps its methods and untouched fields. Only own
181
+ * enumerable (string and symbol) properties are carried; `#private` and
182
+ * non-enumerable members are not, so prefer plain-object contexts.
183
+ * - Object context + non-nullish primitive patch (`false`, `0`, `""`, ...):
184
+ * throws {@link InvalidActionResultError} (`actionName` names the action).
185
+ * - Anything else (a primitive or array context, or a non-plain-object patch
186
+ * such as an array or class instance): the patch replaces `current`.
139
187
  */
140
- declare function mergeContext<Ctx>(current: Ctx, patch: Partial<Ctx> | void): Ctx;
188
+ declare function mergeContext<Ctx>(current: Ctx, patch: Partial<Ctx> | void, actionName?: string): Ctx;
141
189
 
142
190
  declare class UnknownGuardError extends Error {
143
191
  readonly guardName: string;
@@ -220,4 +268,4 @@ declare function createSnapshot<C, S extends string>(args: {
220
268
  status?: "active" | "final";
221
269
  }): Snapshot<C, S>;
222
270
 
223
- export { Action, AsyncGuardError, Guard, GuardRef, Implementations, InvalidDefinitionError, MachineDef, Runtime, RuntimeDisposedError, RuntimeOptions, Snapshot, StateDef, StepResult, SubMachineError, TransitionConfig, TransitionDef, UnknownActionError, UnknownGuardError, assign, createMachine, createRuntime, createSnapshot, deepFreeze, defineMachine, evalGuard, freezeSnapshot, initialSnapshot, isAsyncGuardFn, mergeContext, normalizeTransition, normalizeTransitions, resolveGuard, resolveTransitions, setup, step };
271
+ export { Action, AsyncGuardError, Guard, GuardRef, Implementations, InvalidActionResultError, InvalidDefinitionError, MachineConfig, MachineDef, Runtime, RuntimeDisposedError, RuntimeOptions, Snapshot, StateDef, StepResult, SubMachineError, TransitionConfig, TransitionDef, UnknownActionError, UnknownGuardError, assign, createMachine, createRuntime, createSnapshot, deepFreeze, defineMachine, evalGuard, freezeSnapshot, initialSnapshot, isAsyncGuardFn, mergeContext, normalizeTransition, normalizeTransitions, resolveGuard, resolveTransitions, setup, step };
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
- export { InvalidDefinitionError, RESET_EVENT_TYPE, RuntimeDisposedError, SubMachineError, createMachine, createRuntime, defineMachine, initialSnapshot, setup } from './chunk-VZCSHTOI.js';
2
- export { UnknownActionError, assign, createSnapshot, deepFreeze, freezeSnapshot, mergeContext, normalizeTransition, normalizeTransitions, resolveTransitions, step } from './chunk-ZLQ7HZCE.js';
1
+ export { InvalidDefinitionError, RESET_EVENT_TYPE, RuntimeDisposedError, SubMachineError, createMachine, createRuntime, defineMachine, initialSnapshot, setup } from './chunk-XA24A7VP.js';
2
+ export { InvalidActionResultError, UnknownActionError, assign, createSnapshot, deepFreeze, freezeSnapshot, mergeContext, normalizeTransition, normalizeTransitions, resolveTransitions, step } from './chunk-D6H64FSI.js';
3
3
  import './chunk-JKZAOPQC.js';
4
- export { AsyncGuardError, UnknownGuardError, evalGuard, isAsyncGuardFn, resolveGuard } from './chunk-A7U7QQL5.js';
5
- import './chunk-PZ5AY32C.js';
4
+ export { AsyncGuardError, UnknownGuardError, evalGuard, isAsyncGuardFn, resolveGuard } from './chunk-SSNKGEVB.js';
6
5
  //# sourceMappingURL=index.js.map
7
6
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- require('../chunk-Q7SFCCGT.cjs');
4
-
5
3
  // src/inspect/index.ts
6
4
  function logger(out = (l) => console.log(l)) {
7
5
  return (mw, next) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/inspect/index.ts"],"names":[],"mappings":";;;;;AAUO,SAAS,OACd,GAAA,GAAwE,CAAC,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,EAC9D;AAC9B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAI,GAAG,OAAA,EAAS;AACd,MAAA,GAAA,CAAI,CAAA,CAAA,EAAI,EAAA,CAAG,KAAA,CAAM,IAAI,CAAA,EAAA,EAAK,EAAA,CAAG,IAAA,CAAK,KAAK,CAAA,QAAA,EAAM,EAAA,CAAG,IAAA,CAAK,KAAK,IAAI,EAAE,CAAA;AAAA,IAClE;AAAA,EACF,CAAA;AACF;AAaO,SAAS,QAAkE,IAAA,EAGjD;AAC/B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAI,GAAG,OAAA,EAAS;AACd,MAAA,IAAA,CAAK,OAAA,CAAQ,QAAQ,IAAA,CAAK,GAAA,EAAK,KAAK,SAAA,CAAU,EAAA,CAAG,IAAI,CAAC,CAAA;AAAA,IACxD;AAAA,EACF,CAAA;AACF;AAcO,SAAS,SACd,IAAA,EAC8B;AAC9B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAA,CAAK,IAAA,CAAK;AAAA,MACR,OAAO,EAAA,CAAG,KAAA;AAAA,MACV,MAAM,EAAA,CAAG,IAAA;AAAA,MACT,MAAM,EAAA,CAAG,IAAA;AAAA,MACT,SAAS,EAAA,CAAG;AAAA,KACb,CAAA;AAAA,EACH,CAAA;AACF","file":"index.cjs","sourcesContent":["import type { Middleware, MiddlewareContext } from \"../fsm/types.js\";\n\nexport type { Middleware, MiddlewareContext } from \"../fsm/types.js\";\n\n/**\n * Log every transition that changed the snapshot. Default formatter:\n * \"[EVENT_TYPE] oldState → newState\"\n *\n * Pass a custom `out` callback to integrate with structured logging.\n */\nexport function logger<Ctx, Evt extends { type: string }, States extends string>(\n out: (line: string, ctx: MiddlewareContext<Ctx, Evt, States>) => void = (l) => console.log(l),\n): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n if (mw.changed) {\n out(`[${mw.event.type}] ${mw.prev.value} → ${mw.next.value}`, mw);\n }\n };\n}\n\nexport type StorageLike = {\n setItem(key: string, value: string): void;\n};\n\n/**\n * Persist the latest snapshot to a storage-like sink on every change. The\n * snapshot is JSON-serialised; non-serializable context fields will throw.\n *\n * For replay, pair this with `aifsmjs/replay` and an event log of your own\n * choosing — this middleware only persists the latest snapshot.\n */\nexport function persist<Ctx, Evt extends { type: string }, States extends string>(opts: {\n key: string;\n storage: StorageLike;\n}): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n if (mw.changed) {\n opts.storage.setItem(opts.key, JSON.stringify(mw.next));\n }\n };\n}\n\n/**\n * Collect every event-snapshot pair into the supplied array. Useful for\n * test assertions, time-travel debugging, or building event logs to feed\n * back into `replay()`.\n */\nexport type RecordedEntry<Ctx, Evt, States extends string> = Readonly<{\n event: MiddlewareContext<Ctx, Evt, States>[\"event\"];\n prev: MiddlewareContext<Ctx, Evt, States>[\"prev\"];\n next: MiddlewareContext<Ctx, Evt, States>[\"next\"];\n changed: boolean;\n}>;\n\nexport function recorder<Ctx, Evt extends { type: string }, States extends string>(\n sink: RecordedEntry<Ctx, Evt, States>[],\n): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n sink.push({\n event: mw.event,\n prev: mw.prev,\n next: mw.next,\n changed: mw.changed,\n });\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/inspect/index.ts"],"names":[],"mappings":";;;AAUO,SAAS,OACd,GAAA,GAAwE,CAAC,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,EAC9D;AAC9B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAI,GAAG,OAAA,EAAS;AACd,MAAA,GAAA,CAAI,CAAA,CAAA,EAAI,EAAA,CAAG,KAAA,CAAM,IAAI,CAAA,EAAA,EAAK,EAAA,CAAG,IAAA,CAAK,KAAK,CAAA,QAAA,EAAM,EAAA,CAAG,IAAA,CAAK,KAAK,IAAI,EAAE,CAAA;AAAA,IAClE;AAAA,EACF,CAAA;AACF;AAaO,SAAS,QAAkE,IAAA,EAGjD;AAC/B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAI,GAAG,OAAA,EAAS;AACd,MAAA,IAAA,CAAK,OAAA,CAAQ,QAAQ,IAAA,CAAK,GAAA,EAAK,KAAK,SAAA,CAAU,EAAA,CAAG,IAAI,CAAC,CAAA;AAAA,IACxD;AAAA,EACF,CAAA;AACF;AAcO,SAAS,SACd,IAAA,EAC8B;AAC9B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAA,CAAK,IAAA,CAAK;AAAA,MACR,OAAO,EAAA,CAAG,KAAA;AAAA,MACV,MAAM,EAAA,CAAG,IAAA;AAAA,MACT,MAAM,EAAA,CAAG,IAAA;AAAA,MACT,SAAS,EAAA,CAAG;AAAA,KACb,CAAA;AAAA,EACH,CAAA;AACF","file":"index.cjs","sourcesContent":["import type { Middleware, MiddlewareContext } from \"../fsm/types.js\";\n\nexport type { Middleware, MiddlewareContext } from \"../fsm/types.js\";\n\n/**\n * Log every transition that changed the snapshot. Default formatter:\n * \"[EVENT_TYPE] oldState → newState\"\n *\n * Pass a custom `out` callback to integrate with structured logging.\n */\nexport function logger<Ctx, Evt extends { type: string }, States extends string>(\n out: (line: string, ctx: MiddlewareContext<Ctx, Evt, States>) => void = (l) => console.log(l),\n): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n if (mw.changed) {\n out(`[${mw.event.type}] ${mw.prev.value} → ${mw.next.value}`, mw);\n }\n };\n}\n\nexport type StorageLike = {\n setItem(key: string, value: string): void;\n};\n\n/**\n * Persist the latest snapshot to a storage-like sink on every change. The\n * snapshot is JSON-serialised; non-serializable context fields will throw.\n *\n * For replay, pair this with `aifsmjs/replay` and an event log of your own\n * choosing — this middleware only persists the latest snapshot.\n */\nexport function persist<Ctx, Evt extends { type: string }, States extends string>(opts: {\n key: string;\n storage: StorageLike;\n}): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n if (mw.changed) {\n opts.storage.setItem(opts.key, JSON.stringify(mw.next));\n }\n };\n}\n\n/**\n * Collect every event-snapshot pair into the supplied array. Useful for\n * test assertions, time-travel debugging, or building event logs to feed\n * back into `replay()`.\n */\nexport type RecordedEntry<Ctx, Evt, States extends string> = Readonly<{\n event: MiddlewareContext<Ctx, Evt, States>[\"event\"];\n prev: MiddlewareContext<Ctx, Evt, States>[\"prev\"];\n next: MiddlewareContext<Ctx, Evt, States>[\"next\"];\n changed: boolean;\n}>;\n\nexport function recorder<Ctx, Evt extends { type: string }, States extends string>(\n sink: RecordedEntry<Ctx, Evt, States>[],\n): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n sink.push({\n event: mw.event,\n prev: mw.prev,\n next: mw.next,\n changed: mw.changed,\n });\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { h as MiddlewareContext, g as Middleware } from '../types-DIM7QTtf.cjs';
1
+ import { h as MiddlewareContext, g as Middleware } from '../types-CrDxFfBx.cjs';
2
2
 
3
3
  /**
4
4
  * Log every transition that changed the snapshot. Default formatter:
@@ -1,4 +1,4 @@
1
- import { h as MiddlewareContext, g as Middleware } from '../types-DIM7QTtf.js';
1
+ import { h as MiddlewareContext, g as Middleware } from '../types-CrDxFfBx.js';
2
2
 
3
3
  /**
4
4
  * Log every transition that changed the snapshot. Default formatter:
@@ -1,5 +1,3 @@
1
- import '../chunk-PZ5AY32C.js';
2
-
3
1
  // src/inspect/index.ts
4
2
  function logger(out = (l) => console.log(l)) {
5
3
  return (mw, next) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/inspect/index.ts"],"names":[],"mappings":";;;AAUO,SAAS,OACd,GAAA,GAAwE,CAAC,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,EAC9D;AAC9B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAI,GAAG,OAAA,EAAS;AACd,MAAA,GAAA,CAAI,CAAA,CAAA,EAAI,EAAA,CAAG,KAAA,CAAM,IAAI,CAAA,EAAA,EAAK,EAAA,CAAG,IAAA,CAAK,KAAK,CAAA,QAAA,EAAM,EAAA,CAAG,IAAA,CAAK,KAAK,IAAI,EAAE,CAAA;AAAA,IAClE;AAAA,EACF,CAAA;AACF;AAaO,SAAS,QAAkE,IAAA,EAGjD;AAC/B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAI,GAAG,OAAA,EAAS;AACd,MAAA,IAAA,CAAK,OAAA,CAAQ,QAAQ,IAAA,CAAK,GAAA,EAAK,KAAK,SAAA,CAAU,EAAA,CAAG,IAAI,CAAC,CAAA;AAAA,IACxD;AAAA,EACF,CAAA;AACF;AAcO,SAAS,SACd,IAAA,EAC8B;AAC9B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAA,CAAK,IAAA,CAAK;AAAA,MACR,OAAO,EAAA,CAAG,KAAA;AAAA,MACV,MAAM,EAAA,CAAG,IAAA;AAAA,MACT,MAAM,EAAA,CAAG,IAAA;AAAA,MACT,SAAS,EAAA,CAAG;AAAA,KACb,CAAA;AAAA,EACH,CAAA;AACF","file":"index.js","sourcesContent":["import type { Middleware, MiddlewareContext } from \"../fsm/types.js\";\n\nexport type { Middleware, MiddlewareContext } from \"../fsm/types.js\";\n\n/**\n * Log every transition that changed the snapshot. Default formatter:\n * \"[EVENT_TYPE] oldState → newState\"\n *\n * Pass a custom `out` callback to integrate with structured logging.\n */\nexport function logger<Ctx, Evt extends { type: string }, States extends string>(\n out: (line: string, ctx: MiddlewareContext<Ctx, Evt, States>) => void = (l) => console.log(l),\n): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n if (mw.changed) {\n out(`[${mw.event.type}] ${mw.prev.value} → ${mw.next.value}`, mw);\n }\n };\n}\n\nexport type StorageLike = {\n setItem(key: string, value: string): void;\n};\n\n/**\n * Persist the latest snapshot to a storage-like sink on every change. The\n * snapshot is JSON-serialised; non-serializable context fields will throw.\n *\n * For replay, pair this with `aifsmjs/replay` and an event log of your own\n * choosing — this middleware only persists the latest snapshot.\n */\nexport function persist<Ctx, Evt extends { type: string }, States extends string>(opts: {\n key: string;\n storage: StorageLike;\n}): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n if (mw.changed) {\n opts.storage.setItem(opts.key, JSON.stringify(mw.next));\n }\n };\n}\n\n/**\n * Collect every event-snapshot pair into the supplied array. Useful for\n * test assertions, time-travel debugging, or building event logs to feed\n * back into `replay()`.\n */\nexport type RecordedEntry<Ctx, Evt, States extends string> = Readonly<{\n event: MiddlewareContext<Ctx, Evt, States>[\"event\"];\n prev: MiddlewareContext<Ctx, Evt, States>[\"prev\"];\n next: MiddlewareContext<Ctx, Evt, States>[\"next\"];\n changed: boolean;\n}>;\n\nexport function recorder<Ctx, Evt extends { type: string }, States extends string>(\n sink: RecordedEntry<Ctx, Evt, States>[],\n): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n sink.push({\n event: mw.event,\n prev: mw.prev,\n next: mw.next,\n changed: mw.changed,\n });\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/inspect/index.ts"],"names":[],"mappings":";AAUO,SAAS,OACd,GAAA,GAAwE,CAAC,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,EAC9D;AAC9B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAI,GAAG,OAAA,EAAS;AACd,MAAA,GAAA,CAAI,CAAA,CAAA,EAAI,EAAA,CAAG,KAAA,CAAM,IAAI,CAAA,EAAA,EAAK,EAAA,CAAG,IAAA,CAAK,KAAK,CAAA,QAAA,EAAM,EAAA,CAAG,IAAA,CAAK,KAAK,IAAI,EAAE,CAAA;AAAA,IAClE;AAAA,EACF,CAAA;AACF;AAaO,SAAS,QAAkE,IAAA,EAGjD;AAC/B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAI,GAAG,OAAA,EAAS;AACd,MAAA,IAAA,CAAK,OAAA,CAAQ,QAAQ,IAAA,CAAK,GAAA,EAAK,KAAK,SAAA,CAAU,EAAA,CAAG,IAAI,CAAC,CAAA;AAAA,IACxD;AAAA,EACF,CAAA;AACF;AAcO,SAAS,SACd,IAAA,EAC8B;AAC9B,EAAA,OAAO,CAAC,IAAI,IAAA,KAAS;AACnB,IAAA,IAAA,EAAK;AACL,IAAA,IAAA,CAAK,IAAA,CAAK;AAAA,MACR,OAAO,EAAA,CAAG,KAAA;AAAA,MACV,MAAM,EAAA,CAAG,IAAA;AAAA,MACT,MAAM,EAAA,CAAG,IAAA;AAAA,MACT,SAAS,EAAA,CAAG;AAAA,KACb,CAAA;AAAA,EACH,CAAA;AACF","file":"index.js","sourcesContent":["import type { Middleware, MiddlewareContext } from \"../fsm/types.js\";\n\nexport type { Middleware, MiddlewareContext } from \"../fsm/types.js\";\n\n/**\n * Log every transition that changed the snapshot. Default formatter:\n * \"[EVENT_TYPE] oldState → newState\"\n *\n * Pass a custom `out` callback to integrate with structured logging.\n */\nexport function logger<Ctx, Evt extends { type: string }, States extends string>(\n out: (line: string, ctx: MiddlewareContext<Ctx, Evt, States>) => void = (l) => console.log(l),\n): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n if (mw.changed) {\n out(`[${mw.event.type}] ${mw.prev.value} → ${mw.next.value}`, mw);\n }\n };\n}\n\nexport type StorageLike = {\n setItem(key: string, value: string): void;\n};\n\n/**\n * Persist the latest snapshot to a storage-like sink on every change. The\n * snapshot is JSON-serialised; non-serializable context fields will throw.\n *\n * For replay, pair this with `aifsmjs/replay` and an event log of your own\n * choosing — this middleware only persists the latest snapshot.\n */\nexport function persist<Ctx, Evt extends { type: string }, States extends string>(opts: {\n key: string;\n storage: StorageLike;\n}): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n if (mw.changed) {\n opts.storage.setItem(opts.key, JSON.stringify(mw.next));\n }\n };\n}\n\n/**\n * Collect every event-snapshot pair into the supplied array. Useful for\n * test assertions, time-travel debugging, or building event logs to feed\n * back into `replay()`.\n */\nexport type RecordedEntry<Ctx, Evt, States extends string> = Readonly<{\n event: MiddlewareContext<Ctx, Evt, States>[\"event\"];\n prev: MiddlewareContext<Ctx, Evt, States>[\"prev\"];\n next: MiddlewareContext<Ctx, Evt, States>[\"next\"];\n changed: boolean;\n}>;\n\nexport function recorder<Ctx, Evt extends { type: string }, States extends string>(\n sink: RecordedEntry<Ctx, Evt, States>[],\n): Middleware<Ctx, Evt, States> {\n return (mw, next) => {\n next();\n sink.push({\n event: mw.event,\n prev: mw.prev,\n next: mw.next,\n changed: mw.changed,\n });\n };\n}\n"]}
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var chunk2MME5V4F_cjs = require('../chunk-2MME5V4F.cjs');
4
- var chunkFHTQ7LSQ_cjs = require('../chunk-FHTQ7LSQ.cjs');
5
- var chunkI354FONA_cjs = require('../chunk-I354FONA.cjs');
3
+ var chunkYK25NVFC_cjs = require('../chunk-YK25NVFC.cjs');
4
+ var chunkVV5TKFQO_cjs = require('../chunk-VV5TKFQO.cjs');
5
+ var chunkVGLF5NQH_cjs = require('../chunk-VGLF5NQH.cjs');
6
6
  require('../chunk-3B2USJ3H.cjs');
7
- require('../chunk-CDK25FTD.cjs');
8
- var chunkQ7SFCCGT_cjs = require('../chunk-Q7SFCCGT.cjs');
7
+ var chunkQCTA2X4J_cjs = require('../chunk-QCTA2X4J.cjs');
9
8
  var fc2 = require('fast-check');
9
+ var util = require('util');
10
10
 
11
11
  function _interopNamespace(e) {
12
12
  if (e && e.__esModule) return e;
@@ -52,7 +52,7 @@ var SendCommand = class {
52
52
  const before = r.getSnapshot();
53
53
  r.send(this.event);
54
54
  const after = r.getSnapshot();
55
- const predicted = chunkI354FONA_cjs.step(this.def, before, this.event, this.impl);
55
+ const predicted = chunkVGLF5NQH_cjs.step(this.def, before, this.event, this.impl);
56
56
  if (predicted.snapshot.value !== after.value) {
57
57
  throw new Error(
58
58
  `aifsmjs/pbt: determinism violation \u2014 predicted "${String(predicted.snapshot.value)}" but runtime returned "${String(after.value)}" after ${this.toString()}`
@@ -74,18 +74,6 @@ function commandsFromMachine(def, impl, eventArbitraries) {
74
74
  }
75
75
  return fc2__namespace.commands(arbs, { size: "+1" });
76
76
  }
77
-
78
- // src/pbt/properties.ts
79
- var properties_exports = {};
80
- chunkQ7SFCCGT_cjs.__export(properties_exports, {
81
- assertAll: () => assertAll,
82
- assignDoesNotMutate: () => assignDoesNotMutate,
83
- guardsFalseNoTransition: () => guardsFalseNoTransition,
84
- reachableStatesSubsetDeclared: () => reachableStatesSubsetDeclared,
85
- replayEqualsFold: () => replayEqualsFold,
86
- snapshotAlwaysFrozen: () => snapshotAlwaysFrozen,
87
- unknownEventNoOp: () => unknownEventNoOp
88
- });
89
77
  function buildAssertOpts(opts) {
90
78
  const out = {};
91
79
  if (opts?.numRuns !== void 0) out.numRuns = opts.numRuns;
@@ -93,13 +81,20 @@ function buildAssertOpts(opts) {
93
81
  if (opts?.verbose) out.verbose = true;
94
82
  return out;
95
83
  }
84
+ function contextEquals(a, b) {
85
+ return util.isDeepStrictEqual(a, b);
86
+ }
96
87
  function snapshotAlwaysFrozen(def, impl, eventArbitraries, opts) {
97
88
  fc2__namespace.assert(
98
89
  fc2__namespace.property(commandsFromMachine(def, impl, eventArbitraries), (cmds) => {
99
- const real = chunk2MME5V4F_cjs.createRuntime(def, impl);
100
- const model = initialModel(def);
101
- fc2__namespace.modelRun(() => ({ model, real }), cmds);
102
- return Object.isFrozen(real.getSnapshot());
90
+ const real = chunkYK25NVFC_cjs.createRuntime(def, impl, { dispatchEffects: false });
91
+ try {
92
+ const model = initialModel(def);
93
+ fc2__namespace.modelRun(() => ({ model, real }), cmds);
94
+ return Object.isFrozen(real.getSnapshot());
95
+ } finally {
96
+ real.dispose();
97
+ }
103
98
  }),
104
99
  buildAssertOpts(opts)
105
100
  );
@@ -107,8 +102,8 @@ function snapshotAlwaysFrozen(def, impl, eventArbitraries, opts) {
107
102
  function unknownEventNoOp(def, impl, unknownType, opts) {
108
103
  fc2__namespace.assert(
109
104
  fc2__namespace.property(fc2__namespace.constant(unknownType), (t) => {
110
- const initial = chunk2MME5V4F_cjs.initialSnapshot(def);
111
- const result = chunkI354FONA_cjs.step(def, initial, { type: t }, impl);
105
+ const initial = chunkYK25NVFC_cjs.initialSnapshot(def);
106
+ const result = chunkVGLF5NQH_cjs.step(def, initial, { type: t }, impl);
112
107
  return result.changed === false && result.snapshot === initial && result.effects.length === 0;
113
108
  }),
114
109
  buildAssertOpts(opts)
@@ -118,13 +113,17 @@ function reachableStatesSubsetDeclared(def, impl, eventArbitraries, opts) {
118
113
  const declared = new Set(Object.keys(def.states));
119
114
  fc2__namespace.assert(
120
115
  fc2__namespace.property(commandsFromMachine(def, impl, eventArbitraries), (cmds) => {
121
- const real = chunk2MME5V4F_cjs.createRuntime(def, impl);
122
- const model = initialModel(def);
123
- fc2__namespace.modelRun(() => ({ model, real }), cmds);
124
- for (const s of model.reached) {
125
- if (!declared.has(s)) return false;
116
+ const real = chunkYK25NVFC_cjs.createRuntime(def, impl, { dispatchEffects: false });
117
+ try {
118
+ const model = initialModel(def);
119
+ fc2__namespace.modelRun(() => ({ model, real }), cmds);
120
+ for (const s of model.reached) {
121
+ if (!declared.has(s)) return false;
122
+ }
123
+ return declared.has(real.getSnapshot().value);
124
+ } finally {
125
+ real.dispose();
126
126
  }
127
- return declared.has(real.getSnapshot().value);
128
127
  }),
129
128
  buildAssertOpts(opts)
130
129
  );
@@ -133,11 +132,15 @@ function replayEqualsFold(def, impl, eventArbitraries, opts) {
133
132
  const eventArb = fc2__namespace.oneof(...Object.values(eventArbitraries));
134
133
  fc2__namespace.assert(
135
134
  fc2__namespace.property(fc2__namespace.array(eventArb, { maxLength: 32 }), (events) => {
136
- const real = chunk2MME5V4F_cjs.createRuntime(def, impl, { dispatchEffects: false });
137
- for (const e of events) real.send(e);
138
- const live = real.getSnapshot();
139
- const replayed = chunkFHTQ7LSQ_cjs.replay(chunk2MME5V4F_cjs.initialSnapshot(def), events, def, impl).snapshot;
140
- return live.value === replayed.value && JSON.stringify(live.context) === JSON.stringify(replayed.context);
135
+ const real = chunkYK25NVFC_cjs.createRuntime(def, impl, { dispatchEffects: false });
136
+ try {
137
+ for (const e of events) real.send(e);
138
+ const live = real.getSnapshot();
139
+ const replayed = chunkVV5TKFQO_cjs.replay(chunkYK25NVFC_cjs.initialSnapshot(def), events, def, impl).snapshot;
140
+ return live.value === replayed.value && contextEquals(live.context, replayed.context);
141
+ } finally {
142
+ real.dispose();
143
+ }
141
144
  }),
142
145
  buildAssertOpts(opts)
143
146
  );
@@ -146,7 +149,8 @@ function guardsFalseNoTransition(def, impl, eventArbitraries, opts) {
146
149
  const blockedGuards = new Proxy(
147
150
  {},
148
151
  {
149
- get: () => () => false
152
+ get: () => () => false,
153
+ getOwnPropertyDescriptor: () => ({ configurable: true })
150
154
  }
151
155
  );
152
156
  const blockedImpl = {
@@ -154,17 +158,17 @@ function guardsFalseNoTransition(def, impl, eventArbitraries, opts) {
154
158
  guards: blockedGuards
155
159
  };
156
160
  const isFullyGuarded = (value, eventType) => {
157
- const candidates = chunkI354FONA_cjs.normalizeTransitions(def.states[value]?.on?.[eventType]);
161
+ const candidates = chunkVGLF5NQH_cjs.normalizeTransitions(chunkQCTA2X4J_cjs.ownValue(def.states[value]?.on, eventType));
158
162
  return candidates.length > 0 && candidates.every((t) => t.guard !== void 0);
159
163
  };
160
164
  fc2__namespace.assert(
161
165
  fc2__namespace.property(
162
166
  fc2__namespace.array(fc2__namespace.oneof(...Object.values(eventArbitraries)), { maxLength: 16 }),
163
167
  (events) => {
164
- let snap = chunk2MME5V4F_cjs.initialSnapshot(def);
168
+ let snap = chunkYK25NVFC_cjs.initialSnapshot(def);
165
169
  for (const e of events) {
166
170
  const fullyGuarded = isFullyGuarded(snap.value, e.type);
167
- const r = chunkI354FONA_cjs.step(def, snap, e, blockedImpl);
171
+ const r = chunkVGLF5NQH_cjs.step(def, snap, e, blockedImpl);
168
172
  if (fullyGuarded && r.changed !== false) return false;
169
173
  snap = r.snapshot;
170
174
  }
@@ -174,20 +178,28 @@ function guardsFalseNoTransition(def, impl, eventArbitraries, opts) {
174
178
  buildAssertOpts(opts)
175
179
  );
176
180
  }
181
+ var ownState = (v) => [
182
+ ...Reflect.ownKeys(v).flatMap((k) => [k, v[k]]),
183
+ ...v instanceof Map || v instanceof Set ? [...v.entries()].flat() : [],
184
+ v instanceof Date && v.getTime()
185
+ ];
177
186
  function assignDoesNotMutate(def, impl, eventArbitraries, opts) {
178
- const dummy = { a: 1, b: 2 };
179
- const merged = chunkI354FONA_cjs.mergeContext(dummy, { b: 3 });
180
- if (merged === dummy) throw new Error("aifsmjs/pbt: mergeContext returned the same reference");
181
187
  fc2__namespace.assert(
182
188
  fc2__namespace.property(
183
189
  fc2__namespace.array(fc2__namespace.oneof(...Object.values(eventArbitraries)), { maxLength: 16 }),
184
190
  (events) => {
185
- let snap = chunk2MME5V4F_cjs.initialSnapshot(def);
191
+ let snap = chunkYK25NVFC_cjs.initialSnapshot(def);
186
192
  for (const e of events) {
187
- const beforeCtxSerialised = JSON.stringify(snap.context);
188
- chunkI354FONA_cjs.step(def, snap, e, impl);
189
- if (JSON.stringify(snap.context) !== beforeCtxSerialised) return false;
190
- snap = chunkI354FONA_cjs.step(def, snap, e, impl).snapshot;
193
+ const before = /* @__PURE__ */ new Map();
194
+ const walk = (v) => {
195
+ if (!v || typeof v !== "object" || before.has(v)) return;
196
+ const own = ownState(v);
197
+ before.set(v, own);
198
+ own.forEach(walk);
199
+ };
200
+ walk(snap.context);
201
+ snap = chunkVGLF5NQH_cjs.step(def, snap, e, impl).snapshot;
202
+ for (const [obj, own] of before) if (!contextEquals(ownState(obj), own)) return false;
191
203
  }
192
204
  return true;
193
205
  }
@@ -205,7 +217,16 @@ function assertAll(def, impl, eventArbitraries, opts) {
205
217
  }
206
218
 
207
219
  // src/pbt/index.ts
208
- var properties = properties_exports;
220
+ var properties = Object.freeze({
221
+ assertAll,
222
+ assignDoesNotMutate,
223
+ contextEquals,
224
+ guardsFalseNoTransition,
225
+ reachableStatesSubsetDeclared,
226
+ replayEqualsFold,
227
+ snapshotAlwaysFrozen,
228
+ unknownEventNoOp
229
+ });
209
230
 
210
231
  exports.assertAll = assertAll;
211
232
  exports.assignDoesNotMutate = assignDoesNotMutate;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pbt/commands.ts","../../src/pbt/properties.ts","../../src/pbt/index.ts"],"names":["step","fc","__export","fc2","createRuntime","initialSnapshot","replay","normalizeTransitions","mergeContext"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBO,SAAS,aACd,GAAA,EACuB;AACvB,EAAA,OAAO;AAAA,IACL,OAAO,GAAA,CAAI,OAAA;AAAA,IACX,SAAS,GAAA,CAAI,OAAA;AAAA,IACb,QAAQ,GAAA,CAAI,MAAA,CAAO,IAAI,OAAO,CAAA,EAAG,QAAQ,OAAA,GAAU,QAAA;AAAA,IACnD,yBAAS,IAAI,GAAA,CAAI,CAAC,GAAA,CAAI,OAAO,CAAC;AAAA,GAChC;AACF;AAEA,IAAM,cAAN,MAEA;AAAA,EACE,WAAA,CACmB,KAAA,EACA,GAAA,EACA,IAAA,EACjB;AAHiB,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AACA,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAChB;AAAA,EAHgB,KAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EAGnB,MAAM,EAAA,EAA8C;AAElD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,GAAA,CAAI,GAA0B,CAAA,EAAoC;AAChE,IAAA,MAAM,MAAA,GAAgC,EAAE,WAAA,EAAY;AACpD,IAAA,CAAA,CAAE,IAAA,CAAK,KAAK,KAAK,CAAA;AACjB,IAAA,MAAM,KAAA,GAAQ,EAAE,WAAA,EAAY;AAE5B,IAAA,MAAM,SAAA,GAAYA,uBAAK,IAAA,CAAK,GAAA,EAAK,QAAQ,IAAA,CAAK,KAAA,EAAO,KAAK,IAAI,CAAA;AAI9D,IAAA,IAAI,SAAA,CAAU,QAAA,CAAS,KAAA,KAAU,KAAA,CAAM,KAAA,EAAO;AAC5C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,qDAAA,EAAmD,MAAA,CAAO,SAAA,CAAU,QAAA,CAAS,KAAK,CAAC,CAAA,wBAAA,EAA2B,MAAA,CAAO,KAAA,CAAM,KAAK,CAAC,CAAA,QAAA,EAAW,IAAA,CAAK,UAAU,CAAA;AAAA,OAC7J;AAAA,IACF;AAGA,IAAA,CAAA,CAAE,QAAQ,KAAA,CAAM,KAAA;AAChB,IAAA,CAAA,CAAE,UAAU,KAAA,CAAM,OAAA;AAClB,IAAA,CAAA,CAAE,SAAS,KAAA,CAAM,MAAA;AACjB,IAAA,CAAA,CAAE,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAK,CAAA;AAAA,EAC3B;AAAA,EAEA,QAAA,GAAmB;AACjB,IAAA,OAAO,CAAA,KAAA,EAAQ,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,KAAK,CAAC,CAAA,CAAA,CAAA;AAAA,EAC3C;AACF,CAAA;AAWO,SAAS,mBAAA,CACd,GAAA,EACA,IAAA,EACA,gBAAA,EACsD;AACtD,EAAA,MAAM,OAAqD,EAAC;AAC5D,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,MAAA,CAAO,gBAAgB,CAAA,EAAG;AACjD,IAAA,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,CAAC,KAAA,KAAU,IAAI,WAAA,CAAY,KAAA,EAAO,GAAA,EAAK,IAAI,CAAC,CAAC,CAAA;AAAA,EACjE;AACA,EAAA,OAAUC,cAAA,CAAA,QAAA,CAAS,IAAA,EAAM,EAAE,IAAA,EAAM,MAAM,CAAA;AACzC;;;AChGA,IAAA,kBAAA,GAAA,EAAA;AAAAC,0BAAA,CAAA,kBAAA,EAAA;AAAA,EAAA,SAAA,EAAA,MAAA,SAAA;AAAA,EAAA,mBAAA,EAAA,MAAA,mBAAA;AAAA,EAAA,uBAAA,EAAA,MAAA,uBAAA;AAAA,EAAA,6BAAA,EAAA,MAAA,6BAAA;AAAA,EAAA,gBAAA,EAAA,MAAA,gBAAA;AAAA,EAAA,oBAAA,EAAA,MAAA,oBAAA;AAAA,EAAA,gBAAA,EAAA,MAAA;AAAA,CAAA,CAAA;AAqBA,SAAS,gBAAgB,IAAA,EAAsD;AAC7E,EAAA,MAAM,MAA8B,EAAC;AACrC,EAAA,IAAI,IAAA,EAAM,OAAA,KAAY,MAAA,EAAW,GAAA,CAAI,UAAU,IAAA,CAAK,OAAA;AACpD,EAAA,IAAI,IAAA,EAAM,IAAA,KAAS,MAAA,EAAW,GAAA,CAAI,OAAO,IAAA,CAAK,IAAA;AAC9C,EAAA,IAAI,IAAA,EAAM,OAAA,EAAS,GAAA,CAAI,OAAA,GAAU,IAAA;AACjC,EAAA,OAAO,GAAA;AACT;AAMO,SAAS,oBAAA,CACd,GAAA,EACA,IAAA,EACA,gBAAA,EACA,IAAA,EACM;AACN,EAAGC,cAAA,CAAA,MAAA;AAAA,IACEA,wBAAS,mBAAA,CAAoB,GAAA,EAAK,MAAM,gBAAgB,CAAA,EAAG,CAAC,IAAA,KAAS;AACtE,MAAA,MAAM,IAAA,GAAOC,+BAAA,CAAc,GAAA,EAAK,IAAI,CAAA;AACpC,MAAA,MAAM,KAAA,GAA+B,aAAa,GAAG,CAAA;AACrD,MAAGD,wBAAS,OAAO,EAAE,KAAA,EAAO,IAAA,KAAS,IAAI,CAAA;AACzC,MAAA,OAAO,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,WAAA,EAAa,CAAA;AAAA,IAC3C,CAAC,CAAA;AAAA,IACD,gBAAgB,IAAI;AAAA,GACtB;AACF;AAMO,SAAS,gBAAA,CACd,GAAA,EACA,IAAA,EACA,WAAA,EACA,IAAA,EACM;AACN,EAAGA,cAAA,CAAA,MAAA;AAAA,IACEA,cAAA,CAAA,QAAA,CAAYA,cAAA,CAAA,QAAA,CAAS,WAAW,CAAA,EAAG,CAAC,CAAA,KAAM;AAC3C,MAAA,MAAM,OAAA,GAAUE,kCAAgB,GAAG,CAAA;AACnC,MAAA,MAAM,MAAA,GAASL,uBAAK,GAAA,EAAK,OAAA,EAAS,EAAE,IAAA,EAAM,CAAA,IAAuB,IAAI,CAAA;AACrE,MAAA,OAAO,MAAA,CAAO,YAAY,KAAA,IAAS,MAAA,CAAO,aAAa,OAAA,IAAW,MAAA,CAAO,QAAQ,MAAA,KAAW,CAAA;AAAA,IAC9F,CAAC,CAAA;AAAA,IACD,gBAAgB,IAAI;AAAA,GACtB;AACF;AAMO,SAAS,6BAAA,CAKd,GAAA,EACA,IAAA,EACA,gBAAA,EACA,IAAA,EACM;AACN,EAAA,MAAM,WAAW,IAAI,GAAA,CAAY,OAAO,IAAA,CAAK,GAAA,CAAI,MAAM,CAAC,CAAA;AACxD,EAAGG,cAAA,CAAA,MAAA;AAAA,IACEA,wBAAS,mBAAA,CAAoB,GAAA,EAAK,MAAM,gBAAgB,CAAA,EAAG,CAAC,IAAA,KAAS;AACtE,MAAA,MAAM,IAAA,GAAOC,+BAAA,CAAc,GAAA,EAAK,IAAI,CAAA;AACpC,MAAA,MAAM,KAAA,GAA+B,aAAa,GAAG,CAAA;AACrD,MAAGD,wBAAS,OAAO,EAAE,KAAA,EAAO,IAAA,KAAS,IAAI,CAAA;AACzC,MAAA,KAAA,MAAW,CAAA,IAAK,MAAM,OAAA,EAAwB;AAE5C,QAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,CAAC,GAAG,OAAO,KAAA;AAAA,MAC/B;AACA,MAAA,OAAO,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,WAAA,GAAc,KAAK,CAAA;AAAA,IAC9C,CAAC,CAAA;AAAA,IACD,gBAAgB,IAAI;AAAA,GACtB;AACF;AAOO,SAAS,gBAAA,CACd,GAAA,EACA,IAAA,EACA,gBAAA,EACA,IAAA,EACM;AACN,EAAA,MAAM,WAAcA,cAAA,CAAA,KAAA,CAAM,GAAG,MAAA,CAAO,MAAA,CAAO,gBAAgB,CAAC,CAAA;AAC5D,EAAGA,cAAA,CAAA,MAAA;AAAA,IACEA,cAAA,CAAA,QAAA,CAAYA,qBAAM,QAAA,EAAU,EAAE,WAAW,EAAA,EAAI,CAAA,EAAG,CAAC,MAAA,KAAW;AAC7D,MAAA,MAAM,OAAOC,+BAAA,CAAc,GAAA,EAAK,MAAM,EAAE,eAAA,EAAiB,OAAO,CAAA;AAChE,MAAA,KAAA,MAAW,CAAA,IAAK,MAAA,EAAQ,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA;AACnC,MAAA,MAAM,IAAA,GAAO,KAAK,WAAA,EAAY;AAC9B,MAAA,MAAM,QAAA,GAAWE,yBAAOD,iCAAA,CAAgB,GAAG,GAAG,MAAA,EAAQ,GAAA,EAAK,IAAI,CAAA,CAAE,QAAA;AACjE,MAAA,OACE,IAAA,CAAK,KAAA,KAAU,QAAA,CAAS,KAAA,IACxB,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,OAAO,CAAA,KAAM,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,OAAO,CAAA;AAAA,IAEpE,CAAC,CAAA;AAAA,IACD,gBAAgB,IAAI;AAAA,GACtB;AACF;AAaO,SAAS,uBAAA,CACd,GAAA,EACA,IAAA,EACA,gBAAA,EACA,IAAA,EACM;AACN,EAAA,MAAM,gBAAgB,IAAI,KAAA;AAAA,IACxB,EAAC;AAAA,IACD;AAAA,MACE,GAAA,EAAK,MAAM,MAAM;AAAA;AACnB,GACF;AACA,EAAA,MAAM,WAAA,GAAyC;AAAA,IAC7C,GAAG,IAAA;AAAA,IACH,MAAA,EAAQ;AAAA,GACV;AAIA,EAAA,MAAM,cAAA,GAAiB,CAAC,KAAA,EAAe,SAAA,KAA+B;AACpE,IAAA,MAAM,UAAA,GAAaE,uCAAqB,GAAA,CAAI,MAAA,CAAO,KAAK,CAAA,EAAG,EAAA,GAAK,SAAS,CAAC,CAAA;AAC1E,IAAA,OAAO,UAAA,CAAW,SAAS,CAAA,IAAK,UAAA,CAAW,MAAM,CAAC,CAAA,KAAM,CAAA,CAAE,KAAA,KAAU,MAAS,CAAA;AAAA,EAC/E,CAAA;AACA,EAAGJ,cAAA,CAAA,MAAA;AAAA,IACEA,cAAA,CAAA,QAAA;AAAA,MACEA,cAAA,CAAA,KAAA,CAASA,cAAA,CAAA,KAAA,CAAM,GAAG,MAAA,CAAO,MAAA,CAAO,gBAAgB,CAAC,CAAA,EAAG,EAAE,SAAA,EAAW,EAAA,EAAI,CAAA;AAAA,MACxE,CAAC,MAAA,KAAW;AACV,QAAA,IAAI,IAAA,GAAOE,kCAAgB,GAAG,CAAA;AAC9B,QAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,UAAA,MAAM,YAAA,GAAe,cAAA,CAAe,IAAA,CAAK,KAAA,EAAO,EAAE,IAAI,CAAA;AACtD,UAAA,MAAM,CAAA,GAAIL,sBAAA,CAAK,GAAA,EAAK,IAAA,EAAM,GAAG,WAAW,CAAA;AAIxC,UAAA,IAAI,YAAA,IAAgB,CAAA,CAAE,OAAA,KAAY,KAAA,EAAO,OAAO,KAAA;AAChD,UAAA,IAAA,GAAO,CAAA,CAAE,QAAA;AAAA,QACX;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,KACF;AAAA,IACA,gBAAgB,IAAI;AAAA,GACtB;AACF;AAOO,SAAS,mBAAA,CACd,GAAA,EACA,IAAA,EACA,gBAAA,EACA,IAAA,EACM;AAEN,EAAA,MAAM,KAAA,GAAQ,EAAE,CAAA,EAAG,CAAA,EAAG,GAAG,CAAA,EAAE;AAC3B,EAAA,MAAM,SAASQ,8BAAA,CAAa,KAAA,EAAO,EAAE,CAAA,EAAG,GAAG,CAAA;AAE3C,EAAA,IAAI,MAAA,KAAW,KAAA,EAAO,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAE7F,EAAGL,cAAA,CAAA,MAAA;AAAA,IACEA,cAAA,CAAA,QAAA;AAAA,MACEA,cAAA,CAAA,KAAA,CAASA,cAAA,CAAA,KAAA,CAAM,GAAG,MAAA,CAAO,MAAA,CAAO,gBAAgB,CAAC,CAAA,EAAG,EAAE,SAAA,EAAW,EAAA,EAAI,CAAA;AAAA,MACxE,CAAC,MAAA,KAAW;AACV,QAAA,IAAI,IAAA,GAAOE,kCAAgB,GAAG,CAAA;AAC9B,QAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,UAAA,MAAM,mBAAA,GAAsB,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,OAAO,CAAA;AACvD,UAAAL,sBAAA,CAAK,GAAA,EAAK,IAAA,EAAM,CAAA,EAAG,IAAI,CAAA;AAEvB,UAAA,IAAI,KAAK,SAAA,CAAU,IAAA,CAAK,OAAO,CAAA,KAAM,qBAAqB,OAAO,KAAA;AAEjE,UAAA,IAAA,GAAOA,sBAAA,CAAK,GAAA,EAAK,IAAA,EAAM,CAAA,EAAG,IAAI,CAAA,CAAE,QAAA;AAAA,QAClC;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,KACF;AAAA,IACA,gBAAgB,IAAI;AAAA,GACtB;AACF;AAMO,SAAS,SAAA,CACd,GAAA,EACA,IAAA,EACA,gBAAA,EACA,IAAA,EACM;AACN,EAAA,oBAAA,CAAqB,GAAA,EAAK,IAAA,EAAM,gBAAA,EAAkB,IAAI,CAAA;AACtD,EAAA,gBAAA,CAAiB,GAAA,EAAK,IAAA,EAAM,IAAA,EAAM,gBAAA,IAAoB,uBAAuB,IAAI,CAAA;AACjF,EAAA,6BAAA,CAA8B,GAAA,EAAK,IAAA,EAAM,gBAAA,EAAkB,IAAI,CAAA;AAC/D,EAAA,gBAAA,CAAiB,GAAA,EAAK,IAAA,EAAM,gBAAA,EAAkB,IAAI,CAAA;AAClD,EAAA,uBAAA,CAAwB,GAAA,EAAK,IAAA,EAAM,gBAAA,EAAkB,IAAI,CAAA;AACzD,EAAA,mBAAA,CAAoB,GAAA,EAAK,IAAA,EAAM,gBAAA,EAAkB,IAAI,CAAA;AACvD;;;ACpNO,IAAM,UAAA,GAAa","file":"index.cjs","sourcesContent":["import * as fc from \"fast-check\";\nimport { step } from \"../fsm/lifecycle.js\";\nimport type { Implementations, MachineDef, Runtime, Snapshot } from \"../fsm/types.js\";\n\n/**\n * Pure-model representation of an FSM run, used by `fc.commands`.\n * `reached` tracks every state visited so generic properties can check\n * containment without re-running.\n */\nexport type FsmModel<Ctx, States extends string> = {\n value: States;\n context: Ctx;\n status: \"active\" | \"final\";\n reached: Set<States>;\n};\n\nexport type EventArbitraries<Evt extends { type: string }> = Readonly<\n Record<string, fc.Arbitrary<Evt>>\n>;\n\nexport type FsmCommand<Ctx, Evt extends { type: string }, States extends string> = fc.Command<\n FsmModel<Ctx, States>,\n Runtime<Ctx, Evt, States>\n>;\n\nexport function initialModel<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n): FsmModel<Ctx, States> {\n return {\n value: def.initial,\n context: def.context,\n status: def.states[def.initial]?.final ? \"final\" : \"active\",\n reached: new Set([def.initial]),\n };\n}\n\nclass SendCommand<Ctx, Evt extends { type: string }, States extends string>\n implements FsmCommand<Ctx, Evt, States>\n{\n constructor(\n private readonly event: Evt,\n private readonly def: MachineDef<Ctx, Evt, States>,\n private readonly impl: Implementations<Ctx, Evt>,\n ) {}\n\n check(_m: Readonly<FsmModel<Ctx, States>>): boolean {\n // Every event is always applicable; invariants are asserted in run().\n return true;\n }\n\n run(m: FsmModel<Ctx, States>, r: Runtime<Ctx, Evt, States>): void {\n const before: Snapshot<Ctx, States> = r.getSnapshot();\n r.send(this.event);\n const after = r.getSnapshot();\n\n const predicted = step(this.def, before, this.event, this.impl);\n\n /* v8 ignore start — invariant guard: fires only when the live runtime\n diverges from the pure step() prediction, i.e. an internal bug. */\n if (predicted.snapshot.value !== after.value) {\n throw new Error(\n `aifsmjs/pbt: determinism violation — predicted \"${String(predicted.snapshot.value)}\" but runtime returned \"${String(after.value)}\" after ${this.toString()}`,\n );\n }\n /* v8 ignore stop */\n\n m.value = after.value;\n m.context = after.context as Ctx;\n m.status = after.status;\n m.reached.add(after.value);\n }\n\n toString(): string {\n return `send(${JSON.stringify(this.event)})`;\n }\n}\n\n/**\n * Build an `fc.Arbitrary` of FSM command sequences. Each command pulls one\n * event from the user-supplied arbitrary map and, when run, asserts that the\n * pure `step()` prediction matches the runtime's observable outcome.\n *\n * Pair this arbitrary with `fc.property(...)` inside a `fc.assert(...)` call,\n * or use the helpers in `aifsmjs/pbt` properties to get the six generic\n * invariants for free.\n */\nexport function commandsFromMachine<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n): fc.Arbitrary<Iterable<FsmCommand<Ctx, Evt, States>>> {\n const arbs: fc.Arbitrary<FsmCommand<Ctx, Evt, States>>[] = [];\n for (const arb of Object.values(eventArbitraries)) {\n arbs.push(arb.map((event) => new SendCommand(event, def, impl)));\n }\n return fc.commands(arbs, { size: \"+1\" });\n}\n","import * as fc from \"fast-check\";\nimport { initialSnapshot } from \"../fsm/definition.js\";\nimport { step } from \"../fsm/lifecycle.js\";\nimport { normalizeTransitions } from \"../fsm/resolver.js\";\nimport { createRuntime } from \"../fsm/runtime.js\";\nimport type { Guard, Implementations, MachineDef } from \"../fsm/types.js\";\nimport { mergeContext } from \"../fsm/updater.js\";\nimport { replay } from \"../replay/index.js\";\nimport {\n type EventArbitraries,\n type FsmModel,\n commandsFromMachine,\n initialModel,\n} from \"./commands.js\";\n\nexport type AssertOpts = Readonly<{\n numRuns?: number;\n seed?: number;\n verbose?: boolean;\n}>;\n\nfunction buildAssertOpts(opts: AssertOpts | undefined): fc.Parameters<unknown> {\n const out: fc.Parameters<unknown> = {};\n if (opts?.numRuns !== undefined) out.numRuns = opts.numRuns;\n if (opts?.seed !== undefined) out.seed = opts.seed;\n if (opts?.verbose) out.verbose = true;\n return out;\n}\n\n/**\n * #1 snapshotAlwaysFrozen — after any event sequence the live snapshot remains\n * frozen at the top level.\n */\nexport function snapshotAlwaysFrozen<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n fc.assert(\n fc.property(commandsFromMachine(def, impl, eventArbitraries), (cmds) => {\n const real = createRuntime(def, impl);\n const model: FsmModel<Ctx, States> = initialModel(def);\n fc.modelRun(() => ({ model, real }), cmds);\n return Object.isFrozen(real.getSnapshot());\n }),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * #2 unknownEventNoOp — sending an event whose `type` is not declared in any\n * state's `on` map never changes the snapshot.\n */\nexport function unknownEventNoOp<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n unknownType: string,\n opts?: AssertOpts,\n): void {\n fc.assert(\n fc.property(fc.constant(unknownType), (t) => {\n const initial = initialSnapshot(def);\n const result = step(def, initial, { type: t } as unknown as Evt, impl);\n return result.changed === false && result.snapshot === initial && result.effects.length === 0;\n }),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * #3 reachableStatesSubsetDeclared — every state visited during a run belongs\n * to `def.states`.\n */\nexport function reachableStatesSubsetDeclared<\n Ctx,\n Evt extends { type: string },\n States extends string,\n>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n const declared = new Set<string>(Object.keys(def.states));\n fc.assert(\n fc.property(commandsFromMachine(def, impl, eventArbitraries), (cmds) => {\n const real = createRuntime(def, impl);\n const model: FsmModel<Ctx, States> = initialModel(def);\n fc.modelRun(() => ({ model, real }), cmds);\n for (const s of model.reached as Set<string>) {\n /* v8 ignore next — property failure branch; an unreachable state would indicate a bug. */\n if (!declared.has(s)) return false;\n }\n return declared.has(real.getSnapshot().value);\n }),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * #4 replayEqualsFold — `replay(initial, log)` produces the same final state\n * as a live runtime fed the same events. Effects dispatched by the runtime are\n * ignored; the comparison is on `{ value, context }`.\n */\nexport function replayEqualsFold<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n const eventArb = fc.oneof(...Object.values(eventArbitraries));\n fc.assert(\n fc.property(fc.array(eventArb, { maxLength: 32 }), (events) => {\n const real = createRuntime(def, impl, { dispatchEffects: false });\n for (const e of events) real.send(e);\n const live = real.getSnapshot();\n const replayed = replay(initialSnapshot(def), events, def, impl).snapshot;\n return (\n live.value === replayed.value &&\n JSON.stringify(live.context) === JSON.stringify(replayed.context)\n );\n }),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * #5 guardsFalseNoTransition — when every candidate transition for the current\n * (state, event) pair carries a guard and every guard returns `false`, the\n * snapshot is unchanged (`changed === false`).\n *\n * Implementation: synthesise an impl that forces every guard to `false`, then\n * for each step whose candidate list is fully guarded, assert the step did not\n * change state. Steps with an unguarded fallback candidate (which fires even\n * when all guards are false) are skipped — the README claim is specifically\n * about the all-guards-false case.\n */\nexport function guardsFalseNoTransition<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n const blockedGuards = new Proxy(\n {},\n {\n get: () => () => false,\n },\n ) as Readonly<Record<string, Guard<Ctx, Evt>>>;\n const blockedImpl: Implementations<Ctx, Evt> = {\n ...impl,\n guards: blockedGuards,\n };\n // True when every candidate transition for (value, eventType) carries a\n // guard — i.e. blocking all guards leaves no unconditional fallback, so a\n // correct step() must report changed === false.\n const isFullyGuarded = (value: States, eventType: string): boolean => {\n const candidates = normalizeTransitions(def.states[value]?.on?.[eventType]);\n return candidates.length > 0 && candidates.every((t) => t.guard !== undefined);\n };\n fc.assert(\n fc.property(\n fc.array(fc.oneof(...Object.values(eventArbitraries)), { maxLength: 16 }),\n (events) => {\n let snap = initialSnapshot(def);\n for (const e of events) {\n const fullyGuarded = isFullyGuarded(snap.value, e.type);\n const r = step(def, snap, e, blockedImpl);\n // The named invariant: all guards false + no unconditional fallback\n // ⇒ no transition. Without this assertion the property was vacuous\n // (it only failed if step() threw).\n if (fullyGuarded && r.changed !== false) return false;\n snap = r.snapshot;\n }\n return true;\n },\n ),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * #6 assignDoesNotMutate — running an `assign`-style action never mutates the\n * previous context object. Verified by deep-equality check on a snapshot taken\n * before each event.\n */\nexport function assignDoesNotMutate<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n // Quick sanity guard: mergeContext is the only context mutator used by step.\n const dummy = { a: 1, b: 2 };\n const merged = mergeContext(dummy, { b: 3 });\n /* v8 ignore next — invariant guard; mergeContext returning the same ref would mean unit tests have already broken. */\n if (merged === dummy) throw new Error(\"aifsmjs/pbt: mergeContext returned the same reference\");\n\n fc.assert(\n fc.property(\n fc.array(fc.oneof(...Object.values(eventArbitraries)), { maxLength: 16 }),\n (events) => {\n let snap = initialSnapshot(def);\n for (const e of events) {\n const beforeCtxSerialised = JSON.stringify(snap.context);\n step(def, snap, e, impl);\n /* v8 ignore next — property failure branch; step() mutating snap.context would indicate a bug. */\n if (JSON.stringify(snap.context) !== beforeCtxSerialised) return false;\n // Continue with the actual result for subsequent events\n snap = step(def, snap, e, impl).snapshot;\n }\n return true;\n },\n ),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * Assert every generic property in one call. Use this when you don't need\n * fine-grained control over per-property options.\n */\nexport function assertAll<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts & { unknownEventType?: string },\n): void {\n snapshotAlwaysFrozen(def, impl, eventArbitraries, opts);\n unknownEventNoOp(def, impl, opts?.unknownEventType ?? \"__AIFSMJS_UNKNOWN__\", opts);\n reachableStatesSubsetDeclared(def, impl, eventArbitraries, opts);\n replayEqualsFold(def, impl, eventArbitraries, opts);\n guardsFalseNoTransition(def, impl, eventArbitraries, opts);\n assignDoesNotMutate(def, impl, eventArbitraries, opts);\n}\n","export {\n commandsFromMachine,\n initialModel,\n type EventArbitraries,\n type FsmCommand,\n type FsmModel,\n} from \"./commands.js\";\n\nexport {\n assertAll,\n assignDoesNotMutate,\n guardsFalseNoTransition,\n reachableStatesSubsetDeclared,\n replayEqualsFold,\n snapshotAlwaysFrozen,\n unknownEventNoOp,\n type AssertOpts,\n} from \"./properties.js\";\n\n// Convenience namespace mirroring the README:\n// import { properties } from \"aifsmjs/pbt\"\n// properties.snapshotAlwaysFrozen(...)\nimport * as propertiesModule from \"./properties.js\";\nexport const properties = propertiesModule;\n"]}
1
+ {"version":3,"sources":["../../src/pbt/commands.ts","../../src/pbt/properties.ts","../../src/pbt/index.ts"],"names":["step","fc","isDeepStrictEqual","fc2","createRuntime","initialSnapshot","replay","normalizeTransitions","ownValue"],"mappings":"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","file":"index.cjs","sourcesContent":["import * as fc from \"fast-check\";\nimport { step } from \"../fsm/lifecycle.js\";\nimport type { Implementations, MachineDef, Runtime, Snapshot } from \"../fsm/types.js\";\n\n/**\n * Pure-model representation of an FSM run, used by `fc.commands`.\n * `reached` tracks every state visited so generic properties can check\n * containment without re-running.\n */\nexport type FsmModel<Ctx, States extends string> = {\n value: States;\n context: Ctx;\n status: \"active\" | \"final\";\n reached: Set<States>;\n};\n\nexport type EventArbitraries<Evt extends { type: string }> = Readonly<\n Record<string, fc.Arbitrary<Evt>>\n>;\n\nexport type FsmCommand<Ctx, Evt extends { type: string }, States extends string> = fc.Command<\n FsmModel<Ctx, States>,\n Runtime<Ctx, Evt, States>\n>;\n\nexport function initialModel<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n): FsmModel<Ctx, States> {\n return {\n value: def.initial,\n context: def.context,\n status: def.states[def.initial]?.final ? \"final\" : \"active\",\n reached: new Set([def.initial]),\n };\n}\n\nclass SendCommand<Ctx, Evt extends { type: string }, States extends string>\n implements FsmCommand<Ctx, Evt, States>\n{\n constructor(\n private readonly event: Evt,\n private readonly def: MachineDef<Ctx, Evt, States>,\n private readonly impl: Implementations<Ctx, Evt>,\n ) {}\n\n check(_m: Readonly<FsmModel<Ctx, States>>): boolean {\n // Every event is always applicable; invariants are asserted in run().\n return true;\n }\n\n run(m: FsmModel<Ctx, States>, r: Runtime<Ctx, Evt, States>): void {\n const before: Snapshot<Ctx, States> = r.getSnapshot();\n r.send(this.event);\n const after = r.getSnapshot();\n\n const predicted = step(this.def, before, this.event, this.impl);\n\n /* v8 ignore start — invariant guard: fires only when the live runtime\n diverges from the pure step() prediction, i.e. an internal bug. */\n if (predicted.snapshot.value !== after.value) {\n throw new Error(\n `aifsmjs/pbt: determinism violation — predicted \"${String(predicted.snapshot.value)}\" but runtime returned \"${String(after.value)}\" after ${this.toString()}`,\n );\n }\n /* v8 ignore stop */\n\n m.value = after.value;\n m.context = after.context as Ctx;\n m.status = after.status;\n m.reached.add(after.value);\n }\n\n toString(): string {\n return `send(${JSON.stringify(this.event)})`;\n }\n}\n\n/**\n * Build an `fc.Arbitrary` of FSM command sequences. Each command pulls one\n * event from the user-supplied arbitrary map and, when run, asserts that the\n * pure `step()` prediction matches the runtime's observable outcome.\n *\n * Pair this arbitrary with `fc.property(...)` inside a `fc.assert(...)` call,\n * or use the helpers in `aifsmjs/pbt` properties to get the six generic\n * invariants for free.\n */\nexport function commandsFromMachine<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n): fc.Arbitrary<Iterable<FsmCommand<Ctx, Evt, States>>> {\n const arbs: fc.Arbitrary<FsmCommand<Ctx, Evt, States>>[] = [];\n for (const arb of Object.values(eventArbitraries)) {\n arbs.push(arb.map((event) => new SendCommand(event, def, impl)));\n }\n return fc.commands(arbs, { size: \"+1\" });\n}\n","import { isDeepStrictEqual } from \"node:util\";\nimport * as fc from \"fast-check\";\nimport { initialSnapshot } from \"../fsm/definition.js\";\nimport { ownValue } from \"../fsm/evaluator.js\";\nimport { step } from \"../fsm/lifecycle.js\";\nimport { normalizeTransitions } from \"../fsm/resolver.js\";\nimport { createRuntime } from \"../fsm/runtime.js\";\nimport type { Guard, Implementations, MachineDef } from \"../fsm/types.js\";\nimport { replay } from \"../replay/index.js\";\nimport {\n type EventArbitraries,\n type FsmModel,\n commandsFromMachine,\n initialModel,\n} from \"./commands.js\";\n\nexport type AssertOpts = Readonly<{\n numRuns?: number;\n seed?: number;\n verbose?: boolean;\n}>;\n\nfunction buildAssertOpts(opts: AssertOpts | undefined): fc.Parameters<unknown> {\n const out: fc.Parameters<unknown> = {};\n if (opts?.numRuns !== undefined) out.numRuns = opts.numRuns;\n if (opts?.seed !== undefined) out.seed = opts.seed;\n if (opts?.verbose) out.verbose = true;\n return out;\n}\n\n/**\n * Structural deep-equality for two context values (C3). Backed by `node:util`\n * `isDeepStrictEqual`, replacing the previous `JSON.stringify(a) === JSON.stringify(b)`\n * oracle which was unsound:\n *\n * - key-order-sensitive → false-FAIL on `{a:1,b:2}` vs `{b:2,a:1}`;\n * - drops undefined keys → false-PASS on `{v:undefined,w:1}` vs `{w:1}`;\n * - lossy for `Map`/`Set`/`Date` (all serialise to `{}` or an ISO string);\n * - throws on `BigInt`.\n *\n * `isDeepStrictEqual` distinguishes present-but-undefined from absent keys,\n * compares `Map`/`Set`/`Date` by contents, and tolerates `BigInt` — exactly\n * the verdicts a context-equality oracle for PBT requires. No new dependency\n * (Node built-in; the package already targets Node >=18).\n */\nexport function contextEquals(a: unknown, b: unknown): boolean {\n return isDeepStrictEqual(a, b);\n}\n\n/**\n * #1 snapshotAlwaysFrozen — after any event sequence the live snapshot remains\n * frozen at the top level.\n */\nexport function snapshotAlwaysFrozen<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n fc.assert(\n fc.property(commandsFromMachine(def, impl, eventArbitraries), (cmds) => {\n const real = createRuntime(def, impl, { dispatchEffects: false });\n // Dispose every generated run's runtime (its signal aborts, its child\n // runtimes are torn down); the asserted value is computed first.\n try {\n const model: FsmModel<Ctx, States> = initialModel(def);\n fc.modelRun(() => ({ model, real }), cmds);\n return Object.isFrozen(real.getSnapshot());\n } finally {\n real.dispose();\n }\n }),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * #2 unknownEventNoOp — sending an event whose `type` is not declared in any\n * state's `on` map never changes the snapshot.\n */\nexport function unknownEventNoOp<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n unknownType: string,\n opts?: AssertOpts,\n): void {\n fc.assert(\n fc.property(fc.constant(unknownType), (t) => {\n const initial = initialSnapshot(def);\n const result = step(def, initial, { type: t } as unknown as Evt, impl);\n return result.changed === false && result.snapshot === initial && result.effects.length === 0;\n }),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * #3 reachableStatesSubsetDeclared — every state visited during a run belongs\n * to `def.states`.\n */\nexport function reachableStatesSubsetDeclared<\n Ctx,\n Evt extends { type: string },\n States extends string,\n>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n const declared = new Set<string>(Object.keys(def.states));\n fc.assert(\n fc.property(commandsFromMachine(def, impl, eventArbitraries), (cmds) => {\n const real = createRuntime(def, impl, { dispatchEffects: false });\n try {\n const model: FsmModel<Ctx, States> = initialModel(def);\n fc.modelRun(() => ({ model, real }), cmds);\n for (const s of model.reached as Set<string>) {\n /* v8 ignore next — property failure branch; an unreachable state would indicate a bug. */\n if (!declared.has(s)) return false;\n }\n return declared.has(real.getSnapshot().value);\n } finally {\n real.dispose();\n }\n }),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * #4 replayEqualsFold — `replay(initial, log)` produces the same final state\n * as a live runtime fed the same events. Effects dispatched by the runtime are\n * ignored; the comparison is on `{ value, context }`.\n */\nexport function replayEqualsFold<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n const eventArb = fc.oneof(...Object.values(eventArbitraries));\n fc.assert(\n fc.property(fc.array(eventArb, { maxLength: 32 }), (events) => {\n const real = createRuntime(def, impl, { dispatchEffects: false });\n try {\n for (const e of events) real.send(e);\n const live = real.getSnapshot();\n const replayed = replay(initialSnapshot(def), events, def, impl).snapshot;\n return live.value === replayed.value && contextEquals(live.context, replayed.context);\n } finally {\n real.dispose();\n }\n }),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * #5 guardsFalseNoTransition — when every candidate transition for the current\n * (state, event) pair carries a guard and every guard returns `false`, the\n * snapshot is unchanged (`changed === false`).\n *\n * Implementation: synthesise an impl that forces every guard to `false`, then\n * for each step whose candidate list is fully guarded, assert the step did not\n * change state. Steps with an unguarded fallback candidate (which fires even\n * when all guards are false) are skipped — the README claim is specifically\n * about the all-guards-false case.\n */\nexport function guardsFalseNoTransition<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n // Every key reads as an own property: step() resolves guard refs with an\n // own-key lookup (the value itself still comes from `get`), so a `get` trap\n // alone would surface as UnknownGuardError.\n const blockedGuards = new Proxy(\n {},\n {\n get: () => () => false,\n getOwnPropertyDescriptor: () => ({ configurable: true }),\n },\n ) as Readonly<Record<string, Guard<Ctx, Evt>>>;\n const blockedImpl: Implementations<Ctx, Evt> = {\n ...impl,\n guards: blockedGuards,\n };\n // True when every candidate transition for (value, eventType) carries a\n // guard — i.e. blocking all guards leaves no unconditional fallback, so a\n // correct step() must report changed === false.\n const isFullyGuarded = (value: States, eventType: string): boolean => {\n const candidates = normalizeTransitions(ownValue(def.states[value]?.on, eventType));\n return candidates.length > 0 && candidates.every((t) => t.guard !== undefined);\n };\n fc.assert(\n fc.property(\n fc.array(fc.oneof(...Object.values(eventArbitraries)), { maxLength: 16 }),\n (events) => {\n let snap = initialSnapshot(def);\n for (const e of events) {\n const fullyGuarded = isFullyGuarded(snap.value, e.type);\n const r = step(def, snap, e, blockedImpl);\n // The named invariant: all guards false + no unconditional fallback\n // ⇒ no transition. Without this assertion the property was vacuous\n // (it only failed if step() threw).\n if (fullyGuarded && r.changed !== false) return false;\n snap = r.snapshot;\n }\n return true;\n },\n ),\n buildAssertOpts(opts),\n );\n}\n\n// Own keys/values of one object, plus Map/Set entries and Date time.\nconst ownState = (v: object): unknown[] => [\n ...Reflect.ownKeys(v).flatMap((k) => [k, (v as Record<PropertyKey, unknown>)[k]]),\n ...(v instanceof Map || v instanceof Set ? [...v.entries()].flat() : []),\n v instanceof Date && v.getTime(),\n];\n\n/**\n * #6 assignDoesNotMutate — running an `assign`-style action never mutates the\n * previous context object. Verified by re-checking the own state of every\n * object reachable from the pre-step context.\n */\nexport function assignDoesNotMutate<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts,\n): void {\n fc.assert(\n fc.property(\n fc.array(fc.oneof(...Object.values(eventArbitraries)), { maxLength: 16 }),\n (events) => {\n let snap = initialSnapshot(def);\n for (const e of events) {\n // Record, not clone (clones drop prototypes, throw on functions).\n const before = new Map<object, unknown[]>();\n const walk = (v: unknown): void => {\n if (!v || typeof v !== \"object\" || before.has(v)) return;\n const own = ownState(v);\n before.set(v, own);\n own.forEach(walk);\n };\n walk(snap.context);\n // `before` keeps the pre-step objects, so advancing `snap` first is safe.\n snap = step(def, snap, e, impl).snapshot;\n for (const [obj, own] of before) if (!contextEquals(ownState(obj), own)) return false;\n }\n return true;\n },\n ),\n buildAssertOpts(opts),\n );\n}\n\n/**\n * Assert every generic property in one call. Use this when you don't need\n * fine-grained control over per-property options.\n */\nexport function assertAll<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n eventArbitraries: EventArbitraries<Evt>,\n opts?: AssertOpts & { unknownEventType?: string },\n): void {\n snapshotAlwaysFrozen(def, impl, eventArbitraries, opts);\n unknownEventNoOp(def, impl, opts?.unknownEventType ?? \"__AIFSMJS_UNKNOWN__\", opts);\n reachableStatesSubsetDeclared(def, impl, eventArbitraries, opts);\n replayEqualsFold(def, impl, eventArbitraries, opts);\n guardsFalseNoTransition(def, impl, eventArbitraries, opts);\n assignDoesNotMutate(def, impl, eventArbitraries, opts);\n}\n","export {\n commandsFromMachine,\n initialModel,\n type EventArbitraries,\n type FsmCommand,\n type FsmModel,\n} from \"./commands.js\";\n\nexport {\n assertAll,\n assignDoesNotMutate,\n guardsFalseNoTransition,\n reachableStatesSubsetDeclared,\n replayEqualsFold,\n snapshotAlwaysFrozen,\n unknownEventNoOp,\n type AssertOpts,\n} from \"./properties.js\";\n\n// Convenience namespace mirroring the README:\n// import { properties } from \"aifsmjs/pbt\"\n// properties.snapshotAlwaysFrozen(...)\n// A frozen object literal rather than `import * as`: a namespace re-export\n// makes tsup emit a shared `__export` helper chunk that every subpath entry\n// (root, guards, timer, ...) then imports and pays for.\nimport {\n assertAll,\n assignDoesNotMutate,\n contextEquals,\n guardsFalseNoTransition,\n reachableStatesSubsetDeclared,\n replayEqualsFold,\n snapshotAlwaysFrozen,\n unknownEventNoOp,\n} from \"./properties.js\";\nexport const properties = Object.freeze({\n assertAll,\n assignDoesNotMutate,\n contextEquals,\n guardsFalseNoTransition,\n reachableStatesSubsetDeclared,\n replayEqualsFold,\n snapshotAlwaysFrozen,\n unknownEventNoOp,\n});\n"]}
@@ -1,5 +1,5 @@
1
1
  import * as fc from 'fast-check';
2
- import { j as Runtime, f as MachineDef, I as Implementations } from '../types-DIM7QTtf.cjs';
2
+ import { j as Runtime, f as MachineDef, I as Implementations } from '../types-CrDxFfBx.cjs';
3
3
 
4
4
  /**
5
5
  * Pure-model representation of an FSM run, used by `fc.commands`.
@@ -39,6 +39,22 @@ type AssertOpts = Readonly<{
39
39
  seed?: number;
40
40
  verbose?: boolean;
41
41
  }>;
42
+ /**
43
+ * Structural deep-equality for two context values (C3). Backed by `node:util`
44
+ * `isDeepStrictEqual`, replacing the previous `JSON.stringify(a) === JSON.stringify(b)`
45
+ * oracle which was unsound:
46
+ *
47
+ * - key-order-sensitive → false-FAIL on `{a:1,b:2}` vs `{b:2,a:1}`;
48
+ * - drops undefined keys → false-PASS on `{v:undefined,w:1}` vs `{w:1}`;
49
+ * - lossy for `Map`/`Set`/`Date` (all serialise to `{}` or an ISO string);
50
+ * - throws on `BigInt`.
51
+ *
52
+ * `isDeepStrictEqual` distinguishes present-but-undefined from absent keys,
53
+ * compares `Map`/`Set`/`Date` by contents, and tolerates `BigInt` — exactly
54
+ * the verdicts a context-equality oracle for PBT requires. No new dependency
55
+ * (Node built-in; the package already targets Node >=18).
56
+ */
57
+ declare function contextEquals(a: unknown, b: unknown): boolean;
42
58
  /**
43
59
  * #1 snapshotAlwaysFrozen — after any event sequence the live snapshot remains
44
60
  * frozen at the top level.
@@ -84,8 +100,8 @@ declare function guardsFalseNoTransition<Ctx, Evt extends {
84
100
  }, States extends string>(def: MachineDef<Ctx, Evt, States>, impl: Implementations<Ctx, Evt>, eventArbitraries: EventArbitraries<Evt>, opts?: AssertOpts): void;
85
101
  /**
86
102
  * #6 assignDoesNotMutate — running an `assign`-style action never mutates the
87
- * previous context object. Verified by deep-equality check on a snapshot taken
88
- * before each event.
103
+ * previous context object. Verified by re-checking the own state of every
104
+ * object reachable from the pre-step context.
89
105
  */
90
106
  declare function assignDoesNotMutate<Ctx, Evt extends {
91
107
  type: string;
@@ -100,18 +116,15 @@ declare function assertAll<Ctx, Evt extends {
100
116
  unknownEventType?: string;
101
117
  }): void;
102
118
 
103
- type propertiesModule_AssertOpts = AssertOpts;
104
- declare const propertiesModule_assertAll: typeof assertAll;
105
- declare const propertiesModule_assignDoesNotMutate: typeof assignDoesNotMutate;
106
- declare const propertiesModule_guardsFalseNoTransition: typeof guardsFalseNoTransition;
107
- declare const propertiesModule_reachableStatesSubsetDeclared: typeof reachableStatesSubsetDeclared;
108
- declare const propertiesModule_replayEqualsFold: typeof replayEqualsFold;
109
- declare const propertiesModule_snapshotAlwaysFrozen: typeof snapshotAlwaysFrozen;
110
- declare const propertiesModule_unknownEventNoOp: typeof unknownEventNoOp;
111
- declare namespace propertiesModule {
112
- export { type propertiesModule_AssertOpts as AssertOpts, propertiesModule_assertAll as assertAll, propertiesModule_assignDoesNotMutate as assignDoesNotMutate, propertiesModule_guardsFalseNoTransition as guardsFalseNoTransition, propertiesModule_reachableStatesSubsetDeclared as reachableStatesSubsetDeclared, propertiesModule_replayEqualsFold as replayEqualsFold, propertiesModule_snapshotAlwaysFrozen as snapshotAlwaysFrozen, propertiesModule_unknownEventNoOp as unknownEventNoOp };
113
- }
114
-
115
- declare const properties: typeof propertiesModule;
119
+ declare const properties: Readonly<{
120
+ assertAll: typeof assertAll;
121
+ assignDoesNotMutate: typeof assignDoesNotMutate;
122
+ contextEquals: typeof contextEquals;
123
+ guardsFalseNoTransition: typeof guardsFalseNoTransition;
124
+ reachableStatesSubsetDeclared: typeof reachableStatesSubsetDeclared;
125
+ replayEqualsFold: typeof replayEqualsFold;
126
+ snapshotAlwaysFrozen: typeof snapshotAlwaysFrozen;
127
+ unknownEventNoOp: typeof unknownEventNoOp;
128
+ }>;
116
129
 
117
130
  export { type AssertOpts, type EventArbitraries, type FsmCommand, type FsmModel, assertAll, assignDoesNotMutate, commandsFromMachine, guardsFalseNoTransition, initialModel, properties, reachableStatesSubsetDeclared, replayEqualsFold, snapshotAlwaysFrozen, unknownEventNoOp };