@turing-machine-js/machine 7.0.0-alpha.1 → 7.0.0-alpha.3
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/CHANGELOG.md +90 -0
- package/README.md +65 -24
- package/dist/classes/State.d.ts +18 -0
- package/dist/index.cjs +705 -312
- package/dist/index.mjs +705 -312
- package/dist/utilities/graph.d.ts +4 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,96 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [7.0.0-alpha.3] - 2026-05-21
|
|
8
|
+
|
|
9
|
+
Third v7 pre-release. Adds first-class out-of-band tags on `State` ([#186](https://github.com/mellonis/turing-machine-js/issues/186)) — a metadata channel for visualization grouping and debugger labels that survives `toGraph` / `fromGraph` / `toMermaid` / `fromMermaid` round-trips. Driven by downstream [post-machine-js#86](https://github.com/mellonis/post-machine-js/issues/86), which will build a path-based registry and inline pseudo-command on top once this ships. Published under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
|
|
10
|
+
|
|
11
|
+
**Pre-release — the API surface may still shift before stable v7.0.0.** Pin to a specific alpha for reproducibility: `@turing-machine-js/machine@7.0.0-alpha.3`.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`State.tag(...) / .untag(...) / .tags` API** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). Fluent post-construct API for attaching string tags to a State. Tags are out-of-band metadata — they don't affect runtime transition lookup, `equivalentOn` comparisons, or any structural identity. Storage lives on the State INSTANCE (not on the shared `#symbolToDataMap`), so engine [#175](https://github.com/mellonis/turing-machine-js/issues/175) memoization doesn't leak tags across wrappers that share a bare: `A.wohs(t1).tag('hot')` does NOT propagate to `A.wohs(t2)`.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
const s = new State({...}, 'foo')
|
|
19
|
+
.tag('hot', 'sampled')
|
|
20
|
+
.untag('sampled');
|
|
21
|
+
s.tags; // ['hot'] ← frozen snapshot, in insertion order
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- **`GraphNode.tags: string[]`** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). New field on the serialized graph node. Survives `State.toGraph` / `State.fromGraph` round-trip; empty array for untagged states.
|
|
25
|
+
|
|
26
|
+
- **`toMermaid` tag rendering** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). Two surfaces:
|
|
27
|
+
- **Visible labels via `<br>`**: tagged node labels include the tag names inline (`s5["A<br>hot, sampled"]`). Uses Mermaid's universal `<br>` line break — works across mermaid.js, the live editor, machines-demo, and any other renderer; no CSS-pseudo-element tricks.
|
|
28
|
+
- **Color grouping via `classDef` + `class`**: each unique tag gets a `classDef tag_<sanitized> fill:#...,stroke:#...` line (palette of 6 colors selected by tag-name hash) and a `class s5,s6 tag_<sanitized>` line listing every node carrying the tag.
|
|
29
|
+
|
|
30
|
+
- **`fromMermaid` tag parse** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). Splits the node label on `<br>` to extract tags as the source of truth; `class` lines are decorative and discarded on parse (they regenerate on the next `toMermaid` emit from the tag set).
|
|
31
|
+
|
|
32
|
+
### Compatibility
|
|
33
|
+
|
|
34
|
+
- Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.2` → `^7.0.0-alpha.3` on `@turing-machine-js/builder`, `@turing-machine-js/library-binary-numbers`, `@turing-machine-js/library-binary-numbers-bare`.
|
|
35
|
+
|
|
36
|
+
### Migration from alpha.2
|
|
37
|
+
|
|
38
|
+
Purely additive — no breaking changes. Existing code that doesn't call `state.tag(...)` or read `state.tags` / `GraphNode.tags` continues to work identically. Mermaid emit for untagged states is bytewise unchanged.
|
|
39
|
+
|
|
40
|
+
If you serialize `GraphNode` JSON, note that the new `tags: string[]` field is now required by the type (always emitted; empty array if no tags).
|
|
41
|
+
|
|
42
|
+
### Out of v7-alpha.3 (still pending for stable v7.0.0)
|
|
43
|
+
|
|
44
|
+
- **[#102](https://github.com/mellonis/turing-machine-js/issues/102)** — debugger step-in / step-out / step-over primitives.
|
|
45
|
+
- **[#180](https://github.com/mellonis/turing-machine-js/issues/180)** — extract `State.toGraph`/`fromGraph` to its own module.
|
|
46
|
+
|
|
47
|
+
## [7.0.0-alpha.2] - 2026-05-21
|
|
48
|
+
|
|
49
|
+
Second v7 pre-release. Refines alpha.1's `toMermaid` wrapped-state emit into the function-call model ([#174](https://github.com/mellonis/turing-machine-js/issues/174)) and adds two construction-time improvements to `withOverriddenHaltState` ([#175](https://github.com/mellonis/turing-machine-js/issues/175), [#176](https://github.com/mellonis/turing-machine-js/issues/176)). Published to npm under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
|
|
50
|
+
|
|
51
|
+
**Pre-release — the API surface may still shift before stable v7.0.0.** Pin to a specific alpha for reproducibility: `@turing-machine-js/machine@7.0.0-alpha.2`. Migration walkthrough at the bottom.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- **`toMermaid` callable-subtree emit** ([#174](https://github.com/mellonis/turing-machine-js/issues/174)) — supersedes alpha.1's collapsed-bare emit (#138/#139) with a function-call model:
|
|
56
|
+
- **Wrapper and bare are separate graph nodes.** The wrapper sits OUTSIDE any subgraph as `[[composite-name]]` (call site). The bare lives INSIDE its callable subtree subgraph as a regular `[name]` node.
|
|
57
|
+
- **Subgraph label** changed from `"halt frame"` to `"callable subtree of NAME"` (single bare) or `"callable scope: A ∪ B"` (multi-bare union frame).
|
|
58
|
+
- **Halt marker is per-frame**, not per-wrapper. When union-find merges two bares' subtrees (shared body state), they share one halt marker.
|
|
59
|
+
- **Arrow vocabulary rewritten**:
|
|
60
|
+
- Solid `-->` for regular transitions AND for the wrapper's post-return `--> override` (just an ordinary transition under the call/return model).
|
|
61
|
+
- Bold `==> "call"` is RESERVED for the wrapper-to-bare call. `&` ribbon syntax (`s_W1 & s_W2 == "call" ==> s_A`) collapses multiple wrappers that share a bare.
|
|
62
|
+
- Dotted `-.->` is reserved for frame-level dispatch: `w_N -. "return" .-> wrapper` (demand-emit), `w_N -. "halt" .-> s0` (demand-emit on non-wrapper entry), `idle -. enter .-> sN` (unchanged).
|
|
63
|
+
- The `-. onHalt .->` keyword from alpha.1 is **retired** — replaced by a solid `--> override` arrow.
|
|
64
|
+
- **No per-context duplication.** Shared bares (e.g. `library-binary-numbers/minusOne`'s `invertNumber`, called by two wrappers) appear as a single subtree with `& `-joined call arrows from each wrapper.
|
|
65
|
+
- **`GraphNode` field changes**: `isWrapped` removed; `isWrapper: boolean`, `bareStateId: number | null`, `frameId: number | null` added. `overriddenHaltStateId` now lives on wrapper nodes only.
|
|
66
|
+
- Bytewise round-trip stability now holds for **all** wrapped states, including shared-bare cases (alpha.1 was only stable for simple wrappers).
|
|
67
|
+
|
|
68
|
+
- **Nested `.wohs()` chain collapse** ([#176](https://github.com/mellonis/turing-machine-js/issues/176)) — `A.withOverriddenHaltState(t1).withOverriddenHaltState(t2)` is now equivalent to `A.withOverriddenHaltState(t2)`. The chain's inner override (`t1`) is dead at runtime (only the outermost wrapper's override is pushed onto the halt stack on entry — verified empirically). Composite name now reflects runtime behavior: `A(t2)`, not the misleading `A(t1)(t2)`. `withOverriddenHaltState` unwraps `this` to its bare before constructing the new wrapper.
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- **`withOverriddenHaltState` memoization** ([#175](https://github.com/mellonis/turing-machine-js/issues/175)) — calls with the same `(bare, override)` pair return the literally-same `State` instance. Backed by a two-level `WeakMap` keyed by the bare with `WeakRef`-valued entries, so cached wrappers can be GC'd when nothing else holds them. Composes with #176's chain-collapse: `A.wohs(t1).wohs(t2)` and `A.wohs(t2)` both resolve to the same instance.
|
|
73
|
+
|
|
74
|
+
- **Spec doc** at `docs/superpowers/specs/2026-05-21-halt-frame-transitive-closure.md` capturing the callable-subtree model design (worked examples for simple wrappers, PostMachine subroutines, shared-bare wrappers, self-wrapping, nested chains, Reference cycles, shared body states).
|
|
75
|
+
|
|
76
|
+
### Migration from alpha.1
|
|
77
|
+
|
|
78
|
+
Three breaking changes vs alpha.1, all in the visualization layer:
|
|
79
|
+
|
|
80
|
+
**1. Mermaid format** — alpha.1 Mermaid strings (with `-. onHalt .->` edges or `[[bare]]` inside subgraphs) will NOT parse with the new `fromMermaid`. The format is one-way: re-emit via `toMermaid(toGraph(state, tapeBlock))` on alpha.2 to regenerate.
|
|
81
|
+
|
|
82
|
+
**2. `Graph` data shape** — `GraphNode.isWrapped` removed; replaced by `isWrapper: boolean`, `bareStateId: number | null`, `frameId: number | null`. If you store serialized `Graph` JSON from alpha.1, re-emit on alpha.2.
|
|
83
|
+
|
|
84
|
+
**3. Wrapper composite name in nested chains** — `A.wohs(t1).wohs(t2)` was `A(t1)(t2)` in alpha.1; under #176 it's now `A(t2)`. Code that parsed the composite name to extract intermediate overrides will need to adapt (those overrides were never actually pushed at runtime — alpha.1's name was misleading).
|
|
85
|
+
|
|
86
|
+
`withOverriddenHaltState` memoization (#175) is fully additive — same arguments now return the same instance, but no observable behavior changes for code that doesn't rely on instance identity.
|
|
87
|
+
|
|
88
|
+
### Out of v7-alpha.2 (still pending for stable v7.0.0)
|
|
89
|
+
|
|
90
|
+
- **[#102](https://github.com/mellonis/turing-machine-js/issues/102)** — debugger step-in / step-out / step-over primitives. Additive — won't change any existing API. Will land in `v7.0.0-alpha.3` or stable `v7.0.0`.
|
|
91
|
+
- **[#180](https://github.com/mellonis/turing-machine-js/issues/180)** — extract `State.toGraph`/`fromGraph` to its own module. Internal refactor; no API change.
|
|
92
|
+
|
|
93
|
+
### Compatibility
|
|
94
|
+
|
|
95
|
+
- Peer dep `@turing-machine-js/machine` widened `^7.0.0-alpha.1` → `^7.0.0-alpha.2` on `@turing-machine-js/builder`, `@turing-machine-js/library-binary-numbers`, `@turing-machine-js/library-binary-numbers-bare`.
|
|
96
|
+
|
|
7
97
|
## [7.0.0-alpha.1] - 2026-05-21
|
|
8
98
|
|
|
9
99
|
First v7 pre-release. Consolidates the composition-representation overhaul landed across [#149](https://github.com/mellonis/turing-machine-js/issues/149), [#148](https://github.com/mellonis/turing-machine-js/issues/148), [#138](https://github.com/mellonis/turing-machine-js/issues/138), and [#139](https://github.com/mellonis/turing-machine-js/issues/139). Published to npm under the `next` dist-tag: `npm install @turing-machine-js/machine@next`.
|
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ A composable Turing-machine engine for JavaScript: multi-tape, subroutine compos
|
|
|
13
13
|
- [Building from a state table](#building-from-a-state-table)
|
|
14
14
|
- [Classes](#classes) — [`Alphabet`](#alphabet) · [`Tape`](#tape) · [`TapeBlock`](#tapeblock) · [`TapeCommand`](#tapecommand) · [`Command`](#command) · [`State`](#state) · [`Reference`](#reference) · [`TuringMachine`](#turingmachine)
|
|
15
15
|
- [Subroutine composition with `withOverriddenHaltState`](#subroutine-composition-with-withoverriddenhaltstate)
|
|
16
|
+
- [State tags](#state-tags)
|
|
16
17
|
- [Debugging breakpoints](#debugging-breakpoints)
|
|
17
18
|
- [Special objects](#special-objects) — [`haltState`](#haltstate) · [`ifOtherSymbol`](#ifothersymbol) · [`movements`](#movements) · [`symbolCommands`](#symbolcommands)
|
|
18
19
|
- [Introspection and testing](#introspection-and-testing)
|
|
@@ -268,7 +269,7 @@ flowchart TD
|
|
|
268
269
|
|
|
269
270
|
> 💡 **Mermaid renders at most one edge per source/target pair.** If a state has two distinct transitions back to itself (or two parallel transitions to the same target), only one shows in the diagram. The string output is correct — this is a viewer-side limitation. For graphs with multiple parallel edges, paste the `toMermaid` output into [mermaid.live](https://mermaid.live) and switch to the `stateDiagram-v2` renderer, or post-process the output to your preferred format.
|
|
270
271
|
|
|
271
|
-
`fromMermaid` parses the same format back into a `Graph`. The round-trip is **behaviorally** lossless — the rebuilt graph runs to the same outputs on the same inputs (tested in `test/round-trip.spec.ts` for the binary-numbers libraries).
|
|
272
|
+
`fromMermaid` parses the same format back into a `Graph`. The round-trip is **behaviorally** lossless — the rebuilt graph runs to the same outputs on the same inputs (tested in `test/round-trip.spec.ts` for the binary-numbers libraries). Under the v7 callable-subtree emit (#174), bytewise stability holds across rebuilds even for shared-bare cases (modulo state-id renumbering, which the test normalizes). The composite name is not stored as any graph node's label — `fromGraph` recomputes it fresh on reconstruction — so the accumulation problem from #138 cannot reoccur.
|
|
272
273
|
|
|
273
274
|
### Reference
|
|
274
275
|
|
|
@@ -398,32 +399,54 @@ flowchart TD
|
|
|
398
399
|
%% alphabets: [[" ","a","b","X"]]
|
|
399
400
|
s0(((halt)))
|
|
400
401
|
s2["eraseHere"]
|
|
402
|
+
s3[["scanToX(eraseHere)"]]
|
|
401
403
|
idle([idle])
|
|
402
|
-
subgraph
|
|
403
|
-
|
|
404
|
-
|
|
404
|
+
subgraph w_1["callable subtree of scanToX"]
|
|
405
|
+
s1["scanToX"]
|
|
406
|
+
c1(((halt)))
|
|
405
407
|
end
|
|
406
408
|
idle -. enter .-> s3
|
|
409
|
+
s3 == "call" ==> s1
|
|
410
|
+
w_1 -. "return" .-> s3
|
|
411
|
+
s3 --> s2
|
|
412
|
+
s1 -- "['X'] → [K]/[S]" --> c1
|
|
413
|
+
s1 -- "[*] → [K]/[R]" --> s1
|
|
407
414
|
s2 -- "[*] → [E]/[S]" --> s0
|
|
408
|
-
s3 -- "['X'] → [K]/[S]" --> c3
|
|
409
|
-
s3 -- "[*] → [K]/[R]" --> s3
|
|
410
|
-
s3 -. onHalt .-> s2
|
|
411
415
|
```
|
|
412
416
|
|
|
413
|
-
**Reading guide** — the v7 emit (introduced in [#
|
|
417
|
+
**Reading guide** — the v7 callable-subtree emit (introduced in [#174](https://github.com/mellonis/turing-machine-js/issues/174)) models `withOverriddenHaltState` as a function call: the wrapper is the call site, the bare's subtree is the callable body.
|
|
414
418
|
|
|
415
|
-
1.
|
|
416
|
-
2. **`[
|
|
417
|
-
3. **The
|
|
418
|
-
4. **The dotted
|
|
419
|
-
5. **Real `(((halt)))` outside any subgraph** (`s0`) is the actual run terminus. Reached only by states
|
|
419
|
+
1. **`[[scanToX(eraseHere)]]` (Mermaid subroutine / double-walled-rectangle shape)** is the wrapper node, drawn OUTSIDE any subgraph. It's the runtime entry point — `idle -. enter .->` arrives here — and shows the composite name (`bare(override)`). Wrappers have no transitions of their own; they delegate to the bare via the `call` arrow.
|
|
420
|
+
2. **`subgraph w_1["callable subtree of scanToX"]`** is the bare's callable subtree — the scope of code that runs when the wrapper is "called." It contains the bare `s1["scanToX"]`, any body states reachable from the bare, and a local halt marker `c1(((halt)))` where the bare's halt-bound transitions land.
|
|
421
|
+
3. **The bold `==> call`** from wrapper to bare is the call arrow — visual signature of "wrapper invokes this callable subtree, pushing its override onto the runtime stack." Bold arrows are reserved for wrapper-to-bare calls; counting them in a diagram counts the wrappers in play.
|
|
422
|
+
4. **The dotted `-. return .->`** from the subtree back to the wrapper is the return arrow — fires when the bare halts (lands on `c1`) and the stack pops. The wrapper's solid `--> s2` (to `eraseHere`) is the post-return continuation; ordinary transition under the function-call mental model.
|
|
423
|
+
5. **Real `(((halt)))` outside any subgraph** (`s0`) is the actual run terminus. Reached only by states OUTSIDE any callable subtree — here, by `eraseHere` after it erases the cell.
|
|
420
424
|
|
|
421
|
-
**Reading runtime sequence on tape `['a','b','X','b','a']`:** enter
|
|
425
|
+
**Reading runtime sequence on tape `['a','b','X','b','a']`:** enter at wrapper `[[scanToX(eraseHere)]]` (with `eraseHere` queued as the override); `call` into the subtree of `scanToX`; `[*] → [K]/[R]` self-loops on `s1` until the head sees `X`; the `['X'] → [K]/[S]` edge lands on `c1`; `return` to the wrapper; solid `--> s2` to `eraseHere`; `eraseHere` runs `[*] → [E]/[S]` and halts at real `s0`. Run terminates.
|
|
422
426
|
|
|
423
|
-
> 💡 **Round-trip
|
|
427
|
+
> 💡 **Round-trip stability.** `toMermaid → fromMermaid → toGraph → toMermaid` is bytewise stable for wrapped states ([#139](https://github.com/mellonis/turing-machine-js/issues/139) regression). The callable-subtree emit (#174) eliminates per-context duplication: shared bares like `library-binary-numbers`'s `invertNumber` (used by two wrappers in `minusOne`) render as a single subtree with two `& `-joined call arrows — so even shared-bare cases now produce stable, dedup'd round-trips.
|
|
424
428
|
|
|
425
429
|
Wrappers nest: `inner.withOverriddenHaltState(middle).withOverriddenHaltState(outer)` chains halt-redirects through `middle → outer → halt`. `library-binary-numbers/src/index.ts`'s `minusOne` (the `~(~x + 1)` composition) uses a 4-deep nest of wrappers.
|
|
426
430
|
|
|
431
|
+
## State tags
|
|
432
|
+
|
|
433
|
+
A State carries an optional set of string tags — out-of-band metadata for visualization grouping and debugger labels. Tags don't affect runtime transition lookup, `equivalentOn` comparisons, or any structural identity; they ride alongside the State.
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
const s = new State({...}, 'walkToBlank::1')
|
|
437
|
+
.tag('hot', 'subroutine-entry');
|
|
438
|
+
|
|
439
|
+
s.tags; // readonly ['hot', 'subroutine-entry'] — frozen snapshot
|
|
440
|
+
s.untag('hot');
|
|
441
|
+
s.tags; // readonly ['subroutine-entry']
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**Scoped to the wrapper instance.** Under [`withOverriddenHaltState` memoization (#175)](https://github.com/mellonis/turing-machine-js/issues/175), `A.wohs(t1)` and `A.wohs(t2)` are distinct wrapper instances even though they share `A`'s `#symbolToDataMap`. Tags live on the instance, so tagging one wrapper doesn't propagate to siblings sharing the same bare. Wrappers from `withOverriddenHaltState` start with an empty tag set (do not inherit from bare); the caller tags explicitly as needed.
|
|
445
|
+
|
|
446
|
+
**Round-trip preserved.** `state.toGraph` writes the tag set to `GraphNode.tags`; `state.fromGraph` reads it back and reapplies. `toMermaid` renders tags two ways: inline in the node label (`sN["name<br>tag1, tag2"]`, universal Mermaid line break) and as `classDef tag_<sanitized>` + `class sN tag_<sanitized>` lines for color grouping. `fromMermaid` splits the label on `<br>` as source of truth; the `class` lines are decorative and discarded on parse.
|
|
447
|
+
|
|
448
|
+
See [§Diagram conventions § Tags](#tags) for the full emit shape.
|
|
449
|
+
|
|
427
450
|
## Debugging breakpoints
|
|
428
451
|
|
|
429
452
|
Any `State` can carry a runtime-mutable `debug` config that pauses execution at chosen points.
|
|
@@ -575,8 +598,8 @@ The full reference for reading `toMermaid` output — shapes, edge styles, and t
|
|
|
575
598
|
| Shape | Meaning |
|
|
576
599
|
|---|---|
|
|
577
600
|
| `s0(((halt)))` | the halt state |
|
|
578
|
-
| `sN["name"]` | a regular state |
|
|
579
|
-
| `sN[["name"]]` | a `withOverriddenHaltState` wrapper
|
|
601
|
+
| `sN["name"]` | a regular state (or a bare, when inside a subgraph) |
|
|
602
|
+
| `sN[["composite-name"]]` | a `withOverriddenHaltState` wrapper (call site, outside any subgraph) — see [§Subroutine composition](#subroutine-composition-with-withoverriddenhaltstate) |
|
|
580
603
|
| `cN(((halt)))` inside a subgraph | halt marker (visualization aid; maps back to the singleton `haltState` at runtime) |
|
|
581
604
|
| `idle([idle])` | pre-execution sentinel (not a real state) |
|
|
582
605
|
|
|
@@ -584,14 +607,26 @@ The full reference for reading `toMermaid` output — shapes, edge styles, and t
|
|
|
584
607
|
|
|
585
608
|
| Style | Where | Meaning |
|
|
586
609
|
|---|---|---|
|
|
587
|
-
| `-->` regular solid | between states | plain transition |
|
|
588
|
-
|
|
|
589
|
-
|
|
|
590
|
-
|
|
|
610
|
+
| `-->` regular solid | between states; wrapper → override | plain transition / wrapper's post-return continuation |
|
|
611
|
+
| `==> "call"` thick solid | wrapper → bare | the wrapper's call into its callable subtree; reserved for wrapper-to-bare |
|
|
612
|
+
| `w_N -. "return" .->` dotted | subtree → wrapper | the subtree's halt-marker has incoming → control returns to the calling wrapper |
|
|
613
|
+
| `w_N -. "halt" .->` dotted | subtree → `s0` | the subtree has a non-wrapper entry path → halt-marker can fire with empty stack (real halt) |
|
|
614
|
+
| `idle -. enter .->` dotted | from `idle` to initial state | execution-start marker |
|
|
615
|
+
|
|
616
|
+
The `&` ribbon syntax (`s_W1 & s_W2 == "call" ==> s_A`) collapses multiple wrappers that share a bare into one arrow. Bold `==>` is reserved exclusively for the wrapper-to-bare `call` arrow.
|
|
591
617
|
|
|
592
618
|
### Groupings
|
|
593
619
|
|
|
594
|
-
`subgraph w_N["
|
|
620
|
+
`subgraph w_N["callable subtree of NAME"] … end` wraps a bare + its body + a halt marker — the callable scope of code that runs when a wrapper "calls" the bare. Multi-bare frames (union-find merged from shared body states) use the label `"callable scope: A ∪ B"`.
|
|
621
|
+
|
|
622
|
+
### Tags
|
|
623
|
+
|
|
624
|
+
Tagged states (via `state.tag('hot', 'sampled')` — see [§State tags](#state-tags)) render two ways simultaneously:
|
|
625
|
+
|
|
626
|
+
- **Inline in the node label**: `sN["name<br>tag1, tag2"]` — the `<br>` is Mermaid's universal line break, so the tags display as a second line under the state name in any renderer.
|
|
627
|
+
- **As a color class**: `classDef tag_<sanitized> fill:#...,stroke:#...` per unique tag (6-color palette selected by tag-name hash), plus `class sN,sM tag_<sanitized>` listing all nodes carrying the tag. Lets the eye group related states by color even when their names are scattered across the diagram.
|
|
628
|
+
|
|
629
|
+
The `<br>`-embedded label is the source of truth for `fromMermaid` round-trip; the `classDef`/`class` lines are decorative and regenerate on the next `toMermaid` emit. Tag-name sanitization in `classDef` identifiers: any char outside `[A-Za-z0-9_-]` is replaced with `_`. Labels preserve the raw tag names.
|
|
595
630
|
|
|
596
631
|
### Edge label format
|
|
597
632
|
|
|
@@ -648,10 +683,16 @@ API surface changes since v3, in past tense so the timing of each piece is expli
|
|
|
648
683
|
- **v6.2** *(superseded by v6.3.0)* — widened `onStep`'s signature to `(m) => void | Promise<void>` and added an inline `await onStep(...)` in the run loop, enabling throttle-in-`onStep` patterns. This overturned the docstring-stated contract that `onStep` is sync (microtask-free); the right place for per-iter throttling is `onPause` with self-rearm (see [Throttle pattern](#throttle-pattern)). Restored in v6.3.0.
|
|
649
684
|
- **v6.3** — `onStep` reverted to its v6.0–v6.1 sync contract — `(m) => void`, called synchronously inside the run loop. The Throttle pattern section documents the engine-native shape for per-iter throttle / "wait between iters" UIs. No other API changes.
|
|
650
685
|
- **v6.4** — New **`onIter`** hook on `run()`: awaited, fires once at the end of every iter (after both `onPause` dispatches on the same yield), unaffected by the `debug` master switch. Use for per-iter throttle / animation / coordination needing a suspend point; complements the existing sync `onStep` (tracing) and conditional `onPause` (user breakpoints). Three-hook contract is now `onStep` (sync, mid-iter) / `onPause` (awaited, on `state.debug` match) / `onIter` (awaited, end-of-iter). Additive — peer-deps unchanged. The v6.3.0 README's `onPause`-rearm throttle workaround is superseded.
|
|
651
|
-
- **v7** *(alpha
|
|
686
|
+
- **v7** *(latest alpha: alpha.3, 2026-05-21)* — Composition-representation overhaul + first-class state tags. **Pre-release on the `next` dist-tag:** `npm install @turing-machine-js/machine@next` (or pin `@7.0.0-alpha.3`). Stable v7.0.0 still pending [#102](https://github.com/mellonis/turing-machine-js/issues/102) (debugger step-in/over/out primitives). Highlights across alphas:
|
|
687
|
+
|
|
688
|
+
**alpha.3** — first-class **State tags** ([#186](https://github.com/mellonis/turing-machine-js/issues/186)). `state.tag(...) / .untag(...) / .tags` API; `GraphNode.tags: string[]` round-trips through `toGraph`/`fromGraph`; `toMermaid` emits tags two ways simultaneously — inline via `<br>` in node labels (`sN["name<br>tag1, tag2"]`) and as `classDef`/`class` for color grouping. Tags live on the State instance (not on the shared `#symbolToDataMap`), so engine [#175](https://github.com/mellonis/turing-machine-js/issues/175) memoization doesn't leak tags across wrappers sharing a bare. See [§State tags](#state-tags).
|
|
689
|
+
|
|
690
|
+
**alpha.2** — callable-subtree `toMermaid` emit refinement ([#174](https://github.com/mellonis/turing-machine-js/issues/174)). The wrapper is a separate `[[composite-name]]` node OUTSIDE the subgraph; the bare's reachable subtree becomes a `subgraph w_${frameId}["callable subtree of NAME"]` block. Frames computed via union-find — shared bares dedupe with `&` ribbons on call arrows. Bold `==> "call"` reserved for wrapper-to-bare; dotted `-.->` for frame dispatch (`return` / `halt` / `enter`). Plus engine memoization ([#175](https://github.com/mellonis/turing-machine-js/issues/175)) and nested-chain collapse ([#176](https://github.com/mellonis/turing-machine-js/issues/176)) for `.wohs()`.
|
|
691
|
+
|
|
692
|
+
**alpha.1** — initial v7 composition-representation overhaul:
|
|
652
693
|
- **`withOverrodeHaltState` → `withOverriddenHaltState`** ([#149](https://github.com/mellonis/turing-machine-js/issues/149)). Grammar fix on a name introduced in 2019: the past-participle `overridden` fits the "with a halt-state that has been ___" naming idiom; `overrode` (simple past) didn't. Hard cutover — no deprecated alias. The getter (`state.overrodeHaltState` → `state.overriddenHaltState`) and the serialized `Graph` data field (`node.overrodeHaltStateId` → `node.overriddenHaltStateId`) rename in lockstep. Consumer migration: global find/replace `OverrodeHaltState` → `OverriddenHaltState` and `overrodeHaltState` → `overriddenHaltState`. Persisted `State.toGraph` JSON dumps would need the same field-rename treatment, but persistence isn't a known consumer pattern.
|
|
653
694
|
- **Paren-based wrapped-state naming** ([#148](https://github.com/mellonis/turing-machine-js/issues/148)). `withOverriddenHaltState`'s composite name format changed from flat `bare>override` to nested `bare(override)`. Same nesting depth reads as `A(B(A))` (bare = `A`, override = `B(A)`) versus `A(B)(A)` (bare = `A(B)`, override = `A`) — two structurally-different wrap-trees that the old `>`-flat notation collided into the single string `A>B>A`. As a consequence, **user-provided state names must not contain `(` or `)`** — `State` now throws at construction time if a user passes such a name. The `>` character stays valid in user names (no longer reserved). The `inspect()` / `toGraph` / `toMermaid` outputs carry the new format. `states.md` files in `library-binary-numbers` regenerate accordingly.
|
|
654
|
-
- **`toMermaid`
|
|
695
|
+
- **`toMermaid` callable-subtree emit** ([#174](https://github.com/mellonis/turing-machine-js/issues/174), supersedes the alpha.1 collapsed-bare shape from #138/#139). `withOverriddenHaltState` is modeled as a function call: the wrapper is a `[[composite-name]]` call site OUTSIDE any subgraph, the bare's reachable subtree becomes a `subgraph w_${frameId}["callable subtree of NAME"] … end` block containing the bare + body states + a per-frame halt marker `c${frameId}(((halt)))`. Frames are computed via union-find on bare-reachability — overlapping reach sets merge into a single union frame, so shared bares (`library-binary-numbers/minusOne`'s `invertNumber`) appear ONCE with `& `-joined call arrows from each wrapper. Bold `==> "call"` arrows are reserved for the wrapper-to-bare call; dotted `-.->` is reserved for frame-level dispatch (`return` / `halt` / `enter`). The retired `-. onHalt .->` keyword is replaced by a solid `--> override` arrow (just an ordinary transition under the call/return mental model). `GraphNode` gains `isWrapper`, `bareStateId`, `frameId` fields (and drops `isWrapped`). Bytewise round-trip stability now holds for all wrapped states including shared-bare cases (no per-context duplication).
|
|
655
696
|
|
|
656
697
|
For the full release history, see the [GitHub releases page](https://github.com/mellonis/turing-machine-js/releases).
|
|
657
698
|
|
package/dist/classes/State.d.ts
CHANGED
|
@@ -32,6 +32,24 @@ export default class State {
|
|
|
32
32
|
before?: symbol[] | readonly symbol[] | true;
|
|
33
33
|
after?: symbol[] | readonly symbol[] | true;
|
|
34
34
|
} | null);
|
|
35
|
+
/**
|
|
36
|
+
* Add one or more tags to this State (#186). Tags are out-of-band metadata
|
|
37
|
+
* used by visualization (`toMermaid` emits `classDef`/`class` lines) and
|
|
38
|
+
* debugger tooling — they don't affect runtime transition lookup,
|
|
39
|
+
* `equivalentOn` comparisons, or any structural identity. Chainable.
|
|
40
|
+
*/
|
|
41
|
+
tag(...tags: string[]): this;
|
|
42
|
+
/**
|
|
43
|
+
* Remove one or more tags from this State (#186). Untagging a tag the
|
|
44
|
+
* State doesn't carry is a no-op. Chainable.
|
|
45
|
+
*/
|
|
46
|
+
untag(...tags: string[]): this;
|
|
47
|
+
/**
|
|
48
|
+
* Frozen snapshot of this State's current tags (#186). The returned array
|
|
49
|
+
* is `Object.freeze`d — mutating it throws in strict mode (which TS-emitted
|
|
50
|
+
* code uses). Order matches insertion order of the underlying Set.
|
|
51
|
+
*/
|
|
52
|
+
get tags(): readonly string[];
|
|
35
53
|
/** @internal — invoked by DebugConfig setters via module-private symbol. */
|
|
36
54
|
[validateDebugFilter](fieldName: 'before' | 'after', filter: readonly symbol[] | true | undefined): void;
|
|
37
55
|
getSymbol(tapeBlock: TapeBlock): symbol;
|