jssm 5.138.0 → 5.139.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 CHANGED
@@ -18,10 +18,10 @@ Please edit the file it's derived from, instead: `./src/md/readme_base.md`
18
18
 
19
19
 
20
20
 
21
- * Generated for version 5.138.0 at 5/28/2026, 7:32:08 AM
21
+ * Generated for version 5.139.0 at 5/28/2026, 8:04:13 AM
22
22
 
23
23
  -->
24
- # jssm 5.138.0
24
+ # jssm 5.139.0
25
25
 
26
26
  [**Try the live editor**](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) ·
27
27
  [Documentation](https://stonecypher.github.io/jssm/docs/) ·
@@ -281,7 +281,7 @@ That decision shows up everywhere downstream:
281
281
  or run `npm run benny` against your own machine.
282
282
 
283
283
  - **More thoroughly tested than any other JavaScript state-machine
284
- library.** 6,349 tests at 100.0% line coverage
284
+ library.** 6,389 tests at 100.0% line coverage
285
285
  ([report](https://coveralls.io/github/StoneCypher/jssm)), plus
286
286
  fuzz testing via `fast-check`, with parser test data across ten natural
287
287
  languages and Emoji.
@@ -414,11 +414,11 @@ If your contribution is missing here, please open an issue.
414
414
 
415
415
  <br/>
416
416
 
417
- ***6,349 tests***, run 57,136 times.
417
+ ***6,389 tests***, run 57,176 times.
418
418
 
419
- - 5,836 specs with 100.0% coverage
420
- - 513 fuzz tests with 3.6% coverage
421
- - 5,288 TypeScript lines - 1.2 tests per line, 10.8 generated tests per line
419
+ - 5,876 specs with 100.0% coverage
420
+ - 513 fuzz tests with 3.5% coverage
421
+ - 5,397 TypeScript lines - 1.2 tests per line, 10.6 generated tests per line
422
422
 
423
423
  [![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)
424
424
  [![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)
@@ -30,7 +30,7 @@
30
30
  "description": "The owning machine; used for the `state()` accessor."
31
31
  }
32
32
  ],
33
- "description": "Build a JssmHookProxy that wraps an arbitrary hook context object.\n\nThe context shape varies by hook kind (`from`/`to`/`action` may be absent\nfor transition-kind hooks), so this normalizes the shape via optional\nfields and exposes mutable `data` while keeping the rest read-only.\n\nThe `machine` parameter is used only for `state()`, so unit tests can\nsubstitute any object with a `state(): unknown` method."
33
+ "description": "Build a JssmHookProxy that wraps an arbitrary hook context object.\r\n\r\nThe context shape varies by hook kind (`from`/`to`/`action` may be absent\r\nfor transition-kind hooks), so this normalizes the shape via optional\r\nfields and exposes mutable `data` while keeping the rest read-only.\r\n\r\nThe `machine` parameter is used only for `state()`, so unit tests can\r\nsubstitute any object with a `state(): unknown` method."
34
34
  },
35
35
  {
36
36
  "kind": "function",
@@ -56,7 +56,7 @@
56
56
  "description": "Identifier appended to the synthetic sourceURL."
57
57
  }
58
58
  ],
59
- "description": "Compile a textContent body into a callable user handler.\n\nUses dynamic function construction — the same primitive browsers use\ninternally for `<a onclick=\"...\">` and `setTimeout(stringBody, ms)`.\nStrict CSP without `'unsafe-eval'` blocks this and the call will throw;\nconsumers should fall back to the `handler=\"name\"` form there.\n\nPrepends a `//# sourceURL=` comment so devtools surface a meaningful name\nin stack traces instead of `anonymous`."
59
+ "description": "Compile a textContent body into a callable user handler.\r\n\r\nUses dynamic function construction — the same primitive browsers use\r\ninternally for `<a onclick=\"...\">` and `setTimeout(stringBody, ms)`.\r\nStrict CSP without `'unsafe-eval'` blocks this and the call will throw;\r\nconsumers should fall back to the `handler=\"name\"` form there.\r\n\r\nPrepends a `//# sourceURL=` comment so devtools surface a meaningful name\r\nin stack traces instead of `anonymous`."
60
60
  },
61
61
  {
62
62
  "kind": "function",
@@ -83,7 +83,7 @@
83
83
  "description": "Optional in-WC registry to consult first."
84
84
  }
85
85
  ],
86
- "description": "Resolve a `handler=\"name\"` attribute to a callable by consulting first the\noptional in-WC registry, then `globalThis[name]`. Throws a clear error if\nneither resolves."
86
+ "description": "Resolve a `handler=\"name\"` attribute to a callable by consulting first the\r\noptional in-WC registry, then `globalThis[name]`. Throws a clear error if\r\nneither resolves."
87
87
  },
88
88
  {
89
89
  "kind": "function",
@@ -102,7 +102,7 @@
102
102
  "description": "The raw attribute value, or null if not present."
103
103
  }
104
104
  ],
105
- "description": "Validate and normalize a `<jssm-hook kind=\"...\">` value, defaulting to\n`\"hook\"` when the attribute is absent. Throws on unknown kinds rather\nthan silently doing nothing later."
105
+ "description": "Validate and normalize a `<jssm-hook kind=\"...\">` value, defaulting to\r\n`\"hook\"` when the attribute is absent. Throws on unknown kinds rather\r\nthan silently doing nothing later."
106
106
  },
107
107
  {
108
108
  "kind": "function",
@@ -136,7 +136,7 @@
136
136
  "description": "Optional in-WC registry of named handlers."
137
137
  }
138
138
  ],
139
- "description": "Parse a single `<jssm-hook>` element into a JssmHookInstallSpec.\n\nValidates the mutual-exclusion rule between `handler=\"name\"` and inline\nbody, defaults `kind` to `\"hook\"`, resolves named handlers against the\noptional registry then `globalThis`, and compiles inline bodies via\ndynamic function construction. Conditional-required attributes (e.g.\n`from`/`to` for `kind=\"hook\"`) are NOT validated here — `set_hook` will\nthrow with its own clear errors on missing pieces, which keeps the\nerror surface single-sourced."
139
+ "description": "Parse a single `<jssm-hook>` element into a JssmHookInstallSpec.\r\n\r\nValidates the mutual-exclusion rule between `handler=\"name\"` and inline\r\nbody, defaults `kind` to `\"hook\"`, resolves named handlers against the\r\noptional registry then `globalThis`, and compiles inline bodies via\r\ndynamic function construction. Conditional-required attributes (e.g.\r\n`from`/`to` for `kind=\"hook\"`) are NOT validated here — `set_hook` will\r\nthrow with its own clear errors on missing pieces, which keeps the\r\nerror surface single-sourced."
140
140
  },
141
141
  {
142
142
  "kind": "function",
@@ -162,7 +162,7 @@
162
162
  "description": "The owning machine; used by the proxy's `state()`."
163
163
  }
164
164
  ],
165
- "description": "Wrap a JssmHookUserHandler so that jssm's native hook contract is\nsatisfied: the user gets a friendly proxy, the proxy's mutated `data`\nbecomes the `HookComplexResult.data`, and an explicit `false` return\ncancels the transition.\n\nAny non-`false` return — including `undefined`, `true`, or an arbitrary\nobject — allows the transition. This matches the contract spelled out\nin the issue (#641): \"return false cancels; anything else allows\"."
165
+ "description": "Wrap a JssmHookUserHandler so that jssm's native hook contract is\r\nsatisfied: the user gets a friendly proxy, the proxy's mutated `data`\r\nbecomes the `HookComplexResult.data`, and an explicit `false` return\r\ncancels the transition.\r\n\r\nAny non-`false` return — including `undefined`, `true`, or an arbitrary\r\nobject — allows the transition. This matches the contract spelled out\r\nin the issue (#641): \"return false cancels; anything else allows\"."
166
166
  },
167
167
  {
168
168
  "kind": "function",
@@ -185,10 +185,10 @@
185
185
  "type": {
186
186
  "text": "(ctx: RawHookContext) => unknown"
187
187
  },
188
- "description": "The wrapped (friendly-proxy) handler from {@link wrap_user_handler}.\n * "
188
+ "description": "The wrapped (friendly-proxy) handler from {@link wrap_user_handler}.\r\n * "
189
189
  }
190
190
  ],
191
- "description": "Build the typed descriptor object passed to `machine.set_hook` (and later\nto `machine.remove_hook` for cleanup) from a parsed JssmHookInstallSpec\nand the wrapped handler.\n\nFor kinds that need `from`/`to`/`action`, the descriptor includes those.\nMissing required keys produce `undefined` here; jssm's `set_hook` will\nsurface the error with its own clear message so we don't duplicate\nvalidation.\n\nReturn type is `unknown` because jssm's `HookDescription` is a\ndiscriminated union and our runtime-discriminator value can't be tracked\nby TypeScript across the build. The WC casts at the `set_hook` call site."
191
+ "description": "Build the typed descriptor object passed to `machine.set_hook` (and later\r\nto `machine.remove_hook` for cleanup) from a parsed JssmHookInstallSpec\r\nand the wrapped handler.\r\n\r\nFor kinds that need `from`/`to`/`action`, the descriptor includes those.\r\nMissing required keys produce `undefined` here; jssm's `set_hook` will\r\nsurface the error with its own clear message so we don't duplicate\r\nvalidation.\r\n\r\nReturn type is `unknown` because jssm's `HookDescription` is a\r\ndiscriminated union and our runtime-discriminator value can't be tracked\r\nby TypeScript across the build. The WC casts at the `set_hook` call site."
192
192
  }
193
193
  ],
194
194
  "exports": [
@@ -254,6 +254,85 @@
254
254
  "kind": "javascript-module",
255
255
  "path": "src/ts/wc/jssm_instance_wc.ts",
256
256
  "declarations": [
257
+ {
258
+ "kind": "variable",
259
+ "name": "JSSM_ON_EVENT_NAMES",
260
+ "default": "new Set<string>([ 'transition', 'rejection', 'action', 'entry', 'exit', 'terminal', 'complete', 'error', 'data-change', 'override', 'timeout', 'hook-registration', 'hook-removal' ])",
261
+ "description": "Allow-list of event names accepted by `<jssm-on event=\"...\">`. Must stay\r\nin sync with the `JssmEventName` union in `jssm_types.ts` (the library's\r\n`machine.on(...)` event API, added in #638). Validating here gives the\r\ndeclarative wiring a clear \"unknown event name\" error at the WC layer\r\ninstead of relying on a downstream library throw whose message would\r\nmention `machine.on(...)` rather than the offending tag."
262
+ },
263
+ {
264
+ "kind": "function",
265
+ "name": "parse_jssm_on_element",
266
+ "return": {
267
+ "type": {
268
+ "text": ""
269
+ }
270
+ },
271
+ "parameters": [
272
+ {
273
+ "name": "el",
274
+ "type": {
275
+ "text": "HTMLElement"
276
+ },
277
+ "description": "The `<jssm-on>` element to parse."
278
+ }
279
+ ],
280
+ "description": "Parse a `<jssm-on>` element into a validated ParsedJssmOn\r\nrecord. Centralized so the declarative-tag logic is testable without\r\nspinning up the full `<jssm-instance>` lifecycle.\r\n\r\nValidation rules (per #643):\r\n - `event` is required and must be in JSSM_ON_EVENT_NAMES.\r\n - Either a `handler=\"name\"` attribute or non-empty `textContent`\r\n must be supplied, but not both.\r\n - `state` is only meaningful for `event=\"entry\"` / `event=\"exit\"`;\r\n it's silently ignored on other events.\r\n - `from` / `to` are only meaningful for `event=\"transition\"`; they\r\n are silently ignored on other events. Both → AND (a specific\r\n edge). Neither → unfiltered.\r\n\r\n```typescript\r\nconst el = document.createElement('jssm-on');\r\nel.setAttribute('event', 'entry');\r\nel.setAttribute('state', 'paid');\r\nel.setAttribute('handler', 'onPaid');\r\nparse_jssm_on_element(el);\r\n// => { event: 'entry', handler_name: 'onPaid', inline_body: undefined,\r\n// once: false, name: undefined, filter: { state: 'paid' } }\r\n```"
281
+ },
282
+ {
283
+ "kind": "variable",
284
+ "name": "jssm_handler_registry",
285
+ "type": {
286
+ "text": "Map<string, (...args: unknown[]) => unknown>"
287
+ },
288
+ "default": "new Map()",
289
+ "description": "Optional global registry that `<jssm-on>` (and, later, `<jssm-hook>`)\r\nconsult first when resolving a `handler=\"name\"` attribute. Consumers\r\nregister named handlers here in a strict-CSP environment where a stray\r\n`globalThis[name]` isn't acceptable. Falls through to `globalThis[name]`\r\nif the registry has no entry.\r\n\r\nIntentionally a `Map<string, Function>` rather than a class with methods,\r\nso consumers can use any of `.get`, `.set`, `.delete`, `.clear` directly\r\nwithout a thin wrapper API."
290
+ },
291
+ {
292
+ "kind": "function",
293
+ "name": "resolve_named_handler",
294
+ "return": {
295
+ "type": {
296
+ "text": ""
297
+ }
298
+ },
299
+ "parameters": [
300
+ {
301
+ "name": "name",
302
+ "type": {
303
+ "text": "string"
304
+ },
305
+ "description": "The handler name as supplied by `handler=\"...\"`."
306
+ }
307
+ ],
308
+ "description": "Resolve a named handler from the registry, then from `globalThis`.\r\nThrows if neither lookup finds a function — earlier failure here is\r\nbetter than a delayed \"is not a function\" at first event delivery."
309
+ },
310
+ {
311
+ "kind": "function",
312
+ "name": "compile_inline_body",
313
+ "return": {
314
+ "type": {
315
+ "text": ""
316
+ }
317
+ },
318
+ "parameters": [
319
+ {
320
+ "name": "body",
321
+ "type": {
322
+ "text": "string"
323
+ },
324
+ "description": "The inline JS body (function body, not full function)."
325
+ },
326
+ {
327
+ "name": "source_id",
328
+ "type": {
329
+ "text": "string"
330
+ },
331
+ "description": "A short identifier for the sourceURL pragma."
332
+ }
333
+ ],
334
+ "description": "Compile an inline-body string into a handler function whose single\r\nparameter is `e` (the event detail object). Uses the same dynamic\r\n`Function(...)` constructor that browsers use internally for inline\r\nevent-handler attributes such as `<a onclick=\"...\">`; the input here\r\nis consumer-authored markup, never network data, so the surface is\r\nexactly that of an inline event-handler attribute and the same CSP\r\ncaveats apply (strict CSP without `'unsafe-eval'` blocks it). A\r\n`//# sourceURL=jssm-on:N` pragma is appended so devtools stack traces\r\npoint at a meaningful name."
335
+ },
257
336
  {
258
337
  "kind": "function",
259
338
  "name": "resolve_fsl_source",
@@ -342,6 +421,16 @@
342
421
  "default": "undefined",
343
422
  "description": "The underlying machine instance, constructed at `connectedCallback`.\r\nExposed raw (not proxied) per the #639/#648 design decision so that\r\nconsumers can use the full Machine API directly.\r\n\r\nMarked optional because Lit will instantiate the element before\r\n`connectedCallback` runs; the instance is guaranteed present after\r\nconnection."
344
423
  },
424
+ {
425
+ "kind": "field",
426
+ "name": "_on_unsubscribes",
427
+ "type": {
428
+ "text": "Array<() => void>"
429
+ },
430
+ "privacy": "private",
431
+ "default": "[]",
432
+ "description": "Unsubscribe callbacks for every `machine.on(...)` / `machine.once(...)`\r\nsubscription installed from a `<jssm-on>` child during\r\n`connectedCallback`. Walked in `disconnectedCallback` so a removed\r\n`<jssm-instance>` doesn't leave dangling handlers on its (now-orphan)\r\nmachine. Array (insertion order) rather than Set so cleanup order is\r\ndeterministic and easy to reason about."
433
+ },
345
434
  {
346
435
  "kind": "field",
347
436
  "name": "registry",
@@ -428,6 +517,17 @@
428
517
  },
429
518
  "description": "Convenience wrapper for `machine.state()`. Returns the current\r\nstate's name."
430
519
  },
520
+ {
521
+ "kind": "method",
522
+ "name": "_install_jssm_on_children",
523
+ "privacy": "private",
524
+ "return": {
525
+ "type": {
526
+ "text": "void"
527
+ }
528
+ },
529
+ "description": "Discover direct-child `<jssm-on>` elements and install their\r\nsubscriptions on the owned machine. Per #643:\r\n\r\n- Direct children only (`:scope > jssm-on`). Deeper nesting is the\r\n responsibility of a future MutationObserver-driven v2.\r\n- Each `<jssm-on>` is parsed by parse_jssm_on_element, which\r\n enforces the form / event-name / filter rules.\r\n- Handlers come from resolve_named_handler (form A) or\r\n compile_inline_body (form B), and the result is installed\r\n via `machine.on(...)` or `machine.once(...)` depending on the\r\n element's `once` attribute.\r\n- Every returned unsubscribe is tracked in _on_unsubscribes\r\n so disconnectedCallback can release them all.\r\n\r\nCalled once from `connectedCallback` after the machine has been\r\nconstructed. Any error thrown by parsing or resolution propagates\r\nout so it surfaces via jsdom's error event (matching the rest of\r\n`<jssm-instance>`'s \"fail loud at connect\" policy)."
530
+ },
431
531
  {
432
532
  "kind": "method",
433
533
  "name": "_install_declarative_hooks",
@@ -437,7 +537,7 @@
437
537
  "text": "void"
438
538
  }
439
539
  },
440
- "description": "Discover every direct-child `<jssm-hook>` element and install each\r\nagainst the owned machine. Handlers are wrapped with the friendly-proxy\r\nadapter that lets user code write `m.data = ...` and return `false` to\r\ncancel — see make_hook_proxy and the issue (#641) doc-comment\r\nfor the full contract.\r\n\r\nDirect children only (the `:scope > jssm-hook` selector) so that nested\r\n`<jssm-instance>` elements don't have their child hooks installed on\r\nthe outer machine.\r\n\r\nTracks every installed descriptor in `_installed_hooks` so that\r\n`disconnectedCallback` can remove them on detach."
540
+ "description": "Discover every direct-child `<jssm-hook>` element and install each\r\nagainst the owned machine. Handlers are wrapped with the friendly-proxy\r\nadapter that lets user code write `m.data = ...` and return `false` to\r\ncancel — see make_hook_proxy and the issue (#641) doc-comment\r\nfor the full contract."
441
541
  },
442
542
  {
443
543
  "kind": "method",
@@ -448,7 +548,7 @@
448
548
  "text": "string"
449
549
  }
450
550
  },
451
- "description": "Prefix used in synthetic `//# sourceURL=jssm-hook:<prefix><n>` annotations\r\nfor inline-body hooks compiled by this element. Includes the element's\r\n`id` when present so multi-instance pages can tell sources apart in\r\ndevtools."
551
+ "description": "Prefix used in synthetic `//# sourceURL=jssm-hook:<prefix><n>` annotations\r\nfor inline-body hooks compiled by this element."
452
552
  },
453
553
  {
454
554
  "kind": "method",
@@ -459,7 +559,7 @@
459
559
  "text": "void"
460
560
  }
461
561
  },
462
- "description": "Wire DOM events to machine actions, using the two declarative forms from\r\nissue #640:\r\n\r\n 1. Inline attribute form: every descendant of the host carrying a\r\n `data-jssm-action=\"<name>\"` attribute receives a listener on the\r\n event named by `data-jssm-event` (default `click`).\r\n 2. Dedicated tag form: each direct `<jssm-action>` child of the host\r\n supplies a CSS `selector` (scoped to the host), an `action`, and an\r\n optional `event` (default `click`); every matching descendant\r\n receives a listener configured by the tag's attributes.\r\n\r\nBoth forms support optional `from-state` guards (dispatch only when the\r\nmachine's current state matches), `from-property` data extraction (pass\r\nthe source element's named property as the action's data argument), and\r\n`prevent-default` / `stop-propagation` modifiers.\r\n\r\nEvery installed listener is recorded in _action_listeners so\r\ndisconnectedCallback can detach them cleanly."
562
+ "description": "Wire DOM events to machine actions, using the two declarative forms from\r\nissue #640. Both forms support optional `from-state` guards,\r\n`from-property` data extraction, and `prevent-default` /\r\n`stop-propagation` modifiers."
463
563
  },
464
564
  {
465
565
  "kind": "method",
@@ -475,39 +575,10 @@
475
575
  "name": "config",
476
576
  "type": {
477
577
  "text": "{\r\n source : HTMLElement;\r\n event_name : string;\r\n action_name : string;\r\n from_state : string | undefined;\r\n from_property : string | undefined;\r\n prevent_default : boolean;\r\n stop_propagation : boolean;\r\n }"
478
- },
479
- "description": "Listener configuration."
480
- },
481
- {
482
- "description": "Element to attach the listener to.",
483
- "name": "config.source"
484
- },
485
- {
486
- "description": "DOM event to listen for.",
487
- "name": "config.event_name"
488
- },
489
- {
490
- "description": "Action to dispatch on the machine.",
491
- "name": "config.action_name"
492
- },
493
- {
494
- "description": "If set, only fire when `machine.state() === from_state`.",
495
- "name": "config.from_state"
496
- },
497
- {
498
- "description": "If set, pass `source[from_property]` as the action's data argument.",
499
- "name": "config.from_property"
500
- },
501
- {
502
- "description": "If true, call `e.preventDefault()` before checking the guard.",
503
- "name": "config.prevent_default"
504
- },
505
- {
506
- "description": "If true, call `e.stopPropagation()` before checking the guard.",
507
- "name": "config.stop_propagation"
578
+ }
508
579
  }
509
580
  ],
510
- "description": "Attach one DOM listener that translates a DOM event into a\r\n`machine.action(...)` call, honoring the configured modifiers. The\r\nlistener is recorded in _action_listeners so it can be removed\r\non disconnect."
581
+ "description": "Attach one DOM listener that translates a DOM event into a\r\n`machine.action(...)` call, honoring the configured modifiers."
511
582
  },
512
583
  {
513
584
  "kind": "method",
@@ -541,6 +612,46 @@
541
612
  }
542
613
  ],
543
614
  "exports": [
615
+ {
616
+ "kind": "js",
617
+ "name": "JSSM_ON_EVENT_NAMES",
618
+ "declaration": {
619
+ "name": "JSSM_ON_EVENT_NAMES",
620
+ "module": "src/ts/wc/jssm_instance_wc.ts"
621
+ }
622
+ },
623
+ {
624
+ "kind": "js",
625
+ "name": "parse_jssm_on_element",
626
+ "declaration": {
627
+ "name": "parse_jssm_on_element",
628
+ "module": "src/ts/wc/jssm_instance_wc.ts"
629
+ }
630
+ },
631
+ {
632
+ "kind": "js",
633
+ "name": "jssm_handler_registry",
634
+ "declaration": {
635
+ "name": "jssm_handler_registry",
636
+ "module": "src/ts/wc/jssm_instance_wc.ts"
637
+ }
638
+ },
639
+ {
640
+ "kind": "js",
641
+ "name": "resolve_named_handler",
642
+ "declaration": {
643
+ "name": "resolve_named_handler",
644
+ "module": "src/ts/wc/jssm_instance_wc.ts"
645
+ }
646
+ },
647
+ {
648
+ "kind": "js",
649
+ "name": "compile_inline_body",
650
+ "declaration": {
651
+ "name": "compile_inline_body",
652
+ "module": "src/ts/wc/jssm_instance_wc.ts"
653
+ }
654
+ },
544
655
  {
545
656
  "kind": "js",
546
657
  "name": "resolve_fsl_source",