aifsmjs 0.2.0 → 0.3.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 +43 -10
- package/README_ZHTW.md +36 -8
- package/STABILITY.md +77 -0
- package/dist/effects/index.d.cts +1 -1
- package/dist/effects/index.d.ts +1 -1
- package/dist/guards/index.d.cts +1 -1
- package/dist/guards/index.d.ts +1 -1
- package/dist/index.cjs +139 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -3
- package/dist/index.d.ts +21 -3
- package/dist/index.js +139 -24
- package/dist/index.js.map +1 -1
- package/dist/inspect/index.d.cts +1 -1
- package/dist/inspect/index.d.ts +1 -1
- package/dist/pbt/index.cjs +129 -23
- package/dist/pbt/index.cjs.map +1 -1
- package/dist/pbt/index.d.cts +1 -1
- package/dist/pbt/index.d.ts +1 -1
- package/dist/pbt/index.js +129 -23
- package/dist/pbt/index.js.map +1 -1
- package/dist/replay/index.d.cts +1 -1
- package/dist/replay/index.d.ts +1 -1
- package/dist/{types-BLwKAHnW.d.cts → types-CGKk6Rur.d.cts} +70 -1
- package/dist/{types-BLwKAHnW.d.ts → types-CGKk6Rur.d.ts} +70 -1
- package/llms-full.txt +110 -10
- package/package.json +6 -4
package/llms-full.txt
CHANGED
|
@@ -21,9 +21,9 @@ The short index lives at `llms.txt` (see https://llmstxt.org/).
|
|
|
21
21
|
|
|
22
22
|
> A small, strict FSM library for any TypeScript/JS app that needs deterministic, replayable state transitions. Lifecycle is a pure `step()` function. Chain-of-Responsibility intuition is reserved for cross-cutting concerns (observe / persist / replay), never for the transition core.
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Part of the [ai\*js micro-runtime ecosystem](https://github.com/yshengliao) — see also [aibridgejs](https://github.com/yshengliao/aibridgejs) (cross-context RPC) and [aiecsjs](https://github.com/yshengliao/aiecsjs) (ECS).
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
**Primary audience**: developers building stateful flows — multi-step forms, checkout funnels, auth flows, tutorial sequences, document-status workflows, scene flow in interactive apps, and the same patterns in browser-based games (PixiJS / Svelte 5 / plain Canvas / WebGL). The core is **environment-neutral** (pure function + adapter boundary): browser, Node, Bun, Deno, Flutter WebView, and Web Workers all work. The Roadmap section keeps gaming-specific niceties (tick hook, ECS bridge) as opt-in subpaths, not core surface.
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -119,15 +119,16 @@ The three layers are fully decoupled: take `step()` alone for replay, take `Mach
|
|
|
119
119
|
|
|
120
120
|
| Will do (v1) | Won't do |
|
|
121
121
|
| --------------------------------------------------- | ------------------------------------------------- |
|
|
122
|
-
| Flat states + transitions |
|
|
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
124
|
| Guards (sync only; inline async throws `InvalidDefinitionError` at `defineMachine`; runtime throws `AsyncGuardError` on thenable return) | Async guards |
|
|
124
125
|
| Actions (assign + enqueue effects) | Async API inside an action (use an effect) |
|
|
125
126
|
| Fire-and-forget effects | Actor invocation / spawn |
|
|
126
127
|
| Read-only inspect middleware | Cancellable transition middleware |
|
|
127
128
|
| `replay(initial, log, def, impl)` pure function | Time-travel debugger (v2 candidate) |
|
|
128
129
|
| `fast-check` `fc.commands` adapter | Custom PBT framework |
|
|
129
|
-
| String ref + runtime injection |
|
|
130
|
-
| Tree-shake friendly subpath exports |
|
|
130
|
+
| String ref + runtime injection | Single root import for everything |
|
|
131
|
+
| Tree-shake friendly subpath exports | ECS / Pixi bridges (opt-in subpath, not core) |
|
|
131
132
|
|
|
132
133
|
---
|
|
133
134
|
|
|
@@ -369,6 +370,27 @@ Non-goals:
|
|
|
369
370
|
- No async lifecycle hook
|
|
370
371
|
- Inspect middleware cannot alter the transition outcome
|
|
371
372
|
|
|
373
|
+
### Sub-machine lifecycle (since 0.3.0, experimental)
|
|
374
|
+
|
|
375
|
+
When a state declares `sub`, the per-transition ordering is:
|
|
376
|
+
|
|
377
|
+
1. Parent `step()` runs: `exit actions → transition.actions → entry actions`.
|
|
378
|
+
2. Old child (if any) `dispose()` — synchronous; exceptions become
|
|
379
|
+
`SubMachineError(phase: "dispose")`.
|
|
380
|
+
3. New child (if next state has `sub`) instantiation — exceptions become
|
|
381
|
+
`SubMachineError(phase: "init")`.
|
|
382
|
+
4. Parent snapshot commits.
|
|
383
|
+
5. Middleware pipeline runs.
|
|
384
|
+
6. Effects dispatch.
|
|
385
|
+
7. `'transition'` event emits to `on()` / `onTransition()` subscribers.
|
|
386
|
+
|
|
387
|
+
If step 2 or 3 throws, the parent snapshot is **not** committed (rollback
|
|
388
|
+
to `prev`); no middleware / effects / `'transition'` runs.
|
|
389
|
+
|
|
390
|
+
`runtime.dispose()` cascades to the child via `controller.signal`'s abort
|
|
391
|
+
listener and an explicit `child.dispose()` call. Cascade swallows child
|
|
392
|
+
exceptions to honour the never-throws dispose contract.
|
|
393
|
+
|
|
372
394
|
---
|
|
373
395
|
|
|
374
396
|
## Lifecycle Protocol
|
|
@@ -451,7 +473,7 @@ Example-first, PBT-augmented. Lesson from jssm: "3000+ tests / 100% coverage" tu
|
|
|
451
473
|
- **Example tests** (vitest): for every src module, write happy path + edge + error-message triplets.
|
|
452
474
|
- **PBT smoke**: each generic property runs 50 iterations as an invariant guard, not as a coverage source.
|
|
453
475
|
- **CI-enforced thresholds**: `@vitest/coverage-v8` is wired to **100% statements / 100% lines / 100% functions / ≥90% branches**. The few defensive invariant-guard branches (e.g. runtime determinism mismatch) carry `/* v8 ignore */` annotations with rationale.
|
|
454
|
-
- **Size budget**: `scripts/check-size.mjs` enforces per-subpath gzip caps in CI — core ≤3
|
|
476
|
+
- **Size budget**: `scripts/check-size.mjs` enforces per-subpath gzip caps in CI — core ≤4.7 KB (raised in 0.3.0 for sub-machine sugar), replay ≤1.8 KB, pbt ≤5.5 KB (raised in 0.3.0 because `pbt` transitively imports `createRuntime`), others ≤1 KB. Exceeding any cap fails the build.
|
|
455
477
|
|
|
456
478
|
### The 6 built-in generic properties
|
|
457
479
|
|
|
@@ -471,7 +493,7 @@ Example-first, PBT-augmented. Lesson from jssm: "3000+ tests / 100% coverage" tu
|
|
|
471
493
|
| | aifsmjs | XState v5 | Robot3 | @xstate/store | Zag.js |
|
|
472
494
|
| -------------------------- | -------------- | ----------------- | ----------------- | ----------------- | ----------------- |
|
|
473
495
|
| Core size (gzip) | ~2.8KB | ~15KB | ~1KB | < 1KB | per-component |
|
|
474
|
-
| Hierarchical states |
|
|
496
|
+
| Hierarchical states | Sugar (0.3.0) | Yes | No | N/A | Yes |
|
|
475
497
|
| Async invoke / actor | No | Yes | No | N/A | No |
|
|
476
498
|
| Guard combinators | and/or/not | and/or/not | No | N/A | No |
|
|
477
499
|
| Effects dual-track | enqueue | enqueueActions | reduce/action | enq.effect() | array of names |
|
|
@@ -487,12 +509,23 @@ Example-first, PBT-augmented. Lesson from jssm: "3000+ tests / 100% coverage" tu
|
|
|
487
509
|
| Version | Scope |
|
|
488
510
|
| ------- | ------------------------------------------------------------------ |
|
|
489
511
|
| v0.1 | core + guards + effects + inspect + replay + pbt (this release) |
|
|
490
|
-
| v0.2 |
|
|
491
|
-
| v0.3 |
|
|
492
|
-
| v0.4 |
|
|
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 |
|
|
493
515
|
| v0.5 | `aifsmjs-bridge-bitecs` / `aifsmjs-bridge-pixi` (separate sub-packages) |
|
|
494
516
|
| v1.0 | API freeze and stability guarantee |
|
|
495
517
|
|
|
518
|
+
**Out of scope (v1)**:
|
|
519
|
+
|
|
520
|
+
- **Parallel state regions** (out of scope for v1)
|
|
521
|
+
- **Actor invocation / spawn** (out of scope for v1)
|
|
522
|
+
- **Tick / game-loop hook** (out of scope for v1)
|
|
523
|
+
- **ECS / Pixi bridges** (out of scope for v1)
|
|
524
|
+
|
|
525
|
+
**v0.4 candidate**: `historyState` — remember last active sub-state on
|
|
526
|
+
re-entry. Workaround today: snapshot via `onTransition` and restore
|
|
527
|
+
manually.
|
|
528
|
+
|
|
496
529
|
---
|
|
497
530
|
|
|
498
531
|
## License
|
|
@@ -510,6 +543,73 @@ All notable changes to this project will be documented in this file.
|
|
|
510
543
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
511
544
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
512
545
|
|
|
546
|
+
## [0.3.0] — 2026-05-29
|
|
547
|
+
|
|
548
|
+
### Added
|
|
549
|
+
|
|
550
|
+
- **Hierarchical / sub-machine sugar** (experimental). `StateDef` accepts
|
|
551
|
+
two new optional fields: `sub` (a child `SubMachineDef`) and `subImpl`
|
|
552
|
+
(the child's `Implementations`). When the runtime enters a state with
|
|
553
|
+
`sub`, a child `Runtime` is lazily instantiated; when it exits, the child
|
|
554
|
+
is disposed. Access via `runtime.subRuntime()`. See `STABILITY.md` for
|
|
555
|
+
the experimental contract.
|
|
556
|
+
- New error: `SubMachineError` (`{ parentState, phase, cause }`) thrown
|
|
557
|
+
by `send()` / `reset()` on child init / dispose failure. `dispose()`
|
|
558
|
+
cascade swallows child dispose exceptions (idempotent + never-throws
|
|
559
|
+
contract).
|
|
560
|
+
- New type alias: `SubMachineDef<SubCtx, SubEvt, SubStates>`.
|
|
561
|
+
- **`runtime.onTransition(handler, opts?)`** — semantic sugar over
|
|
562
|
+
`runtime.on('transition', handler, opts)`. One-line delegation; shares
|
|
563
|
+
the same listener Set, so registration order across both APIs determines
|
|
564
|
+
invocation order. Returned unsubscribe identical to `on('transition', ...)`.
|
|
565
|
+
|
|
566
|
+
### Stability
|
|
567
|
+
|
|
568
|
+
- New file: `STABILITY.md`. Documents the three tiers: **stable**
|
|
569
|
+
(everything shipped 0.1.0–0.2.1), **experimental** (`sub`, `subImpl`,
|
|
570
|
+
`subRuntime`, `SubMachineError`, `SubMachineDef`), **draft**
|
|
571
|
+
(`historyState`, v0.4 candidate).
|
|
572
|
+
|
|
573
|
+
### Changed (positioning)
|
|
574
|
+
|
|
575
|
+
- **README "Capabilities / Limitations" table**: "Hierarchical / compound
|
|
576
|
+
states" moved out of the "Won't do" column. Added on the "Will do" side
|
|
577
|
+
as "Hierarchical sugar via `state.sub` (experimental since 0.3.0)".
|
|
578
|
+
- **README Lifecycle Invariants**: documented the sub-machine ordering —
|
|
579
|
+
parent `step()` lifecycle (exit / actions / entry) runs first, then
|
|
580
|
+
child dispose → child init → snapshot commit → middleware → effects →
|
|
581
|
+
`transition` emit.
|
|
582
|
+
|
|
583
|
+
### Build & tooling
|
|
584
|
+
|
|
585
|
+
- **`scripts/check-size.mjs`**: core gzip budget raised 3,700 → 4,700 B
|
|
586
|
+
to absorb sub-machine lifecycle, `SubMachineError`, and `onTransition`
|
|
587
|
+
sugar (measured at 4,465 B). `pbt` budget raised 4,600 → 5,500 B because
|
|
588
|
+
`pbt/properties.ts` imports `createRuntime` from `runtime.ts`; the new
|
|
589
|
+
sub-machine code is pulled in transitively (measured at 5,228 B).
|
|
590
|
+
|
|
591
|
+
### Compatibility
|
|
592
|
+
|
|
593
|
+
This release is **non-breaking** for v0.2.1 callers who do not opt into
|
|
594
|
+
the new sub-machine fields. All existing API signatures, error types, and
|
|
595
|
+
runtime behaviour are byte-identical.
|
|
596
|
+
|
|
597
|
+
## [0.2.1] — 2026-05-28
|
|
598
|
+
|
|
599
|
+
### Security
|
|
600
|
+
|
|
601
|
+
- **Resolve two Dependabot moderate advisories** on the transitive dev-only graph by upgrading `vitest` 2.1.0 → 4.1.7 and `@vitest/coverage-v8` 2.1.9 → 4.1.7. Adds `vite` 8.0.14 as a direct devDependency to satisfy vitest 4's peer range (`^6 || ^7 || ^8`). These are dev-only — runtime surface unchanged. Same fix as `aibridgejs` 0.1.2.
|
|
602
|
+
- [GHSA-67mh-4wv8-2f99](https://github.com/advisories/GHSA-67mh-4wv8-2f99) `esbuild <=0.24.2` CORS development server data leak (fixed in 0.25.0).
|
|
603
|
+
- [GHSA-4w7w-66w2-5vf9](https://github.com/advisories/GHSA-4w7w-66w2-5vf9) `vite <=6.4.1` path traversal in optimized deps `.map` handling (fixed in 6.4.2 / 7.3.2 / 8.0.5).
|
|
604
|
+
|
|
605
|
+
### Changed
|
|
606
|
+
|
|
607
|
+
- **Coverage threshold relaxed**: statements 100 → 95 in [vitest.config.ts](vitest.config.ts). Vitest 4 with v8 coverage scores defensive race-recovery if-guards (e.g. `if (!current) return;` in timeout/abort handlers) as separate statements that are not deterministically reachable. Lines and functions stay at 100%; branches stays at 90%.
|
|
608
|
+
- **`prepublishOnly` now includes `verify:llms`** so llms-full.txt drift is caught at publish time as well as CI.
|
|
609
|
+
- **README opening unified across the ai*js family**: five-badge shields row, one-line tagline as blockquote, ecosystem footer.
|
|
610
|
+
|
|
611
|
+
Runtime surface unchanged. Production bundles are byte-identical to 0.2.0.
|
|
612
|
+
|
|
513
613
|
## [0.2.0] — 2026-05-28
|
|
514
614
|
|
|
515
615
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aifsmjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"dist",
|
|
75
75
|
"README.md",
|
|
76
76
|
"README_ZHTW.md",
|
|
77
|
+
"STABILITY.md",
|
|
77
78
|
"LICENSE",
|
|
78
79
|
"llms.txt",
|
|
79
80
|
"llms-full.txt"
|
|
@@ -94,7 +95,7 @@
|
|
|
94
95
|
"example:approval": "tsx examples/02-approval-workflow/index.ts",
|
|
95
96
|
"example:checkout-funnel": "tsx examples/03-checkout-funnel/index.ts",
|
|
96
97
|
"example:form-wizard": "tsx examples/04-form-wizard/index.ts",
|
|
97
|
-
"prepublishOnly": "pnpm typecheck && pnpm lint && pnpm coverage && pnpm build && pnpm verify:exports && pnpm check:size"
|
|
98
|
+
"prepublishOnly": "pnpm typecheck && pnpm lint && pnpm coverage && pnpm build && pnpm verify:exports && pnpm verify:llms && pnpm check:size"
|
|
98
99
|
},
|
|
99
100
|
"peerDependencies": {
|
|
100
101
|
"fast-check": "^3.20.0"
|
|
@@ -107,12 +108,13 @@
|
|
|
107
108
|
"devDependencies": {
|
|
108
109
|
"@biomejs/biome": "^1.9.0",
|
|
109
110
|
"@types/node": "^22.0.0",
|
|
110
|
-
"@vitest/coverage-v8": "^
|
|
111
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
111
112
|
"fast-check": "^3.20.0",
|
|
112
113
|
"tsup": "^8.3.0",
|
|
113
114
|
"tsx": "^4.22.3",
|
|
114
115
|
"typescript": "^5.6.0",
|
|
115
|
-
"
|
|
116
|
+
"vite": "^8.0.14",
|
|
117
|
+
"vitest": "^4.1.7"
|
|
116
118
|
},
|
|
117
119
|
"engines": {
|
|
118
120
|
"node": ">=18.0.0"
|