aifsmjs 0.3.1 → 0.4.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 +5 -5
- package/README_ZHTW.md +5 -5
- package/STABILITY.md +25 -15
- package/llms-full.txt +33 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ The three layers are fully decoupled: take `step()` alone for replay, take `Mach
|
|
|
107
107
|
| Will do (v1) | Won't do |
|
|
108
108
|
| --------------------------------------------------- | ------------------------------------------------- |
|
|
109
109
|
| Flat states + transitions | Parallel state regions |
|
|
110
|
-
| Hierarchical sugar via `state.sub` (
|
|
110
|
+
| Hierarchical sugar via `state.sub` (stable since 0.4.0) | Closures embedded in definition (breaks serialize) |
|
|
111
111
|
| Guards (sync only; inline async throws `InvalidDefinitionError` at `defineMachine`; runtime throws `AsyncGuardError` on thenable return) | Async guards |
|
|
112
112
|
| Actions (assign + enqueue effects) | Async API inside an action (use an effect) |
|
|
113
113
|
| Fire-and-forget effects | Actor invocation / spawn |
|
|
@@ -357,7 +357,7 @@ Non-goals:
|
|
|
357
357
|
- No async lifecycle hook
|
|
358
358
|
- Inspect middleware cannot alter the transition outcome
|
|
359
359
|
|
|
360
|
-
### Sub-machine lifecycle (since 0.
|
|
360
|
+
### Sub-machine lifecycle (stable since 0.4.0)
|
|
361
361
|
|
|
362
362
|
When a state declares `sub`, the per-transition ordering is:
|
|
363
363
|
|
|
@@ -497,8 +497,8 @@ Example-first, PBT-augmented. Lesson from jssm: "3000+ tests / 100% coverage" tu
|
|
|
497
497
|
| ------- | ------------------------------------------------------------------ |
|
|
498
498
|
| v0.1 | core + guards + effects + inspect + replay + pbt (this release) |
|
|
499
499
|
| v0.2 | Async-guard detection, coverage tuning, llms-full.txt verify gate |
|
|
500
|
-
| v0.3 | Hierarchical sugar via `state.sub` (experimental
|
|
501
|
-
| v0.4 |
|
|
500
|
+
| v0.3 | Hierarchical sugar via `state.sub` (experimental) |
|
|
501
|
+
| v0.4 | Sub-machine API promoted to stable; dependency-reduction cycle |
|
|
502
502
|
| v0.5 | `aifsmjs-bridge-bitecs` / `aifsmjs-bridge-pixi` (separate sub-packages) |
|
|
503
503
|
| v1.0 | API freeze and stability guarantee |
|
|
504
504
|
|
|
@@ -509,7 +509,7 @@ Example-first, PBT-augmented. Lesson from jssm: "3000+ tests / 100% coverage" tu
|
|
|
509
509
|
- **Tick / game-loop hook** (out of scope for v1)
|
|
510
510
|
- **ECS / Pixi bridges** (out of scope for v1)
|
|
511
511
|
|
|
512
|
-
**
|
|
512
|
+
**Future candidate**: `historyState` — remember last active sub-state on
|
|
513
513
|
re-entry. Workaround today: snapshot via `onTransition` and restore
|
|
514
514
|
manually.
|
|
515
515
|
|
package/README_ZHTW.md
CHANGED
|
@@ -106,7 +106,7 @@ console.log(runtime.getSnapshot().context); // { ticks: 1 }
|
|
|
106
106
|
| 會做(v1) | 不會做 |
|
|
107
107
|
| --------------------------------------------------- | ------------------------------------------------- |
|
|
108
108
|
| Flat states + transitions | Parallel state regions |
|
|
109
|
-
| 透過 `state.sub` 的階層式 sugar(
|
|
109
|
+
| 透過 `state.sub` 的階層式 sugar(stable since 0.4.0) | Definition 內直接綁 closure(會無法序列化) |
|
|
110
110
|
| Guards(sync only;inline async 在 `defineMachine` 時丟 `InvalidDefinitionError`;runtime 偵測到 thenable 回傳則丟 `AsyncGuardError`) | Async guards |
|
|
111
111
|
| Actions(assign + enqueue effects) | 在 action 內呼叫 async API(請放到 effect) |
|
|
112
112
|
| Fire-and-forget effects | Actor invocation / spawn |
|
|
@@ -356,7 +356,7 @@ sched.cancelAll();
|
|
|
356
356
|
- async lifecycle hook
|
|
357
357
|
- Inspect middleware 影響 transition 結果
|
|
358
358
|
|
|
359
|
-
### Sub-machine lifecycle(since 0.
|
|
359
|
+
### Sub-machine lifecycle(stable since 0.4.0)
|
|
360
360
|
|
|
361
361
|
當一個 state 宣告 `sub` 時,每次 transition 的執行順序為:
|
|
362
362
|
|
|
@@ -491,8 +491,8 @@ aifsmjs 在幾個常見議題上做了刻意取捨,跟主流 FSM library 寫
|
|
|
491
491
|
| ---- | ----------------------------------------------------------------- |
|
|
492
492
|
| v0.1 | core + guards + effects + inspect + replay + pbt(本次發佈) |
|
|
493
493
|
| v0.2 | Async-guard 偵測、coverage 調整、llms-full.txt verify gate |
|
|
494
|
-
| v0.3 | 透過 `state.sub` 的階層式 sugar(experimental
|
|
495
|
-
| v0.4 |
|
|
494
|
+
| v0.3 | 透過 `state.sub` 的階層式 sugar(experimental) |
|
|
495
|
+
| v0.4 | sub-machine API 升 stable;降依賴 cycle |
|
|
496
496
|
| v0.5 | `aifsmjs-bridge-bitecs` / `aifsmjs-bridge-pixi`(獨立 sub-package) |
|
|
497
497
|
| v1.0 | API freeze 與 stability guarantee |
|
|
498
498
|
|
|
@@ -503,7 +503,7 @@ aifsmjs 在幾個常見議題上做了刻意取捨,跟主流 FSM library 寫
|
|
|
503
503
|
- **Tick / game-loop hook**(v1 不做)
|
|
504
504
|
- **ECS / Pixi bridges**(v1 不做)
|
|
505
505
|
|
|
506
|
-
|
|
506
|
+
**未來候選**:`historyState` — re-entry 時自動恢復上一次的 sub-state。0.3.0 的暫代方案:透過 `onTransition` snapshot sub-runtime 的 value,之後手動還原。
|
|
507
507
|
|
|
508
508
|
---
|
|
509
509
|
|
package/STABILITY.md
CHANGED
|
@@ -27,10 +27,13 @@ Fully stable. Breaking changes only at a major version bump (1.0+).
|
|
|
27
27
|
`on('transition', ...)` API; listed under Stable because the underlying
|
|
28
28
|
contract is unchanged.
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
### Sub-machines (stable since 0.4.0)
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
The hierarchical sub-machine surface shipped experimentally in 0.3.0 is
|
|
33
|
+
stable as of 0.4.0. Signatures and runtime semantics — including the
|
|
34
|
+
init-failure quarantine behaviour described below — are frozen for the 1.x
|
|
35
|
+
line. The boundaries listed are intentional design trade-offs, not bugs or
|
|
36
|
+
pending instability.
|
|
34
37
|
|
|
35
38
|
- `StateDef.sub` (optional `SubMachineDef`) — when present, a child runtime
|
|
36
39
|
is lazily initialised on entry and disposed on exit. Per-transition
|
|
@@ -45,7 +48,7 @@ is OK; expect a one-line patch on minor upgrades.
|
|
|
45
48
|
failure. Fields: `parentState`, `phase ("init" | "dispose")`, `cause`.
|
|
46
49
|
- `SubMachineDef` type alias.
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
#### Design boundaries
|
|
49
52
|
|
|
50
53
|
- **Replay / PBT do not see child state.** `replay()` and
|
|
51
54
|
`commandsFromMachine` only inspect parent snapshots. If your business
|
|
@@ -54,24 +57,31 @@ is OK; expect a one-line patch on minor upgrades.
|
|
|
54
57
|
disposed it. The handle is not reinitialised until the parent leaves and
|
|
55
58
|
re-enters the sub-bearing state. Detect with `child.disposed`.
|
|
56
59
|
- **Self-targeting external (`A → A`) is treated as full exit/entry**:
|
|
57
|
-
child is disposed and reinitialised. The
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
trigger a reinit.
|
|
60
|
+
child is disposed and reinitialised. The dispatcher re-resolves guards to
|
|
61
|
+
identify the chosen transition before deciding external vs internal, so
|
|
62
|
+
guarded internal transitions on the same event do not trigger a reinit.
|
|
61
63
|
- **Init-failure mid-transition leaves the parent without a live child.**
|
|
62
64
|
If `applySubLifecycle` successfully disposes the old child and then the
|
|
63
65
|
new child's `createRuntime` throws, the parent's snapshot is rolled back
|
|
64
66
|
to `prev` but `subRuntime()` returns `undefined`. Callers catching
|
|
65
67
|
`SubMachineError(phase: "init")` should treat the runtime as quarantined
|
|
66
68
|
— call `runtime.dispose()` (idempotent) or `runtime.reset()` (which
|
|
67
|
-
attempts re-init) before sending further events.
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
attempts re-init) before sending further events. This quarantine
|
|
70
|
+
behaviour is part of the stable contract; a future **major** version may
|
|
71
|
+
switch to a two-phase "init before dispose" commit strategy (a breaking
|
|
72
|
+
change reserved for 1.0+), but the current semantics are frozen for the
|
|
73
|
+
1.x line.
|
|
70
74
|
|
|
71
|
-
##
|
|
75
|
+
## Experimental
|
|
76
|
+
|
|
77
|
+
No experimental APIs as of 0.4.0. The 0.3.0 sub-machine surface graduated to
|
|
78
|
+
Stable in 0.4.0 — see "Sub-machines" above.
|
|
79
|
+
|
|
80
|
+
## Draft (planned, not implemented)
|
|
72
81
|
|
|
73
82
|
API sketched, not shipped. May change before release.
|
|
74
83
|
|
|
75
|
-
- `historyState` (
|
|
76
|
-
the last active sub-state on re-entry. Workaround in 0.3.0:
|
|
77
|
-
the sub-runtime's value on exit via `onTransition`, restore
|
|
84
|
+
- `historyState` (candidate for a future minor) — opt-in pseudo-state that
|
|
85
|
+
remembers the last active sub-state on re-entry. Workaround in 0.3.0:
|
|
86
|
+
snapshot the sub-runtime's value on exit via `onTransition`, restore
|
|
87
|
+
manually.
|
package/llms-full.txt
CHANGED
|
@@ -120,7 +120,7 @@ The three layers are fully decoupled: take `step()` alone for replay, take `Mach
|
|
|
120
120
|
| Will do (v1) | Won't do |
|
|
121
121
|
| --------------------------------------------------- | ------------------------------------------------- |
|
|
122
122
|
| Flat states + transitions | Parallel state regions |
|
|
123
|
-
| Hierarchical sugar via `state.sub` (
|
|
123
|
+
| Hierarchical sugar via `state.sub` (stable since 0.4.0) | Closures embedded in definition (breaks serialize) |
|
|
124
124
|
| Guards (sync only; inline async throws `InvalidDefinitionError` at `defineMachine`; runtime throws `AsyncGuardError` on thenable return) | Async guards |
|
|
125
125
|
| Actions (assign + enqueue effects) | Async API inside an action (use an effect) |
|
|
126
126
|
| Fire-and-forget effects | Actor invocation / spawn |
|
|
@@ -370,7 +370,7 @@ Non-goals:
|
|
|
370
370
|
- No async lifecycle hook
|
|
371
371
|
- Inspect middleware cannot alter the transition outcome
|
|
372
372
|
|
|
373
|
-
### Sub-machine lifecycle (since 0.
|
|
373
|
+
### Sub-machine lifecycle (stable since 0.4.0)
|
|
374
374
|
|
|
375
375
|
When a state declares `sub`, the per-transition ordering is:
|
|
376
376
|
|
|
@@ -510,8 +510,8 @@ Example-first, PBT-augmented. Lesson from jssm: "3000+ tests / 100% coverage" tu
|
|
|
510
510
|
| ------- | ------------------------------------------------------------------ |
|
|
511
511
|
| v0.1 | core + guards + effects + inspect + replay + pbt (this release) |
|
|
512
512
|
| v0.2 | Async-guard detection, coverage tuning, llms-full.txt verify gate |
|
|
513
|
-
| v0.3 | Hierarchical sugar via `state.sub` (experimental
|
|
514
|
-
| v0.4 |
|
|
513
|
+
| v0.3 | Hierarchical sugar via `state.sub` (experimental) |
|
|
514
|
+
| v0.4 | Sub-machine API promoted to stable; dependency-reduction cycle |
|
|
515
515
|
| v0.5 | `aifsmjs-bridge-bitecs` / `aifsmjs-bridge-pixi` (separate sub-packages) |
|
|
516
516
|
| v1.0 | API freeze and stability guarantee |
|
|
517
517
|
|
|
@@ -522,7 +522,7 @@ Example-first, PBT-augmented. Lesson from jssm: "3000+ tests / 100% coverage" tu
|
|
|
522
522
|
- **Tick / game-loop hook** (out of scope for v1)
|
|
523
523
|
- **ECS / Pixi bridges** (out of scope for v1)
|
|
524
524
|
|
|
525
|
-
**
|
|
525
|
+
**Future candidate**: `historyState` — remember last active sub-state on
|
|
526
526
|
re-entry. Workaround today: snapshot via `onTransition` and restore
|
|
527
527
|
manually.
|
|
528
528
|
|
|
@@ -543,6 +543,34 @@ All notable changes to this project will be documented in this file.
|
|
|
543
543
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
544
544
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
545
545
|
|
|
546
|
+
## [0.4.0] — 2026-05-29
|
|
547
|
+
|
|
548
|
+
### Changed
|
|
549
|
+
|
|
550
|
+
- **Sub-machine API promoted experimental → stable.** The hierarchical
|
|
551
|
+
sub-machine surface shipped in 0.3.0 — `StateDef.sub`, `StateDef.subImpl`,
|
|
552
|
+
`Runtime.subRuntime()`, `SubMachineError`, and the `SubMachineDef` type
|
|
553
|
+
alias — is now stable. Signatures and runtime semantics, including the
|
|
554
|
+
init-failure quarantine behaviour, are frozen for the 1.x line; the
|
|
555
|
+
boundaries documented in `STABILITY.md` are intentional design trade-offs,
|
|
556
|
+
not instability. **No signature changed from 0.3.x.**
|
|
557
|
+
|
|
558
|
+
### Dependency reduction
|
|
559
|
+
|
|
560
|
+
- Part of the ai*js v0.4.0 dependency-reduction cycle. `fast-check` remains
|
|
561
|
+
an **optional** peer dependency isolated to the `aifsmjs/pbt` subpath. A
|
|
562
|
+
fresh build confirms the core entry and the five non-pbt subpaths
|
|
563
|
+
(`guards` / `effects` / `inspect` / `replay` / `timer`) are tree-shake-free
|
|
564
|
+
of `fast-check` (only `dist/pbt/index.js` references it). `pnpm audit`
|
|
565
|
+
reports zero advisories. No `devDependency` changes.
|
|
566
|
+
|
|
567
|
+
### Compatibility
|
|
568
|
+
|
|
569
|
+
This release adds **no runtime API** and changes **no signature**. The core
|
|
570
|
+
bundle is byte-identical to 0.3.1 (gzip 4,387 B); existing 0.3.x consumer
|
|
571
|
+
code is unaffected. The only substantive change is the documented stability
|
|
572
|
+
tier of the sub-machine API.
|
|
573
|
+
|
|
546
574
|
## [0.3.1] — 2026-05-29
|
|
547
575
|
|
|
548
576
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aifsmjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Small, strict FSM library for deterministic, replayable state machines in any TypeScript/JS app — multi-step forms, checkout funnels, auth flows, tutorials, scene flow. Pure step() lifecycle, opt-in effects, inspect, replay, and a fast-check property-based testing adapter. Browser / Node / Bun / Deno / WebView / Worker friendly.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fsm",
|