jssm 5.145.6 → 5.147.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.
@@ -48,7 +48,15 @@ function define_with_synonym(canonical_tag, synonym_tag, CanonicalClass, Synonym
48
48
  customElements.define(synonym_tag, SynonymClass);
49
49
  }
50
50
 
51
- /** Thin subclass so `<jssm-instance>` registers under a distinct constructor. */
51
+ /**
52
+ * Thin subclass so `<jssm-instance>` registers under a distinct constructor.
53
+ *
54
+ * @deprecated The `jssm-*` tag and the `JssmInstance` class alias are
55
+ * deprecated since v5 in favor of the canonical `<fsl-instance>` /
56
+ * {@link FslInstance}, for fsl.tools brand alignment. They remain functional
57
+ * but are slated for removal in v6 (tracked in `v6_breaking_changes.json` on
58
+ * the `v6` branch). New components are `fsl-*`-only.
59
+ */
52
60
  class JssmInstance extends FslInstance {
53
61
  }
54
62
  define_with_synonym('fsl-instance', 'jssm-instance', FslInstance, JssmInstance);
@@ -723,10 +723,16 @@ function resolve_fsl_source(host, fsl_attr) {
723
723
  * @cssproperty [--current-state] - The machine's current state name as a CSS string token.
724
724
  * @slot title - Heading area for the instance.
725
725
  * @slot viz - Visualization slot; fallback is a placeholder string.
726
- * @slot editor - Editor surface slot.
726
+ * @slot editor - Editor surface slot (`<fsl-editor>`, #659).
727
727
  * @slot actions - Slot for action buttons / UI.
728
- * @slot toolbar - Slot for toolbar UI.
729
- * @slot info-panel - Slot for an info / status panel.
728
+ * @slot toolbar - Slot for toolbar UI (`<fsl-toolbar>`, #660).
729
+ * @slot info-panel - Slot for an info / status panel (`<fsl-info-panel>`, #661).
730
+ * @slot history - Slot for the visited-state timeline (`<fsl-history>`, #662).
731
+ * @slot data-inspector - Slot for the typed-data tree view (`<fsl-data-inspector>`, #663).
732
+ * @slot hook-log - Slot for the hook-firing log (`<fsl-hook-log>`, #664).
733
+ * @slot effective-properties - Slot for the resolved-properties panel (`<fsl-effective-properties>`, #665).
734
+ * @slot simulation - Slot for the random-walk simulation (`<fsl-simulation>`, #668).
735
+ * @slot export - Slot for the export menu (`<fsl-export>`, #667).
730
736
  * @slot footer - Footer slot.
731
737
  */
732
738
  class FslInstance extends LitElement {
@@ -760,6 +766,20 @@ class FslInstance extends LitElement {
760
766
  * `connectedCallback`. Walked in `disconnectedCallback`.
761
767
  */
762
768
  this._on_unsubscribes = [];
769
+ /**
770
+ * Unsubscribe callbacks for the host-level mechanism-4 re-emission
771
+ * subscriptions installed in {@link _install_event_reemission}. Distinct
772
+ * from {@link _on_unsubscribes} (which belongs to `<jssm-on>` children).
773
+ */
774
+ this._reemit_unsubscribes = [];
775
+ /**
776
+ * Library events captured during the current transition, awaiting DOM
777
+ * re-dispatch once Lit commits the next render. Dispatching here (rather
778
+ * than synchronously from the machine subscription) guarantees the #639
779
+ * ordering: mechanism 1/3 reflection and mechanism 2 slot re-pick are all
780
+ * in place before a mechanism-4 listener runs.
781
+ */
782
+ this._pending_dom_events = [];
763
783
  /**
764
784
  * Per-instance registry of named hook handlers consulted before
765
785
  * `globalThis` when resolving `<fsl-hook handler="name">` /
@@ -840,8 +860,9 @@ class FslInstance extends LitElement {
840
860
  // Step 4: shadow DOM render is automatic via Lit; requesting an update
841
861
  // here ensures the first paint sees the freshly painted attributes.
842
862
  this.requestUpdate();
843
- // TODO #638: subscribe to machine.on('transition', ...) once available
844
- // and dispatch DOM CustomEvents from this element.
863
+ // #639 mechanism 4: subscribe to library events and re-emit them as
864
+ // DOM CustomEvents from this host (#638 supplies the event API).
865
+ this._install_event_reemission();
845
866
  // #641: <jssm-hook> declarative discovery.
846
867
  this._install_declarative_hooks();
847
868
  // #643: <jssm-on> declarative event observation.
@@ -920,14 +941,87 @@ class FslInstance extends LitElement {
920
941
  const host_id = this.getAttribute('id');
921
942
  return host_id !== null && host_id.length > 0 ? `${host_id}-` : '';
922
943
  }
944
+ /**
945
+ * Install the mechanism-4 (#639) re-emission subscriptions: one
946
+ * `machine.on(name, ...)` per entry in {@link REEMITTED_EVENTS}. Each
947
+ * captured library event is queued and re-dispatched as a `fsl-<name>` DOM
948
+ * event after the next render commit (see {@link updated}).
949
+ *
950
+ * Subscribing is also what *enables* the gated observation events: the
951
+ * library suppresses `transition` / `entry` / `exit` / etc. while no
952
+ * listeners exist (#670), so this host subscription is the bridge that
953
+ * turns them on.
954
+ *
955
+ * The subscription handler paints state reflection eagerly so that a host
956
+ * driven directly via `host.machine.action(...)` (bypassing {@link do})
957
+ * still updates its `current-state` attribute and `--current-state`
958
+ * property.
959
+ */
960
+ _install_event_reemission() {
961
+ const machine = this._machine;
962
+ for (const name of FslInstance.REEMITTED_EVENTS) {
963
+ // `as any` collapses the per-event detail typing — the WC is a
964
+ // schema-erased entry point; per-event payload typing belongs upstream.
965
+ const off = machine.on(name, (detail) => {
966
+ this._pending_dom_events.push({ name, detail });
967
+ this._paint_state_reflection();
968
+ this.requestUpdate();
969
+ });
970
+ this._reemit_unsubscribes.push(off);
971
+ }
972
+ }
973
+ /**
974
+ * Lit lifecycle. After every committed render, flush any library events
975
+ * captured since the last commit as DOM `CustomEvent`s. Deferring to this
976
+ * point is what gives mechanism-4 listeners the #639 ordering guarantee:
977
+ * host attributes (mechanism 1), CSS custom properties (mechanism 3), and
978
+ * the state-specific slot (mechanism 2) are all current by the time a
979
+ * `fsl-*` listener runs.
980
+ *
981
+ * @param changed - Lit's changed-property map (forwarded to super).
982
+ */
983
+ updated(changed) {
984
+ super.updated(changed);
985
+ this._flush_pending_dom_events();
986
+ }
987
+ /**
988
+ * Dispatch and clear the queue of pending DOM events. The queue is
989
+ * snapshotted and reset *before* dispatching so that a listener which
990
+ * re-enters the machine (e.g. calls `host.machine.action(...)`
991
+ * synchronously) enqueues into a fresh batch handled by the next update
992
+ * cycle, rather than mutating the array mid-iteration. This is the
993
+ * documented re-entrancy behavior: re-entrant transitions are deferred,
994
+ * not dropped.
995
+ */
996
+ _flush_pending_dom_events() {
997
+ if (this._pending_dom_events.length === 0) {
998
+ return;
999
+ }
1000
+ const batch = this._pending_dom_events;
1001
+ this._pending_dom_events = [];
1002
+ for (const ev of batch) {
1003
+ this.dispatchEvent(new CustomEvent(`fsl-${ev.name}`, {
1004
+ detail: ev.detail,
1005
+ bubbles: true,
1006
+ composed: true,
1007
+ }));
1008
+ }
1009
+ }
923
1010
  /**
924
1011
  * Lifecycle hook. Cleans up everything the WC installed at connect: hook
925
1012
  * registrations from `<jssm-hook>`, event subscriptions from `<jssm-on>`,
926
- * and DOM listeners from `<jssm-action>` / `data-jssm-action`.
1013
+ * mechanism-4 re-emission subscriptions, and DOM listeners from
1014
+ * `<jssm-action>` / `data-jssm-action`.
927
1015
  */
928
1016
  disconnectedCallback() {
929
1017
  super.disconnectedCallback();
930
- // TODO #638: unsubscribe from any direct machine.on(...) handlers added by host.
1018
+ // #639 mechanism 4: release host-level re-emission subscriptions and drop
1019
+ // any events that were queued but not yet flushed.
1020
+ for (const off of this._reemit_unsubscribes) {
1021
+ off();
1022
+ }
1023
+ this._reemit_unsubscribes = [];
1024
+ this._pending_dom_events = [];
931
1025
  // #641: remove installed hooks.
932
1026
  if (this._machine !== undefined) {
933
1027
  const machine = this._machine;
@@ -1083,6 +1177,24 @@ class FslInstance extends LitElement {
1083
1177
  <section class="info-panel">
1084
1178
  <slot name="info-panel"></slot>
1085
1179
  </section>
1180
+ <section class="history">
1181
+ <slot name="history"></slot>
1182
+ </section>
1183
+ <section class="data-inspector">
1184
+ <slot name="data-inspector"></slot>
1185
+ </section>
1186
+ <section class="hook-log">
1187
+ <slot name="hook-log"></slot>
1188
+ </section>
1189
+ <section class="effective-properties">
1190
+ <slot name="effective-properties"></slot>
1191
+ </section>
1192
+ <section class="simulation">
1193
+ <slot name="simulation"></slot>
1194
+ </section>
1195
+ <section class="export">
1196
+ <slot name="export"></slot>
1197
+ </section>
1086
1198
  <section class="state-section">
1087
1199
  <slot name=${state_slot_name}></slot>
1088
1200
  </section>
@@ -1106,6 +1218,21 @@ FslInstance.styles = css `
1106
1218
  font-style: italic;
1107
1219
  }
1108
1220
  `;
1221
+ /**
1222
+ * Library event names this WC re-emits as DOM `CustomEvent`s, fulfilling
1223
+ * mechanism 4 of #639. Each library `machine.on(name, ...)` is bridged to
1224
+ * a `fsl-<name>` DOM event (`composed`, `bubbling`) so slotted content and
1225
+ * outside consumers can observe machine activity declaratively.
1226
+ *
1227
+ * `fsl-` is the canonical prefix (matching the canonical `<fsl-*>` tag
1228
+ * names); the older `jssm-*` event prose in #639 predates that naming flip.
1229
+ * Events are NOT double-emitted under both prefixes — a symmetric listener
1230
+ * would otherwise run twice per machine event.
1231
+ */
1232
+ FslInstance.REEMITTED_EVENTS = [
1233
+ 'transition', 'entry', 'exit', 'terminal', 'complete',
1234
+ 'action', 'rejection', 'override', 'data-change', 'timeout', 'error',
1235
+ ];
1109
1236
  /**
1110
1237
  * Lit reactive properties declaration. We declare `fsl` here (rather
1111
1238
  * than via a decorator) so the attribute observation stays explicit and
@@ -48,7 +48,15 @@ function define_with_synonym(canonical_tag, synonym_tag, CanonicalClass, Synonym
48
48
  customElements.define(synonym_tag, SynonymClass);
49
49
  }
50
50
 
51
- /** Thin subclass so `<jssm-viz>` registers under a distinct constructor. */
51
+ /**
52
+ * Thin subclass so `<jssm-viz>` registers under a distinct constructor.
53
+ *
54
+ * @deprecated The `jssm-*` tag and the `JssmViz` class alias are deprecated
55
+ * since v5 in favor of the canonical `<fsl-viz>` / {@link FslViz}, for
56
+ * fsl.tools brand alignment. They remain functional but are slated for
57
+ * removal in v6 (tracked in `v6_breaking_changes.json` on the `v6` branch).
58
+ * New components are `fsl-*`-only.
59
+ */
52
60
  class JssmViz extends FslViz {
53
61
  }
54
62
  define_with_synonym('fsl-viz', 'jssm-viz', FslViz, JssmViz);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jssm",
3
- "version": "5.145.6",
3
+ "version": "5.147.0",
4
4
  "engines": {
5
5
  "node": ">=10.0.0"
6
6
  },
@@ -66,6 +66,11 @@
66
66
  "import": "./dist/wc/instance.define.js",
67
67
  "default": "./dist/wc/instance.define.js"
68
68
  },
69
+ "./cm6": {
70
+ "types": "./dist/es6/cm6/fsl_language.d.ts",
71
+ "import": "./dist/cm6/fsl_language.js",
72
+ "default": "./dist/cm6/fsl_language.js"
73
+ },
69
74
  "./cdn/viz": "./dist/cdn/viz.js",
70
75
  "./cdn/instance": "./dist/cdn/instance.js",
71
76
  "./custom-elements.json": "./custom-elements.json"
@@ -98,6 +103,8 @@
98
103
  "dist/wc/viz.define.js",
99
104
  "dist/wc/instance.js",
100
105
  "dist/wc/instance.define.js",
106
+ "dist/cm6/fsl_language.js",
107
+ "dist/es6/cm6/fsl_language.d.ts",
101
108
  "dist/cdn/viz.js",
102
109
  "dist/cdn/instance.js",
103
110
  "dist/cli/lib.cjs",
@@ -145,12 +152,13 @@
145
152
  "make_wc_viz_cdn": "rollup -c rollup.config.wc.viz.cdn.js",
146
153
  "make_wc_instance_es6": "rollup -c rollup.config.wc.instance.es6.js",
147
154
  "make_wc_instance_cdn": "rollup -c rollup.config.wc.instance.cdn.js",
155
+ "make_cm6": "rollup -c rollup.config.cm6.es6.js",
148
156
  "typescript": "tsc --build tsconfig.json",
149
157
  "makever": "node src/buildjs/makever.cjs",
150
158
  "make_doctests": "node src/buildjs/extract_examples.cjs",
151
159
  "prep": "npm run makever && npm run peg",
152
- "make": "npm run clean && npm run makever && npm run peg && npm run build:cem && npm run typescript && npm run make_doctests && npm run make_core && npm run make_deno && npm run make_viz && npm run make_wc_viz_es6 && npm run make_wc_viz_cdn && npm run make_wc_instance_es6 && npm run make_wc_instance_cdn && npm run typecheck_cli && npm run make_cli && npm run minify && npm run min_iife && npm run min_es6 && npm run min_cjs && npm run min_deno && npm run min_viz_iife && npm run min_viz_es6 && npm run min_viz_cjs && npm run min_cli && rm ./dist/es6/*.nonmin.js",
153
- "make_ci": "npm run clean && npm run makever && npm run peg && npm run build:cem && npm run typescript && npm run make_doctests && npm run make_wc_viz_es6 && npm run make_wc_viz_cdn && npm run make_wc_instance_es6 && npm run make_wc_instance_cdn && npm run typecheck_cli && npm run make_cli",
160
+ "make": "npm run clean && npm run makever && npm run peg && npm run build:cem && npm run typescript && npm run make_doctests && npm run make_core && npm run make_deno && npm run make_viz && npm run make_wc_viz_es6 && npm run make_wc_viz_cdn && npm run make_wc_instance_es6 && npm run make_wc_instance_cdn && npm run make_cm6 && npm run typecheck_cli && npm run make_cli && npm run minify && npm run min_iife && npm run min_es6 && npm run min_cjs && npm run min_deno && npm run min_viz_iife && npm run min_viz_es6 && npm run min_viz_cjs && npm run min_cli && rm ./dist/es6/*.nonmin.js",
161
+ "make_ci": "npm run clean && npm run makever && npm run peg && npm run build:cem && npm run typescript && npm run make_doctests && npm run make_wc_viz_es6 && npm run make_wc_viz_cdn && npm run make_wc_instance_es6 && npm run make_wc_instance_cdn && npm run make_cm6 && npm run typecheck_cli && npm run make_cli",
154
162
  "eslint": "eslint --color src/ts/jssm.ts src/ts/jssm_types.ts src/ts/tests/*.ts",
155
163
  "audit": "text_audit -r -t major MAJOR wasteful WASTEFUL any mixed fixme FIXME checkme CHECKME testme TESTME stochable STOCHABLE todo TODO comeback COMEBACK whargarbl WHARGARBL -g ./src/ts/**/*.{js,ts}",
156
164
  "vet": "npm run eslint && npm run audit",
@@ -227,8 +235,11 @@
227
235
  },
228
236
  "homepage": "https://stonecypher.github.io/jssm/",
229
237
  "devDependencies": {
238
+ "@codemirror/language": "^6.12.3",
239
+ "@codemirror/state": "^6.6.0",
230
240
  "@custom-elements-manifest/analyzer": "^0.10.4",
231
241
  "@knodes/typedoc-plugin-pages": "^0.22.5",
242
+ "@lezer/highlight": "^1.2.3",
232
243
  "@playwright/test": "^1.61.0",
233
244
  "@rollup/plugin-commonjs": "^28.0.1",
234
245
  "@rollup/plugin-node-resolve": "^15.3.0",
@@ -278,11 +289,23 @@
278
289
  "reduce-to-639-1": "^1.1.0"
279
290
  },
280
291
  "peerDependencies": {
281
- "lit": ">=3"
292
+ "lit": ">=3",
293
+ "@codemirror/language": ">=6",
294
+ "@codemirror/state": ">=6",
295
+ "@lezer/highlight": ">=1"
282
296
  },
283
297
  "peerDependenciesMeta": {
284
298
  "lit": {
285
299
  "optional": true
300
+ },
301
+ "@codemirror/language": {
302
+ "optional": true
303
+ },
304
+ "@codemirror/state": {
305
+ "optional": true
306
+ },
307
+ "@lezer/highlight": {
308
+ "optional": true
286
309
  }
287
310
  },
288
311
  "optionalDependencies": {