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.
- package/README.md +14 -7
- package/README_ZHTW.md +14 -7
- package/dist/{chunk-ZLQ7HZCE.js → chunk-D6H64FSI.js} +66 -46
- package/dist/chunk-D6H64FSI.js.map +1 -0
- package/dist/{chunk-NEJYZAKR.js → chunk-Q45LGXHO.js} +3 -3
- package/dist/{chunk-NEJYZAKR.js.map → chunk-Q45LGXHO.js.map} +1 -1
- package/dist/{chunk-CDK25FTD.cjs → chunk-QCTA2X4J.cjs} +7 -3
- package/dist/chunk-QCTA2X4J.cjs.map +1 -0
- package/dist/{chunk-A7U7QQL5.js → chunk-SSNKGEVB.js} +7 -4
- package/dist/chunk-SSNKGEVB.js.map +1 -0
- package/dist/{chunk-I354FONA.cjs → chunk-VGLF5NQH.cjs} +69 -46
- package/dist/chunk-VGLF5NQH.cjs.map +1 -0
- package/dist/{chunk-FHTQ7LSQ.cjs → chunk-VV5TKFQO.cjs} +4 -4
- package/dist/{chunk-FHTQ7LSQ.cjs.map → chunk-VV5TKFQO.cjs.map} +1 -1
- package/dist/{chunk-VZCSHTOI.js → chunk-XA24A7VP.js} +156 -157
- package/dist/chunk-XA24A7VP.js.map +1 -0
- package/dist/{chunk-2MME5V4F.cjs → chunk-YK25NVFC.cjs} +160 -161
- package/dist/chunk-YK25NVFC.cjs.map +1 -0
- package/dist/effects/index.cjs +3 -4
- package/dist/effects/index.cjs.map +1 -1
- package/dist/effects/index.d.cts +1 -1
- package/dist/effects/index.d.ts +1 -1
- package/dist/effects/index.js +2 -3
- package/dist/effects/index.js.map +1 -1
- package/dist/guards/index.cjs +5 -6
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +1 -1
- package/dist/guards/index.d.ts +1 -1
- package/dist/guards/index.js +2 -3
- package/dist/guards/index.js.map +1 -1
- package/dist/index.cjs +31 -28
- package/dist/index.d.cts +66 -18
- package/dist/index.d.ts +66 -18
- package/dist/index.js +3 -4
- package/dist/inspect/index.cjs +0 -2
- package/dist/inspect/index.cjs.map +1 -1
- package/dist/inspect/index.d.cts +1 -1
- package/dist/inspect/index.d.ts +1 -1
- package/dist/inspect/index.js +0 -2
- package/dist/inspect/index.js.map +1 -1
- package/dist/pbt/index.cjs +69 -48
- package/dist/pbt/index.cjs.map +1 -1
- package/dist/pbt/index.d.cts +29 -16
- package/dist/pbt/index.d.ts +29 -16
- package/dist/pbt/index.js +61 -40
- package/dist/pbt/index.js.map +1 -1
- package/dist/replay/index.cjs +4 -5
- package/dist/replay/index.d.cts +1 -1
- package/dist/replay/index.d.ts +1 -1
- package/dist/replay/index.js +3 -4
- package/dist/timer/index.cjs +18 -5
- package/dist/timer/index.cjs.map +1 -1
- package/dist/timer/index.d.cts +6 -0
- package/dist/timer/index.d.ts +6 -0
- package/dist/timer/index.js +18 -5
- package/dist/timer/index.js.map +1 -1
- package/dist/{types-DIM7QTtf.d.ts → types-CrDxFfBx.d.cts} +64 -16
- package/dist/{types-DIM7QTtf.d.cts → types-CrDxFfBx.d.ts} +64 -16
- package/llms-full.txt +89 -17
- package/package.json +57 -22
- package/dist/chunk-2MME5V4F.cjs.map +0 -1
- package/dist/chunk-A7U7QQL5.js.map +0 -1
- package/dist/chunk-CDK25FTD.cjs.map +0 -1
- package/dist/chunk-I354FONA.cjs.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -9
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-Q7SFCCGT.cjs +0 -11
- package/dist/chunk-Q7SFCCGT.cjs.map +0 -1
- package/dist/chunk-VZCSHTOI.js.map +0 -1
- package/dist/chunk-ZLQ7HZCE.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Small deterministic FSM library for replayable TypeScript/JavaScript state machines. Definitions are plain data; guards/actions/effects are injected at runtime.
|
|
4
4
|
|
|
5
|
-
> **Status: 0.
|
|
5
|
+
> **Status: 0.6.0 - stable 1.0-track core.** Core FSM, guards, effects, inspect, replay, PBT helpers, scheduler, and sub-machines are live.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -49,7 +49,7 @@ Prefer `setup<Ctx, Evt>().defineMachine()` for state inference. Use bare `define
|
|
|
49
49
|
| --- | --- |
|
|
50
50
|
| `aifsmjs` | `setup`, `defineMachine`, `createRuntime`, `createMachine`, `step`, `assign`, snapshots, runtime/errors/types. |
|
|
51
51
|
| `aifsmjs/guards` | `and`, `or`, `not`, `stateIn`. Guards must be synchronous. |
|
|
52
|
-
| `aifsmjs/effects` | `
|
|
52
|
+
| `aifsmjs/effects` | `createEnqueuer()` and `runEffects()`. |
|
|
53
53
|
| `aifsmjs/inspect` | Read-only middleware helpers: `logger`, `persist`, `recorder`. |
|
|
54
54
|
| `aifsmjs/replay` | Pure event-log replay. |
|
|
55
55
|
| `aifsmjs/pbt` | fast-check property helpers. |
|
|
@@ -58,18 +58,25 @@ Prefer `setup<Ctx, Evt>().defineMachine()` for state inference. Use bare `define
|
|
|
58
58
|
## Lifecycle Rules
|
|
59
59
|
|
|
60
60
|
- `step(def, snapshot, event, impl)` is pure and returns `{ snapshot, effects, changed }`.
|
|
61
|
-
- `createRuntime()` owns mutable runtime state,
|
|
61
|
+
- `createRuntime()` owns mutable runtime state. Per event it commits, then runs middleware, effects, `subscribe` listeners and `'transition'` listeners, in that order.
|
|
62
|
+
- `send()`/`reset()` are run-to-completion: a call made from middleware, an effect handler or a listener is queued and runs after the current event's notifications, with the same full sequence.
|
|
62
63
|
- Guards and reducers are sync. Thenable guards throw `AsyncGuardError`.
|
|
63
|
-
- Effects are fire-and-forget descriptors. Async rejection is routed to the runtime `"error"` channel.
|
|
64
|
-
- `reset()` rewinds the snapshot and notifies listeners,
|
|
64
|
+
- Effects are fire-and-forget descriptors. Async rejection is routed to the runtime `"error"` channel; with no `"error"` listener it is dropped (with a `console.warn` outside production).
|
|
65
|
+
- `reset()` rewinds to the initial snapshot without running entry actions, and notifies listeners whenever the value, status or context reference changes.
|
|
65
66
|
- `dispose()` is idempotent; post-dispose `send()`/`reset()` throw `RuntimeDisposedError`.
|
|
67
|
+
- Misused arguments to `defineMachine`, `createRuntime` and the runtime methods throw `InvalidDefinitionError`.
|
|
66
68
|
|
|
67
69
|
## Sharp Edges
|
|
68
70
|
|
|
69
|
-
- Middleware
|
|
70
|
-
-
|
|
71
|
+
- Middleware, synchronous effect and subscriber throws happen after snapshot commit. A throw can leave the committed snapshot visible without later notification, and it drops any queued `send()`/`reset()` calls.
|
|
72
|
+
- A nested `send()` returns the snapshot committed at the time of the call, not the outcome of its own event. Read `getSnapshot()` after the outermost call returns, or subscribe.
|
|
73
|
+
- A listener removed while a notification is running (unsubscribe, `once`, `signal`, `dispose()`) is skipped for the rest of that round.
|
|
74
|
+
- Middleware never freezes your event, but it deep-freezes effect descriptors, including any object you passed as a payload.
|
|
75
|
+
- Actions on an object context must return a plain-object patch (or nothing); the merge keeps the context's prototype. Returning `false`, `0` or `""` throws `InvalidActionResultError`. Prefer plain-object contexts.
|
|
76
|
+
- Sub-machine replacement builds the new child first: on init failure the old child stays live; on dispose failure the old child is already torn down and the new one is discarded.
|
|
71
77
|
- `subRuntime()` can return a disposed child handle if external code disposed it; it is recreated only after the parent exits and re-enters the sub state.
|
|
72
78
|
- `setup().defineMachine()` uses `NoInfer` so states infer from `keyof states`; keep regression tests for exact optional property configurations.
|
|
79
|
+
- `after()` throws `RangeError` for `NaN`, `Infinity` or a negative delay, and clamps delays above 2^31-1 ms (about 24.8 days). To mean "never", do not schedule.
|
|
73
80
|
- Do not perform async I/O inside guards or actions. Send events from effects instead.
|
|
74
81
|
|
|
75
82
|
## AI Context
|
package/README_ZHTW.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
小型 deterministic FSM 函式庫,適合可 replay 的 TypeScript/JavaScript state machine。Definition 是 plain data;guards/actions/effects 在 runtime 注入。
|
|
4
4
|
|
|
5
|
-
> **狀態:0.
|
|
5
|
+
> **狀態:0.6.0 - 穩定 1.0 軌道核心。** Core FSM、guards、effects、inspect、replay、PBT helpers、scheduler、sub-machines 都已可用。
|
|
6
6
|
|
|
7
7
|
## 安裝
|
|
8
8
|
|
|
@@ -49,7 +49,7 @@ console.log(runtime.getSnapshot().value); // "green"
|
|
|
49
49
|
| --- | --- |
|
|
50
50
|
| `aifsmjs` | `setup`、`defineMachine`、`createRuntime`、`createMachine`、`step`、`assign`、snapshots、runtime/errors/types。 |
|
|
51
51
|
| `aifsmjs/guards` | `and`、`or`、`not`、`stateIn`。Guard 必須同步。 |
|
|
52
|
-
| `aifsmjs/effects` | `
|
|
52
|
+
| `aifsmjs/effects` | `createEnqueuer()` 與 `runEffects()`。 |
|
|
53
53
|
| `aifsmjs/inspect` | Read-only middleware helpers:`logger`、`persist`、`recorder`。 |
|
|
54
54
|
| `aifsmjs/replay` | 純 event-log replay。 |
|
|
55
55
|
| `aifsmjs/pbt` | fast-check property helpers。 |
|
|
@@ -58,18 +58,25 @@ console.log(runtime.getSnapshot().value); // "green"
|
|
|
58
58
|
## Lifecycle Rules
|
|
59
59
|
|
|
60
60
|
- `step(def, snapshot, event, impl)` 是 pure function,回傳 `{ snapshot, effects, changed }`。
|
|
61
|
-
- `createRuntime()` 持有 mutable runtime state
|
|
61
|
+
- `createRuntime()` 持有 mutable runtime state。每個 event 先 commit,再依序執行 middleware、effects、`subscribe` listeners 與 `'transition'` listeners。
|
|
62
|
+
- `send()`/`reset()` 採 run-to-completion:在 middleware、effect handler 或 listener 內的呼叫會排入佇列,等目前 event 的通知全部完成後,再以同樣的完整流程執行。
|
|
62
63
|
- Guards 與 reducers 必須同步。Thenable guards 會丟 `AsyncGuardError`。
|
|
63
|
-
- Effects 是 fire-and-forget descriptors。Async rejection 會送到 runtime `"error"` channel
|
|
64
|
-
- `reset()` 會回到 initial snapshot
|
|
64
|
+
- Effects 是 fire-and-forget descriptors。Async rejection 會送到 runtime `"error"` channel;沒有 `"error"` listener 時會被丟棄(非 production 環境會 `console.warn`)。
|
|
65
|
+
- `reset()` 會回到 initial snapshot 且不執行 entry actions;只要 value、status 或 context 參照有變,就會通知 listeners。
|
|
65
66
|
- `dispose()` 可重複呼叫;dispose 後 `send()`/`reset()` 會丟 `RuntimeDisposedError`。
|
|
67
|
+
- 傳給 `defineMachine`、`createRuntime` 與 runtime 方法的參數不正確時,會丟 `InvalidDefinitionError`。
|
|
66
68
|
|
|
67
69
|
## 注意事項
|
|
68
70
|
|
|
69
|
-
- Middleware
|
|
70
|
-
-
|
|
71
|
+
- Middleware、同步 effect 與 subscriber 的 throw 發生在 snapshot commit 之後;可能留下已 commit 的 snapshot 但後續通知中斷,並丟棄佇列中尚未執行的 `send()`/`reset()`。
|
|
72
|
+
- 巢狀 `send()` 回傳的是呼叫當下已 commit 的 snapshot,而不是它自己 event 的結果。請在最外層呼叫回傳後再讀 `getSnapshot()`,或改用 subscribe。
|
|
73
|
+
- 通知進行中被移除的 listener(unsubscribe、`once`、`signal`、`dispose()`)在該輪剩下的部分會被略過。
|
|
74
|
+
- Middleware 不會凍結你的 event,但會深度凍結 effect descriptors,包含你當作 payload 傳入的物件。
|
|
75
|
+
- Context 是物件時,action 必須回傳 plain-object patch(或不回傳);合併會保留 context 的原型。回傳 `false`、`0` 或 `""` 會丟 `InvalidActionResultError`。建議使用 plain-object context。
|
|
76
|
+
- Sub-machine replacement 會先建立新 child:init failure 時舊 child 仍然有效;dispose failure 時舊 child 已被 teardown,新 child 會被捨棄。
|
|
71
77
|
- 若外部自行 dispose child,`subRuntime()` 可能回傳 disposed handle;只有 parent 離開並重新進入 sub state 才會重建。
|
|
72
78
|
- `setup().defineMachine()` 使用 `NoInfer`,讓 states 從 `keyof states` 推斷;請保留 exact optional property 的回歸測試。
|
|
79
|
+
- `after()` 遇到 `NaN`、`Infinity` 或負數延遲會丟 `RangeError`,超過 2^31-1 毫秒(約 24.8 天)的延遲會被截為 2^31-1。若要表示「永不觸發」,就不要排程。
|
|
73
80
|
- 不要在 guards 或 actions 內做 async I/O;請從 effects 發事件回來。
|
|
74
81
|
|
|
75
82
|
## AI Context
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createEnqueuer } from './chunk-JKZAOPQC.js';
|
|
2
|
-
import { evalGuard } from './chunk-
|
|
2
|
+
import { ownValue, evalGuard } from './chunk-SSNKGEVB.js';
|
|
3
3
|
|
|
4
4
|
// src/fsm/resolver.ts
|
|
5
5
|
function normalizeTransition(entry) {
|
|
@@ -15,19 +15,26 @@ function normalizeTransitions(entry) {
|
|
|
15
15
|
function resolveTransitions(def, stateValue, eventType) {
|
|
16
16
|
const state = def.states[stateValue];
|
|
17
17
|
if (!state || !state.on) return [];
|
|
18
|
-
return normalizeTransitions(state.on
|
|
18
|
+
return normalizeTransitions(ownValue(state.on, eventType));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// src/fsm/snapshot.ts
|
|
22
|
-
var IS_DEV =
|
|
22
|
+
var IS_DEV = false;
|
|
23
|
+
try {
|
|
24
|
+
IS_DEV = process.env.NODE_ENV !== "production";
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
23
27
|
function isPlainObject(value) {
|
|
24
28
|
if (value === null || typeof value !== "object") return false;
|
|
25
29
|
const proto = Object.getPrototypeOf(value);
|
|
26
30
|
return proto === Object.prototype || proto === null;
|
|
27
31
|
}
|
|
32
|
+
var DEEP_FROZEN = /* @__PURE__ */ new WeakSet();
|
|
28
33
|
function deepFreeze(value) {
|
|
29
34
|
if (value === null || typeof value !== "object") return value;
|
|
30
|
-
if (
|
|
35
|
+
if (DEEP_FROZEN.has(value)) return value;
|
|
36
|
+
DEEP_FROZEN.add(value);
|
|
37
|
+
if (ArrayBuffer.isView(value)) return value;
|
|
31
38
|
Object.freeze(value);
|
|
32
39
|
if (Array.isArray(value)) {
|
|
33
40
|
for (const item of value) deepFreeze(item);
|
|
@@ -49,23 +56,6 @@ function createSnapshot(args) {
|
|
|
49
56
|
});
|
|
50
57
|
}
|
|
51
58
|
|
|
52
|
-
// src/fsm/updater.ts
|
|
53
|
-
function isPlainRecord(value) {
|
|
54
|
-
if (value === null || typeof value !== "object") return false;
|
|
55
|
-
const proto = Object.getPrototypeOf(value);
|
|
56
|
-
return proto === Object.prototype || proto === null;
|
|
57
|
-
}
|
|
58
|
-
function assign(updater) {
|
|
59
|
-
return ({ context, event }) => updater({ context, event });
|
|
60
|
-
}
|
|
61
|
-
function mergeContext(current, patch) {
|
|
62
|
-
if (patch === void 0 || patch === null) return current;
|
|
63
|
-
if (isPlainRecord(current) && isPlainRecord(patch)) {
|
|
64
|
-
return { ...current, ...patch };
|
|
65
|
-
}
|
|
66
|
-
return patch;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
59
|
// src/fsm/lifecycle.ts
|
|
70
60
|
var UnknownActionError = class extends Error {
|
|
71
61
|
actionName;
|
|
@@ -75,9 +65,19 @@ var UnknownActionError = class extends Error {
|
|
|
75
65
|
this.actionName = actionName;
|
|
76
66
|
}
|
|
77
67
|
};
|
|
68
|
+
var InvalidActionResultError = class extends Error {
|
|
69
|
+
actionName;
|
|
70
|
+
constructor(actionName, patch) {
|
|
71
|
+
super(
|
|
72
|
+
`aifsmjs: action "${actionName}" returned ${typeof patch}; an object context accepts only a plain-object patch (or undefined)`
|
|
73
|
+
);
|
|
74
|
+
this.name = "InvalidActionResultError";
|
|
75
|
+
this.actionName = actionName;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
78
|
function resolveAction(ref, impl) {
|
|
79
79
|
if (typeof ref === "function") return ref;
|
|
80
|
-
const fn = impl.actions
|
|
80
|
+
const fn = ownValue(impl.actions, ref);
|
|
81
81
|
if (!fn) throw new UnknownActionError(ref);
|
|
82
82
|
return fn;
|
|
83
83
|
}
|
|
@@ -88,30 +88,27 @@ function runActions(refs, ctx, event, impl, effectSink) {
|
|
|
88
88
|
for (const ref of refs) {
|
|
89
89
|
const fn = resolveAction(ref, impl);
|
|
90
90
|
const patch = fn({ context: current, event, enqueue });
|
|
91
|
-
current = mergeContext(current, patch);
|
|
91
|
+
current = mergeContext(current, patch, typeof ref === "string" ? ref : fn.name || "<inline>");
|
|
92
92
|
}
|
|
93
93
|
return current;
|
|
94
94
|
}
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (evalGuard(t.guard,
|
|
95
|
+
function chooseTransition(def, snapshot, event, impl) {
|
|
96
|
+
if (snapshot.status === "final") return void 0;
|
|
97
|
+
for (const t of resolveTransitions(def, snapshot.value, event.type)) {
|
|
98
|
+
if (!t.guard || evalGuard(t.guard, snapshot.context, event, impl, snapshot.value)) return t;
|
|
99
99
|
}
|
|
100
100
|
return void 0;
|
|
101
101
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
const state = def.states[snapshot.value];
|
|
107
|
-
if (!state) {
|
|
108
|
-
return Object.freeze({ snapshot, effects: [], changed: false });
|
|
109
|
-
}
|
|
110
|
-
const candidateList = normalizeTransitions(state.on?.[event.type]);
|
|
111
|
-
const chosen = pickTransition(candidateList, snapshot.context, event, impl, snapshot.value);
|
|
102
|
+
var NO_EFFECTS = Object.freeze([]);
|
|
103
|
+
function stepWithMeta(def, snapshot, event, impl) {
|
|
104
|
+
const chosen = chooseTransition(def, snapshot, event, impl);
|
|
112
105
|
if (!chosen) {
|
|
113
|
-
return
|
|
106
|
+
return {
|
|
107
|
+
result: Object.freeze({ snapshot, effects: NO_EFFECTS, changed: false }),
|
|
108
|
+
external: false
|
|
109
|
+
};
|
|
114
110
|
}
|
|
111
|
+
const state = def.states[snapshot.value];
|
|
115
112
|
const isExternal = chosen.target !== void 0;
|
|
116
113
|
const nextStateValue = chosen.target ?? snapshot.value;
|
|
117
114
|
const nextState = def.states[nextStateValue];
|
|
@@ -130,13 +127,36 @@ function step(def, snapshot, event, impl) {
|
|
|
130
127
|
context: ctx,
|
|
131
128
|
status
|
|
132
129
|
});
|
|
133
|
-
return
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
return {
|
|
131
|
+
result: Object.freeze({
|
|
132
|
+
snapshot: nextSnapshot,
|
|
133
|
+
effects: Object.freeze(effectSink.slice()),
|
|
134
|
+
changed: true
|
|
135
|
+
}),
|
|
136
|
+
external: isExternal
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function step(def, snapshot, event, impl) {
|
|
140
|
+
return stepWithMeta(def, snapshot, event, impl).result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/fsm/updater.ts
|
|
144
|
+
function assign(updater) {
|
|
145
|
+
return ({ context, event }) => updater({ context, event });
|
|
146
|
+
}
|
|
147
|
+
function mergeContext(current, patch, actionName = "<inline>") {
|
|
148
|
+
if (patch === void 0 || patch === null) return current;
|
|
149
|
+
if (current !== null && typeof current === "object" && !Array.isArray(current) && !ArrayBuffer.isView(current)) {
|
|
150
|
+
if (isPlainObject(patch)) {
|
|
151
|
+
return Object.setPrototypeOf({ ...current, ...patch }, Object.getPrototypeOf(current));
|
|
152
|
+
}
|
|
153
|
+
if (typeof patch !== "object" && typeof patch !== "function") {
|
|
154
|
+
throw new InvalidActionResultError(actionName, patch);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return patch;
|
|
138
158
|
}
|
|
139
159
|
|
|
140
|
-
export { UnknownActionError, assign, createSnapshot, deepFreeze, freezeSnapshot, mergeContext, normalizeTransition, normalizeTransitions, resolveTransitions, step };
|
|
141
|
-
//# sourceMappingURL=chunk-
|
|
142
|
-
//# sourceMappingURL=chunk-
|
|
160
|
+
export { IS_DEV, InvalidActionResultError, UnknownActionError, assign, chooseTransition, createSnapshot, deepFreeze, freezeSnapshot, mergeContext, normalizeTransition, normalizeTransitions, resolveTransitions, step, stepWithMeta };
|
|
161
|
+
//# sourceMappingURL=chunk-D6H64FSI.js.map
|
|
162
|
+
//# sourceMappingURL=chunk-D6H64FSI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fsm/resolver.ts","../src/fsm/snapshot.ts","../src/fsm/lifecycle.ts","../src/fsm/updater.ts"],"names":[],"mappings":"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","file":"chunk-D6H64FSI.js","sourcesContent":["import { ownValue } from \"./evaluator.js\";\nimport type { MachineDef, TransitionConfig, TransitionDef } from \"./types.js\";\n\n/**\n * Normalize a single transition config into its object form. The string\n * shorthand `\"targetState\"` (à la XState) becomes `{ target: \"targetState\" }`;\n * the object form is returned unchanged. Centralised here so every consumer\n * (`step`, `resolveTransitions`, `can`, validation) sees the same shape.\n *\n * @since 0.5.3\n */\nexport function normalizeTransition<Ctx, Evt, States extends string>(\n entry: TransitionConfig<Ctx, Evt, States>,\n): TransitionDef<Ctx, Evt, States> {\n return typeof entry === \"string\" ? ({ target: entry } as TransitionDef<Ctx, Evt, States>) : entry;\n}\n\n/**\n * Normalize the raw `state.on[eventType]` value (object, string shorthand, or\n * an array mixing both) into an ordered list of {@link TransitionDef} objects.\n * Declaration order is preserved.\n *\n * @since 0.5.3\n */\nexport function normalizeTransitions<Ctx, Evt, States extends string>(\n entry:\n | TransitionConfig<Ctx, Evt, States>\n | readonly TransitionConfig<Ctx, Evt, States>[]\n | undefined,\n): readonly TransitionDef<Ctx, Evt, States>[] {\n if (entry === undefined) return [];\n if (Array.isArray(entry)) {\n // Hot path (send/step/can run this per event): when no string shorthand is\n // present, return the original array instead of allocating a normalized copy.\n return entry.some((t) => typeof t === \"string\")\n ? entry.map((t) => normalizeTransition(t))\n : (entry as readonly TransitionDef<Ctx, Evt, States>[]);\n }\n return [normalizeTransition(entry as TransitionConfig<Ctx, Evt, States>)];\n}\n\n/**\n * Return all transition candidates for (state, eventType). Order is preserved\n * from the declaration so that guard fallthrough behaves predictably. String\n * shorthands are normalized to `{ target }` objects.\n *\n * If the event has no entry under the given state, an empty array is returned.\n */\nexport function resolveTransitions<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n stateValue: States,\n eventType: string,\n): readonly TransitionDef<Ctx, Evt, States>[] {\n const state = def.states[stateValue];\n if (!state || !state.on) return [];\n return normalizeTransitions(ownValue(state.on, eventType));\n}\n","import type { Snapshot } from \"./types.js\";\n\n// Written so a bundler define-replacement of `process.env.NODE_ENV` (Vite,\n// webpack 5, ...) still applies: those tools replace only the\n// `process.env.NODE_ENV` expression, not a runtime `process` global, so\n// gating on `typeof process !== \"undefined\"` first left IS_DEV permanently\n// false in a browser build even though NODE_ENV !== \"production\". A plain\n// try/catch around the read lets the replaced literal survive while still\n// falling back to false wherever `process` is entirely absent. Exported for\n// internal dev-only diagnostics (runtime.ts); not re-exported from the root.\nexport let IS_DEV = false;\ntry {\n IS_DEV = process.env.NODE_ENV !== \"production\";\n} catch {\n /* no `process` global (browser without bundler define-replacement) */\n}\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\n// Trees already deep-frozen here. Short-circuits re-walking carried-over\n// context and guards cycles; unlike `Object.isFrozen` it does not stop at a\n// shallow-frozen object (e.g. an effect descriptor) whose children are mutable.\nconst DEEP_FROZEN = new WeakSet<object>();\n\nexport function deepFreeze<T>(value: T): T {\n if (value === null || typeof value !== \"object\") return value;\n if (DEEP_FROZEN.has(value)) return value;\n DEEP_FROZEN.add(value);\n // Object.freeze throws on a non-empty TypedArray / Buffer; binary data in\n // context or event payloads is left mutable (caller-owned) instead.\n if (ArrayBuffer.isView(value)) return value;\n Object.freeze(value);\n if (Array.isArray(value)) {\n for (const item of value) deepFreeze(item);\n } else if (isPlainObject(value)) {\n for (const key of Object.keys(value)) {\n deepFreeze((value as Record<string, unknown>)[key]);\n }\n }\n return value;\n}\n\n/**\n * Wrap a freshly built snapshot. In dev mode the whole tree is deep-frozen so\n * accidental mutation throws immediately. In production only the top object is\n * frozen, keeping the cost negligible.\n */\nexport function freezeSnapshot<C, S extends string>(snap: Snapshot<C, S>): Snapshot<C, S> {\n // IS_DEV is always true in vitest; the production branch (`Object.freeze`)\n // is exercised only when NODE_ENV === \"production\" and is intentionally\n // left out of the coverage threshold.\n return IS_DEV ? deepFreeze(snap) : Object.freeze(snap);\n}\n\nexport function createSnapshot<C, S extends string>(args: {\n value: S;\n context: C;\n status?: \"active\" | \"final\";\n}): Snapshot<C, S> {\n return freezeSnapshot({\n value: args.value,\n context: args.context,\n status: args.status ?? \"active\",\n });\n}\n","import { createEnqueuer } from \"../effects/enqueuer.js\";\nimport { evalGuard, ownValue } from \"./evaluator.js\";\nimport { resolveTransitions } from \"./resolver.js\";\nimport { freezeSnapshot } from \"./snapshot.js\";\nimport type {\n Action,\n ActionRef,\n Effect,\n Implementations,\n MachineDef,\n Snapshot,\n StepResult,\n TransitionDef,\n} from \"./types.js\";\nimport { mergeContext } from \"./updater.js\";\n\nexport class UnknownActionError extends Error {\n readonly actionName: string;\n constructor(actionName: string) {\n super(`aifsmjs: action \"${actionName}\" not found in implementations.actions`);\n this.name = \"UnknownActionError\";\n this.actionName = actionName;\n }\n}\n\n/**\n * Thrown (from `step()`, and so from `send()` before anything is committed)\n * when an action returns a non-nullish primitive (`false`, `0`, `\"\"`, ...) as\n * the patch for an object context. An object context accepts only a\n * plain-object patch or `undefined`/`null` (no change).\n *\n * `actionName` is the string ref, the inline function's name, or `\"<inline>\"`\n * for an anonymous inline action (mirrors guard naming).\n *\n * @since 0.6.0\n */\nexport class InvalidActionResultError extends Error {\n readonly actionName: string;\n constructor(actionName: string, patch: unknown) {\n super(\n `aifsmjs: action \"${actionName}\" returned ${typeof patch}; an object context accepts only a plain-object patch (or undefined)`,\n );\n this.name = \"InvalidActionResultError\";\n this.actionName = actionName;\n }\n}\n\nfunction resolveAction<Ctx, Evt>(\n ref: ActionRef<Ctx, Evt>,\n impl: Implementations<Ctx, Evt>,\n): Action<Ctx, Evt> {\n if (typeof ref === \"function\") return ref;\n const fn = ownValue(impl.actions, ref);\n if (!fn) throw new UnknownActionError(ref);\n return fn;\n}\n\nfunction runActions<Ctx, Evt>(\n refs: readonly ActionRef<Ctx, Evt>[] | undefined,\n ctx: Ctx,\n event: Evt,\n impl: Implementations<Ctx, Evt>,\n effectSink: Effect[],\n): Ctx {\n if (!refs || refs.length === 0) return ctx;\n const enqueue = createEnqueuer(effectSink as { type: string; payload?: unknown }[]);\n let current = ctx;\n for (const ref of refs) {\n const fn = resolveAction(ref, impl);\n const patch = fn({ context: current, event, enqueue });\n current = mergeContext(current, patch, typeof ref === \"string\" ? ref : fn.name || \"<inline>\");\n }\n return current;\n}\n\n/**\n * The transition `event` would fire from `snapshot`: the first candidate (in\n * declaration order) with no guard or a passing guard, or `undefined` (also\n * for a final state, which never reacts). Shared by `step()` and\n * `Runtime.can()` so both resolve candidates the same way.\n */\nexport function chooseTransition<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n snapshot: Snapshot<Ctx, States>,\n event: Evt,\n impl: Implementations<Ctx, Evt>,\n): TransitionDef<Ctx, Evt, States> | undefined {\n if (snapshot.status === \"final\") return undefined;\n for (const t of resolveTransitions(def, snapshot.value, event.type)) {\n if (!t.guard || evalGuard(t.guard, snapshot.context, event, impl, snapshot.value)) return t;\n }\n return undefined;\n}\n\nconst NO_EFFECTS: readonly Effect[] = Object.freeze([]);\n\n/**\n * Internal `step()` that also reports whether the chosen transition is\n * external (has a `target`, including a same-value self-target). The runtime\n * uses it to decide the sub-machine lifecycle from the SAME guard pass that\n * produced the snapshot, so a non-idempotent guard is evaluated exactly once\n * per event. Not re-exported from the package root.\n */\nexport function stepWithMeta<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n snapshot: Snapshot<Ctx, States>,\n event: Evt,\n impl: Implementations<Ctx, Evt>,\n): { result: StepResult<Ctx, States>; external: boolean } {\n // Nothing fires for a final state (inert), an undeclared event type, or\n // when every candidate's guard fails.\n const chosen = chooseTransition(def, snapshot, event, impl);\n if (!chosen) {\n return {\n result: Object.freeze({ snapshot, effects: NO_EFFECTS, changed: false }),\n external: false,\n };\n }\n // A chosen transition implies snapshot.value is a declared state.\n const state = def.states[snapshot.value];\n\n const isExternal = chosen.target !== undefined;\n const nextStateValue = (chosen.target ?? snapshot.value) as States;\n const nextState = def.states[nextStateValue];\n\n const effectSink: Effect[] = [];\n let ctx = snapshot.context;\n\n if (isExternal) {\n ctx = runActions(state.exit, ctx, event, impl, effectSink);\n }\n ctx = runActions(chosen.actions, ctx, event, impl, effectSink);\n if (isExternal && nextState) {\n ctx = runActions(nextState.entry, ctx, event, impl, effectSink);\n }\n\n const status: \"active\" | \"final\" = nextState?.final === true ? \"final\" : \"active\";\n const nextSnapshot = freezeSnapshot({\n value: nextStateValue,\n context: ctx,\n status,\n });\n\n return {\n result: Object.freeze({\n snapshot: nextSnapshot,\n effects: Object.freeze(effectSink.slice()) as readonly Effect[],\n changed: true,\n }),\n external: isExternal,\n };\n}\n\n/**\n * Compute the next snapshot and collected effects from a single event.\n *\n * Order is fixed and uninterruptible:\n * 1. resolve candidate transitions for (state, event.type)\n * 2. evaluate guards in declaration order; pick the first passing one\n * 3. if external (target defined), run exit actions of the old state\n * 4. run transition.actions in declaration order\n * 5. if external, run entry actions of the new state\n * 6. return { snapshot, effects, changed }\n *\n * The function is pure: it never dispatches effects and never mutates inputs.\n * Each guard on the path is evaluated at most once.\n */\nexport function step<Ctx, Evt extends { type: string }, States extends string>(\n def: MachineDef<Ctx, Evt, States>,\n snapshot: Snapshot<Ctx, States>,\n event: Evt,\n impl: Implementations<Ctx, Evt>,\n): StepResult<Ctx, States> {\n return stepWithMeta(def, snapshot, event, impl).result;\n}\n","import { InvalidActionResultError } from \"./lifecycle.js\";\nimport { isPlainObject } from \"./snapshot.js\";\nimport type { Action } from \"./types.js\";\n\n/**\n * Build an Action that returns a Partial<Ctx> from a pure updater.\n * The partial is merged into the current context by `step()`.\n */\nexport function assign<Ctx, Evt>(\n updater: (args: { context: Ctx; event: Evt }) => Partial<Ctx>,\n): Action<Ctx, Evt> {\n return ({ context, event }) => updater({ context, event });\n}\n\n/**\n * Merge an action's result into the current context. The function never\n * mutates either argument.\n *\n * - `undefined` / `null` patch: `current` is returned unchanged.\n * - Object context (not an array or `ArrayBuffer` view) + plain-object patch:\n * shallow merge into a new object that keeps `current`'s prototype, so a\n * class-instance context keeps its methods and untouched fields. Only own\n * enumerable (string and symbol) properties are carried; `#private` and\n * non-enumerable members are not, so prefer plain-object contexts.\n * - Object context + non-nullish primitive patch (`false`, `0`, `\"\"`, ...):\n * throws {@link InvalidActionResultError} (`actionName` names the action).\n * - Anything else (a primitive or array context, or a non-plain-object patch\n * such as an array or class instance): the patch replaces `current`.\n */\nexport function mergeContext<Ctx>(\n current: Ctx,\n patch: Partial<Ctx> | void,\n actionName = \"<inline>\",\n): Ctx {\n if (patch === undefined || patch === null) return current;\n if (\n current !== null &&\n typeof current === \"object\" &&\n !Array.isArray(current) &&\n !ArrayBuffer.isView(current)\n ) {\n // Spread (CreateDataProperty) rather than Object.assign ([[Set]]): an own\n // `__proto__` key in a JSON-derived patch stays a plain data property\n // instead of re-prototyping the merged context, and prototype setters\n // are never invoked. Object.setPrototypeOf is a no-op for plain contexts.\n if (isPlainObject(patch)) {\n return Object.setPrototypeOf({ ...current, ...patch }, Object.getPrototypeOf(current));\n }\n if (typeof patch !== \"object\" && typeof patch !== \"function\") {\n throw new InvalidActionResultError(actionName, patch);\n }\n }\n return patch as Ctx;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { step } from './chunk-
|
|
1
|
+
import { step } from './chunk-D6H64FSI.js';
|
|
2
2
|
|
|
3
3
|
// src/replay/index.ts
|
|
4
4
|
function replay(initial, events, def, impl) {
|
|
@@ -15,5 +15,5 @@ function replay(initial, events, def, impl) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export { replay };
|
|
18
|
-
//# sourceMappingURL=chunk-
|
|
19
|
-
//# sourceMappingURL=chunk-
|
|
18
|
+
//# sourceMappingURL=chunk-Q45LGXHO.js.map
|
|
19
|
+
//# sourceMappingURL=chunk-Q45LGXHO.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/replay/index.ts"],"names":[],"mappings":";;;AAiBO,SAAS,MAAA,CACd,OAAA,EACA,MAAA,EACA,GAAA,EACA,IAAA,EAC2B;AAC3B,EAAA,IAAI,QAAA,GAAW,OAAA;AACf,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,EAAK,QAAA,EAAU,OAAO,IAAI,CAAA;AACzC,IAAA,QAAA,GAAW,CAAA,CAAE,QAAA;AACb,IAAA,IAAI,CAAA,CAAE,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AACxB,MAAA,KAAA,MAAW,GAAA,IAAO,CAAA,CAAE,OAAA,EAAS,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,IAC/C;AAAA,EACF;AACA,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,EAAE,QAAA,EAAU,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,EAAG,CAAA;AAC5E","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/replay/index.ts"],"names":[],"mappings":";;;AAiBO,SAAS,MAAA,CACd,OAAA,EACA,MAAA,EACA,GAAA,EACA,IAAA,EAC2B;AAC3B,EAAA,IAAI,QAAA,GAAW,OAAA;AACf,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,EAAK,QAAA,EAAU,OAAO,IAAI,CAAA;AACzC,IAAA,QAAA,GAAW,CAAA,CAAE,QAAA;AACb,IAAA,IAAI,CAAA,CAAE,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AACxB,MAAA,KAAA,MAAW,GAAA,IAAO,CAAA,CAAE,OAAA,EAAS,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,IAC/C;AAAA,EACF;AACA,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,EAAE,QAAA,EAAU,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,EAAG,CAAA;AAC5E","file":"chunk-Q45LGXHO.js","sourcesContent":["import { step } from \"../fsm/lifecycle.js\";\nimport type { Effect, Implementations, MachineDef, Snapshot } from \"../fsm/types.js\";\n\nexport type ReplayResult<Ctx, States extends string> = Readonly<{\n snapshot: Snapshot<Ctx, States>;\n effects: readonly Effect[];\n}>;\n\n/**\n * Fold an event log into a final snapshot via `step()`. Effects are collected\n * but never dispatched — this is a pure function, suitable for PBT, time\n * travel, and incident reproduction.\n *\n * Equivalent to:\n * events.reduce((s, e) => step(def, s, e, impl).snapshot, initial)\n * but also accumulates the effects across all events.\n */\nexport function replay<Ctx, Evt extends { type: string }, States extends string>(\n initial: Snapshot<Ctx, States>,\n events: readonly Evt[],\n def: MachineDef<Ctx, Evt, States>,\n impl: Implementations<Ctx, Evt>,\n): ReplayResult<Ctx, States> {\n let snapshot = initial;\n const effects: Effect[] = [];\n for (const event of events) {\n const r = step(def, snapshot, event, impl);\n snapshot = r.snapshot;\n if (r.effects.length > 0) {\n for (const eff of r.effects) effects.push(eff);\n }\n }\n return Object.freeze({ snapshot, effects: Object.freeze(effects.slice()) });\n}\n"]}
|
|
@@ -26,9 +26,12 @@ function isAsyncGuardFn(fn) {
|
|
|
26
26
|
function isThenable(x) {
|
|
27
27
|
return x !== null && (typeof x === "object" || typeof x === "function") && typeof x.then === "function";
|
|
28
28
|
}
|
|
29
|
+
function ownValue(map, key) {
|
|
30
|
+
return map !== void 0 && Object.hasOwn(map, key) ? map[key] : void 0;
|
|
31
|
+
}
|
|
29
32
|
function resolveGuard(ref, impl) {
|
|
30
33
|
if (typeof ref === "function") return ref;
|
|
31
|
-
const fn = impl.guards
|
|
34
|
+
const fn = ownValue(impl.guards, ref);
|
|
32
35
|
if (!fn) throw new UnknownGuardError(ref);
|
|
33
36
|
return fn;
|
|
34
37
|
}
|
|
@@ -54,6 +57,7 @@ exports.UnknownGuardError = UnknownGuardError;
|
|
|
54
57
|
exports.evalGuard = evalGuard;
|
|
55
58
|
exports.isAsyncGuardFn = isAsyncGuardFn;
|
|
56
59
|
exports.isThenable = isThenable;
|
|
60
|
+
exports.ownValue = ownValue;
|
|
57
61
|
exports.resolveGuard = resolveGuard;
|
|
58
|
-
//# sourceMappingURL=chunk-
|
|
59
|
-
//# sourceMappingURL=chunk-
|
|
62
|
+
//# sourceMappingURL=chunk-QCTA2X4J.cjs.map
|
|
63
|
+
//# sourceMappingURL=chunk-QCTA2X4J.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fsm/evaluator.ts"],"names":[],"mappings":";;;AAEO,IAAM,iBAAA,GAAN,cAAgC,KAAA,CAAM;AAAA,EAClC,SAAA;AAAA,EACT,YAAY,SAAA,EAAmB;AAC7B,IAAA,KAAA,CAAM,CAAA,gBAAA,EAAmB,SAAS,CAAA,qCAAA,CAAuC,CAAA;AACzE,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAAA,EACnB;AACF;AAEO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EAChC,SAAA;AAAA,EACT,YAAY,SAAA,EAAmB;AAC7B,IAAA,KAAA;AAAA,MACE,mBAAmB,SAAS,CAAA,uGAAA;AAAA,KAC9B;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAAA,EACnB;AACF;AAcO,SAAS,eAAe,EAAA,EAAsB;AACnD,EAAA,IAAI,OAAO,EAAA,KAAO,UAAA,EAAY,OAAO,KAAA;AACrC,EAAA,OAAO,EAAA,CAAG,aAAa,IAAA,KAAS,eAAA;AAClC;AAYO,SAAS,WAAW,CAAA,EAAuC;AAChE,EAAA,OACE,CAAA,KAAM,IAAA,KACL,OAAO,CAAA,KAAM,QAAA,IAAY,OAAO,CAAA,KAAM,UAAA,CAAA,IACvC,OAAQ,CAAA,CAAyB,IAAA,KAAS,UAAA;AAE9C;AAQO,SAAS,QAAA,CACd,KACA,GAAA,EACe;AACf,EAAA,OAAO,GAAA,KAAQ,UAAa,MAAA,CAAO,MAAA,CAAO,KAAK,GAAG,CAAA,GAAI,GAAA,CAAI,GAAG,CAAA,GAAI,MAAA;AACnE;AAMO,SAAS,YAAA,CACd,KACA,IAAA,EACiB;AACjB,EAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,EAAY,OAAO,GAAA;AACtC,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,IAAA,CAAK,MAAA,EAAQ,GAAG,CAAA;AACpC,EAAA,IAAI,CAAC,EAAA,EAAI,MAAM,IAAI,kBAAkB,GAAG,CAAA;AACxC,EAAA,OAAO,EAAA;AACT;AAcO,SAAS,SAAA,CACd,GAAA,EACA,OAAA,EACA,KAAA,EACA,MACA,KAAA,EACS;AACT,EAAA,MAAM,EAAA,GAAK,YAAA,CAAa,GAAA,EAAK,IAAI,CAAA;AAGjC,EAAA,MAAM,YAAY,OAAO,GAAA,KAAQ,QAAA,GAAW,GAAA,GAAM,GAAG,IAAA,IAAQ,UAAA;AAC7D,EAAA,IAAI,cAAA,CAAe,EAAE,CAAA,EAAG;AACtB,IAAA,MAAM,IAAI,gBAAgB,SAAS,CAAA;AAAA,EACrC;AASA,EAAA,MAAM,IAAA,GAAa,EAAE,OAAA,EAAS,KAAA,EAAM;AACpC,EAAA,MAAM,YAAY,IAAA,CAAK,MAAA;AACvB,EAAA,IAAI,SAAA,OAAgB,MAAA,GAAS,SAAA;AAC7B,EAAA,IAAI,KAAA,KAAU,MAAA,EAAW,IAAA,CAAK,KAAA,GAAQ,KAAA;AACtC,EAAA,MAAM,MAAA,GAAS,GAAG,IAAI,CAAA;AAKtB,EAAA,IAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AACtB,IAAA,MAAM,IAAI,gBAAgB,SAAS,CAAA;AAAA,EACrC;AACA,EAAA,OAAO,MAAA;AACT","file":"chunk-QCTA2X4J.cjs","sourcesContent":["import type { Guard, GuardRef, Implementations } from \"./types.js\";\n\nexport class UnknownGuardError extends Error {\n readonly guardName: string;\n constructor(guardName: string) {\n super(`aifsmjs: guard \"${guardName}\" not found in implementations.guards`);\n this.name = \"UnknownGuardError\";\n this.guardName = guardName;\n }\n}\n\nexport class AsyncGuardError extends Error {\n readonly guardName: string;\n constructor(guardName: string) {\n super(\n `aifsmjs: guard \"${guardName}\" must be sync; received a Promise. Async guards break determinism and replay. Move I/O into an effect.`,\n );\n this.name = \"AsyncGuardError\";\n this.guardName = guardName;\n }\n}\n\n/**\n * Detect declared-async guards at definition time. Catches the common case of\n * `async (args) => ...` inline guards. Combinator builders or arrow returns of\n * a Promise still slip past — those are caught at `evalGuard` runtime via\n * the `isThenable` check.\n *\n * Caveat: this relies on `Function.prototype.constructor.name === \"AsyncFunction\"`,\n * which is reliable in ES2017+ runtimes. If your bundler transpiles `async`\n * to generator-based code (e.g. ES5 / very old TypeScript targets), this\n * check returns `false` for those forms — the runtime `evalGuard` thenable\n * check still catches them.\n */\nexport function isAsyncGuardFn(fn: unknown): boolean {\n if (typeof fn !== \"function\") return false;\n return fn.constructor?.name === \"AsyncFunction\";\n}\n\n/**\n * Detect a thenable (PromiseLike) — anything with a callable `then`. Used in\n * place of `instanceof Promise` so cross-realm Promises (iframe / worker /\n * vm context) and user-defined thenables are also rejected.\n *\n * Exported for the guard combinators (`and`/`or`/`not`), which must apply the\n * same async-guard rejection to the values their inner guards return —\n * otherwise a nested thenable is coerced truthy inside the combinator and the\n * top-level `evalGuard` check never sees it (FSM-S-01).\n */\nexport function isThenable(x: unknown): x is PromiseLike<unknown> {\n return (\n x !== null &&\n (typeof x === \"object\" || typeof x === \"function\") &&\n typeof (x as { then?: unknown }).then === \"function\"\n );\n}\n\n/**\n * Own-property lookup for user-keyed maps (`on`, `guards`, `actions`,\n * `effects`). A plain `map[key]` read resolves inherited `Object.prototype`\n * members, so an undeclared event type or ref such as `\"constructor\"` or\n * `\"toString\"` would act as a declared transition / implementation.\n */\nexport function ownValue<V>(\n map: Readonly<Record<string, V>> | undefined,\n key: string,\n): V | undefined {\n return map !== undefined && Object.hasOwn(map, key) ? map[key] : undefined;\n}\n\n/**\n * Resolve a guard ref to a Guard function. String refs are looked up in the\n * implementations map; inline functions are returned as-is.\n */\nexport function resolveGuard<Ctx, Evt>(\n ref: GuardRef<Ctx, Evt>,\n impl: Implementations<Ctx, Evt>,\n): Guard<Ctx, Evt> {\n if (typeof ref === \"function\") return ref;\n const fn = ownValue(impl.guards, ref);\n if (!fn) throw new UnknownGuardError(ref);\n return fn;\n}\n\n/**\n * Evaluate a guard ref against (context, event). Guards must be sync and pure;\n * TypeScript blocks declared-async signatures at compile time, but JS callers\n * or casts can still slip through. This function checks two ways:\n * 1. Inline AsyncFunction (declared `async`) → throw AsyncGuardError.\n * 2. Return value is a Promise → throw AsyncGuardError.\n * Both throws are user errors; they would otherwise silently pass the guard\n * (Promise is truthy) and break determinism.\n *\n * The optional `value` argument is the current state value, threaded so the\n * `stateIn` combinator and similar predicates can introspect it.\n */\nexport function evalGuard<Ctx, Evt>(\n ref: GuardRef<Ctx, Evt>,\n context: Ctx,\n event: Evt,\n impl: Implementations<Ctx, Evt>,\n value?: string,\n): boolean {\n const fn = resolveGuard(ref, impl);\n // Function.prototype.name is \"\" for anonymous arrows — use `||` not `??`\n // so the empty string falls back to \"<inline>\" for readable error messages.\n const guardName = typeof ref === \"string\" ? ref : fn.name || \"<inline>\";\n if (isAsyncGuardFn(fn)) {\n throw new AsyncGuardError(guardName);\n }\n // Build args while honouring exactOptionalPropertyTypes: omit fields that\n // would otherwise be assigned `undefined`.\n type Args = {\n context: Ctx;\n event: Evt;\n guards?: Readonly<Record<string, Guard<Ctx, Evt>>>;\n value?: string;\n };\n const args: Args = { context, event };\n const guardsMap = impl.guards;\n if (guardsMap) args.guards = guardsMap;\n if (value !== undefined) args.value = value;\n const result = fn(args);\n // TS narrows `result` to boolean from Guard's return type, but a JS caller\n // or a cast can slip a Promise / PromiseLike through. We accept anything\n // thenable (native Promise, cross-realm Promise, user-defined thenable),\n // not just same-realm `instanceof Promise`.\n if (isThenable(result)) {\n throw new AsyncGuardError(guardName);\n }\n return result;\n}\n"]}
|
|
@@ -24,9 +24,12 @@ function isAsyncGuardFn(fn) {
|
|
|
24
24
|
function isThenable(x) {
|
|
25
25
|
return x !== null && (typeof x === "object" || typeof x === "function") && typeof x.then === "function";
|
|
26
26
|
}
|
|
27
|
+
function ownValue(map, key) {
|
|
28
|
+
return map !== void 0 && Object.hasOwn(map, key) ? map[key] : void 0;
|
|
29
|
+
}
|
|
27
30
|
function resolveGuard(ref, impl) {
|
|
28
31
|
if (typeof ref === "function") return ref;
|
|
29
|
-
const fn = impl.guards
|
|
32
|
+
const fn = ownValue(impl.guards, ref);
|
|
30
33
|
if (!fn) throw new UnknownGuardError(ref);
|
|
31
34
|
return fn;
|
|
32
35
|
}
|
|
@@ -47,6 +50,6 @@ function evalGuard(ref, context, event, impl, value) {
|
|
|
47
50
|
return result;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
export { AsyncGuardError, UnknownGuardError, evalGuard, isAsyncGuardFn, isThenable, resolveGuard };
|
|
51
|
-
//# sourceMappingURL=chunk-
|
|
52
|
-
//# sourceMappingURL=chunk-
|
|
53
|
+
export { AsyncGuardError, UnknownGuardError, evalGuard, isAsyncGuardFn, isThenable, ownValue, resolveGuard };
|
|
54
|
+
//# sourceMappingURL=chunk-SSNKGEVB.js.map
|
|
55
|
+
//# sourceMappingURL=chunk-SSNKGEVB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fsm/evaluator.ts"],"names":[],"mappings":";AAEO,IAAM,iBAAA,GAAN,cAAgC,KAAA,CAAM;AAAA,EAClC,SAAA;AAAA,EACT,YAAY,SAAA,EAAmB;AAC7B,IAAA,KAAA,CAAM,CAAA,gBAAA,EAAmB,SAAS,CAAA,qCAAA,CAAuC,CAAA;AACzE,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAAA,EACnB;AACF;AAEO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EAChC,SAAA;AAAA,EACT,YAAY,SAAA,EAAmB;AAC7B,IAAA,KAAA;AAAA,MACE,mBAAmB,SAAS,CAAA,uGAAA;AAAA,KAC9B;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAAA,EACnB;AACF;AAcO,SAAS,eAAe,EAAA,EAAsB;AACnD,EAAA,IAAI,OAAO,EAAA,KAAO,UAAA,EAAY,OAAO,KAAA;AACrC,EAAA,OAAO,EAAA,CAAG,aAAa,IAAA,KAAS,eAAA;AAClC;AAYO,SAAS,WAAW,CAAA,EAAuC;AAChE,EAAA,OACE,CAAA,KAAM,IAAA,KACL,OAAO,CAAA,KAAM,QAAA,IAAY,OAAO,CAAA,KAAM,UAAA,CAAA,IACvC,OAAQ,CAAA,CAAyB,IAAA,KAAS,UAAA;AAE9C;AAQO,SAAS,QAAA,CACd,KACA,GAAA,EACe;AACf,EAAA,OAAO,GAAA,KAAQ,UAAa,MAAA,CAAO,MAAA,CAAO,KAAK,GAAG,CAAA,GAAI,GAAA,CAAI,GAAG,CAAA,GAAI,MAAA;AACnE;AAMO,SAAS,YAAA,CACd,KACA,IAAA,EACiB;AACjB,EAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,EAAY,OAAO,GAAA;AACtC,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,IAAA,CAAK,MAAA,EAAQ,GAAG,CAAA;AACpC,EAAA,IAAI,CAAC,EAAA,EAAI,MAAM,IAAI,kBAAkB,GAAG,CAAA;AACxC,EAAA,OAAO,EAAA;AACT;AAcO,SAAS,SAAA,CACd,GAAA,EACA,OAAA,EACA,KAAA,EACA,MACA,KAAA,EACS;AACT,EAAA,MAAM,EAAA,GAAK,YAAA,CAAa,GAAA,EAAK,IAAI,CAAA;AAGjC,EAAA,MAAM,YAAY,OAAO,GAAA,KAAQ,QAAA,GAAW,GAAA,GAAM,GAAG,IAAA,IAAQ,UAAA;AAC7D,EAAA,IAAI,cAAA,CAAe,EAAE,CAAA,EAAG;AACtB,IAAA,MAAM,IAAI,gBAAgB,SAAS,CAAA;AAAA,EACrC;AASA,EAAA,MAAM,IAAA,GAAa,EAAE,OAAA,EAAS,KAAA,EAAM;AACpC,EAAA,MAAM,YAAY,IAAA,CAAK,MAAA;AACvB,EAAA,IAAI,SAAA,OAAgB,MAAA,GAAS,SAAA;AAC7B,EAAA,IAAI,KAAA,KAAU,MAAA,EAAW,IAAA,CAAK,KAAA,GAAQ,KAAA;AACtC,EAAA,MAAM,MAAA,GAAS,GAAG,IAAI,CAAA;AAKtB,EAAA,IAAI,UAAA,CAAW,MAAM,CAAA,EAAG;AACtB,IAAA,MAAM,IAAI,gBAAgB,SAAS,CAAA;AAAA,EACrC;AACA,EAAA,OAAO,MAAA;AACT","file":"chunk-SSNKGEVB.js","sourcesContent":["import type { Guard, GuardRef, Implementations } from \"./types.js\";\n\nexport class UnknownGuardError extends Error {\n readonly guardName: string;\n constructor(guardName: string) {\n super(`aifsmjs: guard \"${guardName}\" not found in implementations.guards`);\n this.name = \"UnknownGuardError\";\n this.guardName = guardName;\n }\n}\n\nexport class AsyncGuardError extends Error {\n readonly guardName: string;\n constructor(guardName: string) {\n super(\n `aifsmjs: guard \"${guardName}\" must be sync; received a Promise. Async guards break determinism and replay. Move I/O into an effect.`,\n );\n this.name = \"AsyncGuardError\";\n this.guardName = guardName;\n }\n}\n\n/**\n * Detect declared-async guards at definition time. Catches the common case of\n * `async (args) => ...` inline guards. Combinator builders or arrow returns of\n * a Promise still slip past — those are caught at `evalGuard` runtime via\n * the `isThenable` check.\n *\n * Caveat: this relies on `Function.prototype.constructor.name === \"AsyncFunction\"`,\n * which is reliable in ES2017+ runtimes. If your bundler transpiles `async`\n * to generator-based code (e.g. ES5 / very old TypeScript targets), this\n * check returns `false` for those forms — the runtime `evalGuard` thenable\n * check still catches them.\n */\nexport function isAsyncGuardFn(fn: unknown): boolean {\n if (typeof fn !== \"function\") return false;\n return fn.constructor?.name === \"AsyncFunction\";\n}\n\n/**\n * Detect a thenable (PromiseLike) — anything with a callable `then`. Used in\n * place of `instanceof Promise` so cross-realm Promises (iframe / worker /\n * vm context) and user-defined thenables are also rejected.\n *\n * Exported for the guard combinators (`and`/`or`/`not`), which must apply the\n * same async-guard rejection to the values their inner guards return —\n * otherwise a nested thenable is coerced truthy inside the combinator and the\n * top-level `evalGuard` check never sees it (FSM-S-01).\n */\nexport function isThenable(x: unknown): x is PromiseLike<unknown> {\n return (\n x !== null &&\n (typeof x === \"object\" || typeof x === \"function\") &&\n typeof (x as { then?: unknown }).then === \"function\"\n );\n}\n\n/**\n * Own-property lookup for user-keyed maps (`on`, `guards`, `actions`,\n * `effects`). A plain `map[key]` read resolves inherited `Object.prototype`\n * members, so an undeclared event type or ref such as `\"constructor\"` or\n * `\"toString\"` would act as a declared transition / implementation.\n */\nexport function ownValue<V>(\n map: Readonly<Record<string, V>> | undefined,\n key: string,\n): V | undefined {\n return map !== undefined && Object.hasOwn(map, key) ? map[key] : undefined;\n}\n\n/**\n * Resolve a guard ref to a Guard function. String refs are looked up in the\n * implementations map; inline functions are returned as-is.\n */\nexport function resolveGuard<Ctx, Evt>(\n ref: GuardRef<Ctx, Evt>,\n impl: Implementations<Ctx, Evt>,\n): Guard<Ctx, Evt> {\n if (typeof ref === \"function\") return ref;\n const fn = ownValue(impl.guards, ref);\n if (!fn) throw new UnknownGuardError(ref);\n return fn;\n}\n\n/**\n * Evaluate a guard ref against (context, event). Guards must be sync and pure;\n * TypeScript blocks declared-async signatures at compile time, but JS callers\n * or casts can still slip through. This function checks two ways:\n * 1. Inline AsyncFunction (declared `async`) → throw AsyncGuardError.\n * 2. Return value is a Promise → throw AsyncGuardError.\n * Both throws are user errors; they would otherwise silently pass the guard\n * (Promise is truthy) and break determinism.\n *\n * The optional `value` argument is the current state value, threaded so the\n * `stateIn` combinator and similar predicates can introspect it.\n */\nexport function evalGuard<Ctx, Evt>(\n ref: GuardRef<Ctx, Evt>,\n context: Ctx,\n event: Evt,\n impl: Implementations<Ctx, Evt>,\n value?: string,\n): boolean {\n const fn = resolveGuard(ref, impl);\n // Function.prototype.name is \"\" for anonymous arrows — use `||` not `??`\n // so the empty string falls back to \"<inline>\" for readable error messages.\n const guardName = typeof ref === \"string\" ? ref : fn.name || \"<inline>\";\n if (isAsyncGuardFn(fn)) {\n throw new AsyncGuardError(guardName);\n }\n // Build args while honouring exactOptionalPropertyTypes: omit fields that\n // would otherwise be assigned `undefined`.\n type Args = {\n context: Ctx;\n event: Evt;\n guards?: Readonly<Record<string, Guard<Ctx, Evt>>>;\n value?: string;\n };\n const args: Args = { context, event };\n const guardsMap = impl.guards;\n if (guardsMap) args.guards = guardsMap;\n if (value !== undefined) args.value = value;\n const result = fn(args);\n // TS narrows `result` to boolean from Guard's return type, but a JS caller\n // or a cast can slip a Promise / PromiseLike through. We accept anything\n // thenable (native Promise, cross-realm Promise, user-defined thenable),\n // not just same-realm `instanceof Promise`.\n if (isThenable(result)) {\n throw new AsyncGuardError(guardName);\n }\n return result;\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunk3B2USJ3H_cjs = require('./chunk-3B2USJ3H.cjs');
|
|
4
|
-
var
|
|
4
|
+
var chunkQCTA2X4J_cjs = require('./chunk-QCTA2X4J.cjs');
|
|
5
5
|
|
|
6
6
|
// src/fsm/resolver.ts
|
|
7
7
|
function normalizeTransition(entry) {
|
|
@@ -17,19 +17,26 @@ function normalizeTransitions(entry) {
|
|
|
17
17
|
function resolveTransitions(def, stateValue, eventType) {
|
|
18
18
|
const state = def.states[stateValue];
|
|
19
19
|
if (!state || !state.on) return [];
|
|
20
|
-
return normalizeTransitions(state.on
|
|
20
|
+
return normalizeTransitions(chunkQCTA2X4J_cjs.ownValue(state.on, eventType));
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// src/fsm/snapshot.ts
|
|
24
|
-
|
|
24
|
+
exports.IS_DEV = false;
|
|
25
|
+
try {
|
|
26
|
+
exports.IS_DEV = process.env.NODE_ENV !== "production";
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
25
29
|
function isPlainObject(value) {
|
|
26
30
|
if (value === null || typeof value !== "object") return false;
|
|
27
31
|
const proto = Object.getPrototypeOf(value);
|
|
28
32
|
return proto === Object.prototype || proto === null;
|
|
29
33
|
}
|
|
34
|
+
var DEEP_FROZEN = /* @__PURE__ */ new WeakSet();
|
|
30
35
|
function deepFreeze(value) {
|
|
31
36
|
if (value === null || typeof value !== "object") return value;
|
|
32
|
-
if (
|
|
37
|
+
if (DEEP_FROZEN.has(value)) return value;
|
|
38
|
+
DEEP_FROZEN.add(value);
|
|
39
|
+
if (ArrayBuffer.isView(value)) return value;
|
|
33
40
|
Object.freeze(value);
|
|
34
41
|
if (Array.isArray(value)) {
|
|
35
42
|
for (const item of value) deepFreeze(item);
|
|
@@ -41,7 +48,7 @@ function deepFreeze(value) {
|
|
|
41
48
|
return value;
|
|
42
49
|
}
|
|
43
50
|
function freezeSnapshot(snap) {
|
|
44
|
-
return IS_DEV ? deepFreeze(snap) : Object.freeze(snap);
|
|
51
|
+
return exports.IS_DEV ? deepFreeze(snap) : Object.freeze(snap);
|
|
45
52
|
}
|
|
46
53
|
function createSnapshot(args) {
|
|
47
54
|
return freezeSnapshot({
|
|
@@ -51,23 +58,6 @@ function createSnapshot(args) {
|
|
|
51
58
|
});
|
|
52
59
|
}
|
|
53
60
|
|
|
54
|
-
// src/fsm/updater.ts
|
|
55
|
-
function isPlainRecord(value) {
|
|
56
|
-
if (value === null || typeof value !== "object") return false;
|
|
57
|
-
const proto = Object.getPrototypeOf(value);
|
|
58
|
-
return proto === Object.prototype || proto === null;
|
|
59
|
-
}
|
|
60
|
-
function assign(updater) {
|
|
61
|
-
return ({ context, event }) => updater({ context, event });
|
|
62
|
-
}
|
|
63
|
-
function mergeContext(current, patch) {
|
|
64
|
-
if (patch === void 0 || patch === null) return current;
|
|
65
|
-
if (isPlainRecord(current) && isPlainRecord(patch)) {
|
|
66
|
-
return { ...current, ...patch };
|
|
67
|
-
}
|
|
68
|
-
return patch;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
61
|
// src/fsm/lifecycle.ts
|
|
72
62
|
var UnknownActionError = class extends Error {
|
|
73
63
|
actionName;
|
|
@@ -77,9 +67,19 @@ var UnknownActionError = class extends Error {
|
|
|
77
67
|
this.actionName = actionName;
|
|
78
68
|
}
|
|
79
69
|
};
|
|
70
|
+
var InvalidActionResultError = class extends Error {
|
|
71
|
+
actionName;
|
|
72
|
+
constructor(actionName, patch) {
|
|
73
|
+
super(
|
|
74
|
+
`aifsmjs: action "${actionName}" returned ${typeof patch}; an object context accepts only a plain-object patch (or undefined)`
|
|
75
|
+
);
|
|
76
|
+
this.name = "InvalidActionResultError";
|
|
77
|
+
this.actionName = actionName;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
80
|
function resolveAction(ref, impl) {
|
|
81
81
|
if (typeof ref === "function") return ref;
|
|
82
|
-
const fn = impl.actions
|
|
82
|
+
const fn = chunkQCTA2X4J_cjs.ownValue(impl.actions, ref);
|
|
83
83
|
if (!fn) throw new UnknownActionError(ref);
|
|
84
84
|
return fn;
|
|
85
85
|
}
|
|
@@ -90,30 +90,27 @@ function runActions(refs, ctx, event, impl, effectSink) {
|
|
|
90
90
|
for (const ref of refs) {
|
|
91
91
|
const fn = resolveAction(ref, impl);
|
|
92
92
|
const patch = fn({ context: current, event, enqueue });
|
|
93
|
-
current = mergeContext(current, patch);
|
|
93
|
+
current = mergeContext(current, patch, typeof ref === "string" ? ref : fn.name || "<inline>");
|
|
94
94
|
}
|
|
95
95
|
return current;
|
|
96
96
|
}
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
97
|
+
function chooseTransition(def, snapshot, event, impl) {
|
|
98
|
+
if (snapshot.status === "final") return void 0;
|
|
99
|
+
for (const t of resolveTransitions(def, snapshot.value, event.type)) {
|
|
100
|
+
if (!t.guard || chunkQCTA2X4J_cjs.evalGuard(t.guard, snapshot.context, event, impl, snapshot.value)) return t;
|
|
101
101
|
}
|
|
102
102
|
return void 0;
|
|
103
103
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
const state = def.states[snapshot.value];
|
|
109
|
-
if (!state) {
|
|
110
|
-
return Object.freeze({ snapshot, effects: [], changed: false });
|
|
111
|
-
}
|
|
112
|
-
const candidateList = normalizeTransitions(state.on?.[event.type]);
|
|
113
|
-
const chosen = pickTransition(candidateList, snapshot.context, event, impl, snapshot.value);
|
|
104
|
+
var NO_EFFECTS = Object.freeze([]);
|
|
105
|
+
function stepWithMeta(def, snapshot, event, impl) {
|
|
106
|
+
const chosen = chooseTransition(def, snapshot, event, impl);
|
|
114
107
|
if (!chosen) {
|
|
115
|
-
return
|
|
108
|
+
return {
|
|
109
|
+
result: Object.freeze({ snapshot, effects: NO_EFFECTS, changed: false }),
|
|
110
|
+
external: false
|
|
111
|
+
};
|
|
116
112
|
}
|
|
113
|
+
const state = def.states[snapshot.value];
|
|
117
114
|
const isExternal = chosen.target !== void 0;
|
|
118
115
|
const nextStateValue = chosen.target ?? snapshot.value;
|
|
119
116
|
const nextState = def.states[nextStateValue];
|
|
@@ -132,15 +129,40 @@ function step(def, snapshot, event, impl) {
|
|
|
132
129
|
context: ctx,
|
|
133
130
|
status
|
|
134
131
|
});
|
|
135
|
-
return
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
return {
|
|
133
|
+
result: Object.freeze({
|
|
134
|
+
snapshot: nextSnapshot,
|
|
135
|
+
effects: Object.freeze(effectSink.slice()),
|
|
136
|
+
changed: true
|
|
137
|
+
}),
|
|
138
|
+
external: isExternal
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function step(def, snapshot, event, impl) {
|
|
142
|
+
return stepWithMeta(def, snapshot, event, impl).result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/fsm/updater.ts
|
|
146
|
+
function assign(updater) {
|
|
147
|
+
return ({ context, event }) => updater({ context, event });
|
|
148
|
+
}
|
|
149
|
+
function mergeContext(current, patch, actionName = "<inline>") {
|
|
150
|
+
if (patch === void 0 || patch === null) return current;
|
|
151
|
+
if (current !== null && typeof current === "object" && !Array.isArray(current) && !ArrayBuffer.isView(current)) {
|
|
152
|
+
if (isPlainObject(patch)) {
|
|
153
|
+
return Object.setPrototypeOf({ ...current, ...patch }, Object.getPrototypeOf(current));
|
|
154
|
+
}
|
|
155
|
+
if (typeof patch !== "object" && typeof patch !== "function") {
|
|
156
|
+
throw new InvalidActionResultError(actionName, patch);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return patch;
|
|
140
160
|
}
|
|
141
161
|
|
|
162
|
+
exports.InvalidActionResultError = InvalidActionResultError;
|
|
142
163
|
exports.UnknownActionError = UnknownActionError;
|
|
143
164
|
exports.assign = assign;
|
|
165
|
+
exports.chooseTransition = chooseTransition;
|
|
144
166
|
exports.createSnapshot = createSnapshot;
|
|
145
167
|
exports.deepFreeze = deepFreeze;
|
|
146
168
|
exports.freezeSnapshot = freezeSnapshot;
|
|
@@ -149,5 +171,6 @@ exports.normalizeTransition = normalizeTransition;
|
|
|
149
171
|
exports.normalizeTransitions = normalizeTransitions;
|
|
150
172
|
exports.resolveTransitions = resolveTransitions;
|
|
151
173
|
exports.step = step;
|
|
152
|
-
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
174
|
+
exports.stepWithMeta = stepWithMeta;
|
|
175
|
+
//# sourceMappingURL=chunk-VGLF5NQH.cjs.map
|
|
176
|
+
//# sourceMappingURL=chunk-VGLF5NQH.cjs.map
|