aifsmjs 0.3.1 → 0.4.1

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 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` (experimental, since 0.3.0) | Closures embedded in definition (breaks serialize) |
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.3.0, experimental)
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, this release) |
501
- | v0.4 | `historyState` — remember last active sub-state on re-entry |
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
- **v0.4 candidate**: `historyState` — remember last active sub-state on
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(experimental,since 0.3.0) | Definition 內直接綁 closure(會無法序列化) |
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.3.0,experimental)
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 | `historyState` — re-entry 時恢復上一次的 sub-state |
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
- **v0.4 候選**:`historyState` — re-entry 時自動恢復上一次的 sub-state。0.3.0 的暫代方案:透過 `onTransition` snapshot sub-runtime 的 value,之後手動還原。
506
+ **未來候選**:`historyState` — re-entry 時自動恢復上一次的 sub-state。0.3.0 的暫代方案:透過 `onTransition` snapshot sub-runtime 的 value,之後手動還原。
507
507
 
508
508
  ---
509
509
 
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` (experimental, since 0.3.0) | Closures embedded in definition (breaks serialize) |
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.3.0, experimental)
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, this release) |
514
- | v0.4 | `historyState` — remember last active sub-state on re-entry |
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
- **v0.4 candidate**: `historyState` — remember last active sub-state on
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,47 @@ 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.1] — 2026-05-29
547
+
548
+ ### Changed
549
+
550
+ - **`STABILITY.md` is now repo-only** — removed from the npm `files`
551
+ allowlist, aligning with the majority of the ai*js family (5 of 7
552
+ packages already ship the stability contract repo-only). The file stays
553
+ in the repository and remains visible on GitHub and the rendered npm
554
+ package page; it is simply no longer bundled inside the published
555
+ tarball. Packaging consistency patch: **no runtime API change, no
556
+ signature change**; the built bundles (`dist/`) are byte-identical to
557
+ 0.4.0 (core gzip 4,387 B).
558
+
559
+ ## [0.4.0] — 2026-05-29
560
+
561
+ ### Changed
562
+
563
+ - **Sub-machine API promoted experimental → stable.** The hierarchical
564
+ sub-machine surface shipped in 0.3.0 — `StateDef.sub`, `StateDef.subImpl`,
565
+ `Runtime.subRuntime()`, `SubMachineError`, and the `SubMachineDef` type
566
+ alias — is now stable. Signatures and runtime semantics, including the
567
+ init-failure quarantine behaviour, are frozen for the 1.x line; the
568
+ boundaries documented in `STABILITY.md` are intentional design trade-offs,
569
+ not instability. **No signature changed from 0.3.x.**
570
+
571
+ ### Dependency reduction
572
+
573
+ - Part of the ai*js v0.4.0 dependency-reduction cycle. `fast-check` remains
574
+ an **optional** peer dependency isolated to the `aifsmjs/pbt` subpath. A
575
+ fresh build confirms the core entry and the five non-pbt subpaths
576
+ (`guards` / `effects` / `inspect` / `replay` / `timer`) are tree-shake-free
577
+ of `fast-check` (only `dist/pbt/index.js` references it). `pnpm audit`
578
+ reports zero advisories. No `devDependency` changes.
579
+
580
+ ### Compatibility
581
+
582
+ This release adds **no runtime API** and changes **no signature**. The core
583
+ bundle is byte-identical to 0.3.1 (gzip 4,387 B); existing 0.3.x consumer
584
+ code is unaffected. The only substantive change is the documented stability
585
+ tier of the sub-machine API.
586
+
546
587
  ## [0.3.1] — 2026-05-29
547
588
 
548
589
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aifsmjs",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
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",
@@ -74,7 +74,6 @@
74
74
  "dist",
75
75
  "README.md",
76
76
  "README_ZHTW.md",
77
- "STABILITY.md",
78
77
  "LICENSE",
79
78
  "llms.txt",
80
79
  "llms-full.txt"
package/STABILITY.md DELETED
@@ -1,77 +0,0 @@
1
- # Stability
2
-
3
- This document defines the stability tier of every public symbol exported by
4
- `aifsmjs`. Tiers govern what breaks may occur in future minor / major bumps.
5
-
6
- ## Stable (since 0.1.0)
7
-
8
- Fully stable. Breaking changes only at a major version bump (1.0+).
9
-
10
- - `createMachine`, `defineMachine`, `setup`, `createRuntime`, `initialSnapshot`
11
- - `step`, `resolveTransitions`, `evalGuard`, `resolveGuard`, `isAsyncGuardFn`
12
- - `assign`, `mergeContext`, `createSnapshot`, `deepFreeze`, `freezeSnapshot`
13
- - `Runtime.send`, `Runtime.reset`, `Runtime.can`, `Runtime.getSnapshot`,
14
- `Runtime.snapshot`, `Runtime.subscribe`, `Runtime.on`, `Runtime.dispose`,
15
- `Runtime.signal`, `Runtime.disposed`
16
- - All error classes from 0.1.0–0.2.1: `RuntimeDisposedError`,
17
- `InvalidDefinitionError`, `UnknownActionError`, `UnknownGuardError`,
18
- `AsyncGuardError`
19
- - Types: `MachineDef`, `StateDef` (fields `on`, `entry`, `exit`, `final`),
20
- `TransitionDef`, `Snapshot`, `Implementations`, `Guard`, `Action`,
21
- `EffectHandler`, `Effect`, `Enqueuer`, `Middleware`, `MiddlewareContext`,
22
- `RuntimeOptions`, `StepResult`, `ResetEvent`, `RESET_EVENT_TYPE`,
23
- `RuntimeTransitionEvent`, `RuntimeErrorEvent`, `RuntimeEventMap`
24
- - All subpath exports: `aifsmjs/guards`, `aifsmjs/effects`, `aifsmjs/inspect`,
25
- `aifsmjs/replay`, `aifsmjs/pbt`, `aifsmjs/timer`
26
- - `Runtime.onTransition` (added in 0.3.0) — pure sugar over the stable
27
- `on('transition', ...)` API; listed under Stable because the underlying
28
- contract is unchanged.
29
-
30
- ## Experimental (since 0.3.0)
31
-
32
- Shape and behaviour may change in any minor bump until 1.0. Production use
33
- is OK; expect a one-line patch on minor upgrades.
34
-
35
- - `StateDef.sub` (optional `SubMachineDef`) — when present, a child runtime
36
- is lazily initialised on entry and disposed on exit. Per-transition
37
- ordering is parent `step()` (exit / actions / entry) → child dispose →
38
- child init → snapshot commit.
39
- - `StateDef.subImpl` (optional `Implementations`) — paired with `sub`;
40
- passed to the child `createRuntime`. Defaults to `{}`.
41
- - `Runtime.subRuntime()` — returns the live child handle, or `undefined`.
42
- Returned generic is `Runtime<unknown, { type: string }, string>`; caller
43
- narrows via cast if necessary.
44
- - `SubMachineError` — thrown by `send()` / `reset()` on child init/dispose
45
- failure. Fields: `parentState`, `phase ("init" | "dispose")`, `cause`.
46
- - `SubMachineDef` type alias.
47
-
48
- ### Known boundaries (0.3.0)
49
-
50
- - **Replay / PBT do not see child state.** `replay()` and
51
- `commandsFromMachine` only inspect parent snapshots. If your business
52
- logic lives in the parent layer, replay is still deterministic.
53
- - **`subRuntime()` may return a disposed handle** if an external caller
54
- disposed it. The handle is not reinitialised until the parent leaves and
55
- re-enters the sub-bearing state. Detect with `child.disposed`.
56
- - **Self-targeting external (`A → A`) is treated as full exit/entry**:
57
- child is disposed and reinitialised. The 0.3.0 dispatcher re-resolves
58
- guards to identify the chosen transition before deciding external vs
59
- internal, so guarded internal transitions on the same event no longer
60
- trigger a reinit.
61
- - **Init-failure mid-transition leaves the parent without a live child.**
62
- If `applySubLifecycle` successfully disposes the old child and then the
63
- new child's `createRuntime` throws, the parent's snapshot is rolled back
64
- to `prev` but `subRuntime()` returns `undefined`. Callers catching
65
- `SubMachineError(phase: "init")` should treat the runtime as quarantined
66
- — call `runtime.dispose()` (idempotent) or `runtime.reset()` (which
67
- attempts re-init) before sending further events. A future major may
68
- switch to a two-phase "init before dispose" commit strategy; for 0.3.x
69
- this remains opt-in only when child failures are expected.
70
-
71
- ## Draft (planned, not implemented in 0.3.0)
72
-
73
- API sketched, not shipped. May change before release.
74
-
75
- - `historyState` (v0.4 candidate) — opt-in pseudo-state that remembers
76
- the last active sub-state on re-entry. Workaround in 0.3.0: snapshot
77
- the sub-runtime's value on exit via `onTransition`, restore manually.