jssm 5.162.34 → 5.162.35
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 +2 -2
- package/dist/cdn/instance.js +1 -1
- package/dist/cdn/viz.js +1 -1
- package/dist/cli/fsl-export-system-prompt.cjs +1 -1
- package/dist/cli/fsl-render.cjs +1 -1
- package/dist/cli/fsl.cjs +1 -1
- package/dist/cli/lib.cjs +1 -1
- package/dist/cli/lib.mjs +1 -1
- package/dist/deno/README.md +2 -2
- package/dist/deno/jssm.d.ts +127 -1
- package/dist/deno/jssm.js +1 -1
- package/dist/fence/fence.js +316 -183
- package/dist/jssm.es5.cjs +1 -1
- package/dist/jssm.es5.iife.js +1 -1
- package/dist/jssm.es6.mjs +1 -1
- package/dist/jssm_viz.cjs +1 -1
- package/dist/jssm_viz.iife.cjs +1 -1
- package/dist/jssm_viz.mjs +1 -1
- package/jssm.es5.d.cts +126 -0
- package/jssm.es6.d.ts +126 -0
- package/jssm.fence.d.ts +126 -0
- package/jssm_viz.es5.d.cts +126 -0
- package/jssm_viz.es6.d.ts +126 -0
- package/package.json +1 -1
package/dist/fence/fence.js
CHANGED
|
@@ -24454,7 +24454,7 @@ function fslSemanticSpans(text) {
|
|
|
24454
24454
|
* Useful for runtime diagnostics and for embedding in serialized machine
|
|
24455
24455
|
* snapshots so that deserializers can detect version-skew.
|
|
24456
24456
|
*/
|
|
24457
|
-
const version = "5.162.
|
|
24457
|
+
const version = "5.162.35";
|
|
24458
24458
|
|
|
24459
24459
|
/**
|
|
24460
24460
|
* The FSL Markdown fence convention parser — pure, host-agnostic logic that
|
|
@@ -24620,7 +24620,7 @@ var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) ||
|
|
|
24620
24620
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
24621
24621
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
24622
24622
|
};
|
|
24623
|
-
var _Machine_instances, _Machine_unsubscribe_entry, _Machine_subscribe,
|
|
24623
|
+
var _Machine_instances, _Machine_unsubscribe_entry, _Machine_subscribe, _Machine_validate_hook_description, _Machine_recompute_hook_flags, _Machine_resolved_themes, _Machine_individual_state_config, _Machine_groups_by_depth, _Machine_compose_state_config;
|
|
24624
24624
|
const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
|
|
24625
24625
|
const empty_string_set = new Set();
|
|
24626
24626
|
// The spatial fields (besides `handler`, which every hook needs) that each
|
|
@@ -25620,7 +25620,7 @@ class Machine {
|
|
|
25620
25620
|
const oldData = this._data;
|
|
25621
25621
|
this._data = newData;
|
|
25622
25622
|
if (oldData !== newData) {
|
|
25623
|
-
|
|
25623
|
+
this._fire('data-change', {
|
|
25624
25624
|
from: this._state,
|
|
25625
25625
|
to: this._state,
|
|
25626
25626
|
old_data: oldData,
|
|
@@ -27216,6 +27216,127 @@ class Machine {
|
|
|
27216
27216
|
}
|
|
27217
27217
|
return false;
|
|
27218
27218
|
}
|
|
27219
|
+
/**
|
|
27220
|
+
* Invoke a single event-handler entry, respecting its filter, once-removal
|
|
27221
|
+
* semantics, and the error re-fire / recursion-guard logic. Extracted so
|
|
27222
|
+
* {@link _fire} can share identical behavior between the size-1 fast-path
|
|
27223
|
+
* and the general snapshotted loop.
|
|
27224
|
+
* @param entry - The subscriber descriptor to invoke.
|
|
27225
|
+
* @param set - The live Set that owns `entry`; needed for once-removal.
|
|
27226
|
+
* @param name - The event name being dispatched (used in error re-fires).
|
|
27227
|
+
* @param detail - The event payload forwarded to the handler.
|
|
27228
|
+
* @internal
|
|
27229
|
+
*/
|
|
27230
|
+
// PERF: this and the sibling dispatch methods (_fire, _fire_boundary_actions,
|
|
27231
|
+
// _fire_hook_rejection, _has_subscribers) are intentionally underscore-
|
|
27232
|
+
// convention, NOT `#`-private. They are called on the per-transition hot
|
|
27233
|
+
// path (_fire_boundary_actions runs on every transition), and a `#`-private
|
|
27234
|
+
// method cannot be inlined the way its `_` twin can (brand check), so
|
|
27235
|
+
// privatizing them in 5.162.8 cost ~20-25% on transition/action dispatch.
|
|
27236
|
+
// Do not re-privatize. StoneCypher/fsl#1959
|
|
27237
|
+
_fire_one(entry, set, name, detail) {
|
|
27238
|
+
// filter check
|
|
27239
|
+
if (entry.filter !== undefined) {
|
|
27240
|
+
for (const [k, v] of Object.entries(entry.filter)) {
|
|
27241
|
+
if (v !== detail[k]) {
|
|
27242
|
+
return;
|
|
27243
|
+
}
|
|
27244
|
+
}
|
|
27245
|
+
}
|
|
27246
|
+
// once removal happens BEFORE invocation so a throwing handler still
|
|
27247
|
+
// gets removed and so re-entrant `on` calls during the handler see
|
|
27248
|
+
// the post-removal state.
|
|
27249
|
+
if (entry.once) {
|
|
27250
|
+
__classPrivateFieldGet(this, _Machine_instances, "m", _Machine_unsubscribe_entry).call(this, set, entry);
|
|
27251
|
+
}
|
|
27252
|
+
try {
|
|
27253
|
+
entry.handler(detail);
|
|
27254
|
+
}
|
|
27255
|
+
catch (error) {
|
|
27256
|
+
if (name === 'error' || this._firing_error) {
|
|
27257
|
+
// surface to stderr as a last resort but never recurse;
|
|
27258
|
+
// `console` is in the JS standard library and present in every
|
|
27259
|
+
// supported runtime, so guarding it would just add an untestable
|
|
27260
|
+
// branch. See #638.
|
|
27261
|
+
console.error(error);
|
|
27262
|
+
}
|
|
27263
|
+
else {
|
|
27264
|
+
this._firing_error = true;
|
|
27265
|
+
try {
|
|
27266
|
+
this._fire('error', {
|
|
27267
|
+
error: error,
|
|
27268
|
+
source_event: name,
|
|
27269
|
+
source_detail: detail,
|
|
27270
|
+
handler: entry.handler
|
|
27271
|
+
});
|
|
27272
|
+
}
|
|
27273
|
+
finally {
|
|
27274
|
+
this._firing_error = false;
|
|
27275
|
+
}
|
|
27276
|
+
}
|
|
27277
|
+
}
|
|
27278
|
+
}
|
|
27279
|
+
/**
|
|
27280
|
+
* Dispatch an event to every registered subscriber in registration
|
|
27281
|
+
* order. Filters are checked first; non-matching handlers are skipped
|
|
27282
|
+
* without invoking the handler. Exceptions thrown by a handler are
|
|
27283
|
+
* caught and re-emitted as an `error` event so subsequent handlers
|
|
27284
|
+
* still run.
|
|
27285
|
+
*
|
|
27286
|
+
* Re-entry into the `error` event itself is guarded — if an `error`
|
|
27287
|
+
* handler throws, the new exception is swallowed rather than rebroadcast
|
|
27288
|
+
* to avoid an infinite loop.
|
|
27289
|
+
*
|
|
27290
|
+
* When exactly one subscriber is registered the common case avoids the
|
|
27291
|
+
* `Array.from(set)` snapshot allocation by capturing the lone entry into a
|
|
27292
|
+
* local first — equivalent to a 1-element snapshot but allocation-free.
|
|
27293
|
+
* The general path still snapshots for re-entrancy safety.
|
|
27294
|
+
* @internal
|
|
27295
|
+
*/
|
|
27296
|
+
/**
|
|
27297
|
+
* Whether at least one live subscriber is registered for `name`. Used by
|
|
27298
|
+
* the transition-commit observation block to skip building a detail
|
|
27299
|
+
* literal that {@link Machine._fire} would immediately discard — a panel
|
|
27300
|
+
* listening only to `'transition'` (fsl-bind, fsl-viz, fsl-info-panel)
|
|
27301
|
+
* previously paid for the exit/entry/data-change detail allocations on
|
|
27302
|
+
* every transition. Read at fire time, so a listener installed by a
|
|
27303
|
+
* pre-hook is still seen (#671).
|
|
27304
|
+
* @param name The event name to probe.
|
|
27305
|
+
* @returns `true` when a subsequent `_fire(name, ...)` would reach at
|
|
27306
|
+
* least one handler.
|
|
27307
|
+
*
|
|
27308
|
+
* ```typescript
|
|
27309
|
+
* machine.on('transition', () => {});
|
|
27310
|
+
* machine._has_subscribers('transition'); // true
|
|
27311
|
+
* machine._has_subscribers('exit'); // false
|
|
27312
|
+
* ```
|
|
27313
|
+
* @see Machine._fire
|
|
27314
|
+
* @internal
|
|
27315
|
+
*/
|
|
27316
|
+
_has_subscribers(name) {
|
|
27317
|
+
const set = this._event_handlers.get(name);
|
|
27318
|
+
return (set !== undefined) && (set.size > 0);
|
|
27319
|
+
}
|
|
27320
|
+
_fire(name, detail) {
|
|
27321
|
+
const set = this._event_handlers.get(name);
|
|
27322
|
+
if (set === undefined || set.size === 0) {
|
|
27323
|
+
return;
|
|
27324
|
+
}
|
|
27325
|
+
// Fast-path: single subscriber — capture entry before invoking so that
|
|
27326
|
+
// even if the handler mutates `set` (via off/once auto-removal) we hold a
|
|
27327
|
+
// stable reference. Behaviorally identical to a 1-element snapshot.
|
|
27328
|
+
if (set.size === 1) {
|
|
27329
|
+
const only = set.values().next().value;
|
|
27330
|
+
this._fire_one(only, set, name, detail);
|
|
27331
|
+
return;
|
|
27332
|
+
}
|
|
27333
|
+
// General path: snapshot so handlers can `off()` mid-loop without
|
|
27334
|
+
// disturbing iteration.
|
|
27335
|
+
const entries = [...set];
|
|
27336
|
+
for (const entry of entries) {
|
|
27337
|
+
this._fire_one(entry, set, name, detail);
|
|
27338
|
+
}
|
|
27339
|
+
}
|
|
27219
27340
|
set_hook(HookDesc) {
|
|
27220
27341
|
__classPrivateFieldGet(this, _Machine_instances, "m", _Machine_validate_hook_description).call(this, HookDesc);
|
|
27221
27342
|
switch (HookDesc.kind) {
|
|
@@ -27396,7 +27517,7 @@ class Machine {
|
|
|
27396
27517
|
// added after a style has already been computed and memoized.
|
|
27397
27518
|
this._static_state_config_cache.clear();
|
|
27398
27519
|
// fire the registration event for inspector tools (#638)
|
|
27399
|
-
|
|
27520
|
+
this._fire('hook-registration', { description: HookDesc });
|
|
27400
27521
|
}
|
|
27401
27522
|
/**
|
|
27402
27523
|
* Remove a previously-registered hook described by a
|
|
@@ -27599,7 +27720,7 @@ class Machine {
|
|
|
27599
27720
|
// See set_hook: the hooked-state styling layer depends on which states
|
|
27600
27721
|
// carry hooks, so removing one can change a state's composed style.
|
|
27601
27722
|
this._static_state_config_cache.clear();
|
|
27602
|
-
|
|
27723
|
+
this._fire('hook-removal', { description: HookDesc });
|
|
27603
27724
|
}
|
|
27604
27725
|
return removed;
|
|
27605
27726
|
}
|
|
@@ -28046,14 +28167,14 @@ class Machine {
|
|
|
28046
28167
|
if (dataProvided) {
|
|
28047
28168
|
this._data = newData;
|
|
28048
28169
|
}
|
|
28049
|
-
|
|
28170
|
+
this._fire('override', {
|
|
28050
28171
|
from: fromState,
|
|
28051
28172
|
to: newState,
|
|
28052
28173
|
old_data: oldData,
|
|
28053
28174
|
new_data: this._data
|
|
28054
28175
|
});
|
|
28055
28176
|
if (dataProvided && (oldData !== newData)) {
|
|
28056
|
-
|
|
28177
|
+
this._fire('data-change', {
|
|
28057
28178
|
from: fromState,
|
|
28058
28179
|
to: newState,
|
|
28059
28180
|
old_data: oldData,
|
|
@@ -28063,7 +28184,7 @@ class Machine {
|
|
|
28063
28184
|
}
|
|
28064
28185
|
// An override is still a real state change that may cross group/state
|
|
28065
28186
|
// boundaries, so its boundary-hook actions fire too (depth-bounded).
|
|
28066
|
-
|
|
28187
|
+
this._fire_boundary_actions(fromState, newState);
|
|
28067
28188
|
}
|
|
28068
28189
|
else {
|
|
28069
28190
|
throw new JssmError(this, `Cannot override state to "${newState}", a state that does not exist`);
|
|
@@ -28073,6 +28194,164 @@ class Machine {
|
|
|
28073
28194
|
throw new JssmError(this, "Code specifies no override, but config tries to permit; config may not be less strict than code");
|
|
28074
28195
|
}
|
|
28075
28196
|
}
|
|
28197
|
+
/*********
|
|
28198
|
+
*
|
|
28199
|
+
* Fire a `'rejection'` event caused by a hook vetoing a pending transition.
|
|
28200
|
+
* Extracted from the per-call closures inside {@link transition_impl} so
|
|
28201
|
+
* that it is allocated once at class-definition time rather than on every
|
|
28202
|
+
* hooked transition.
|
|
28203
|
+
*
|
|
28204
|
+
* @param hook_name Name of the hook that rejected (e.g. `'exit'`).
|
|
28205
|
+
* @param fromState State the machine was in when the transition was
|
|
28206
|
+
* attempted; used as the `from` field of the rejection event.
|
|
28207
|
+
* @param newState State that would have been entered had the hook
|
|
28208
|
+
* passed; used as the `to` field of the rejection event.
|
|
28209
|
+
* @param fromAction Action name when the transition was initiated by an
|
|
28210
|
+
* action call; `undefined` for plain state transitions.
|
|
28211
|
+
* @param oldData Machine data at the moment the transition was
|
|
28212
|
+
* attempted, before any hook mutations.
|
|
28213
|
+
* @param newData The `next_data` value passed to the transition call.
|
|
28214
|
+
* @param wasForced Whether the transition was attempted via
|
|
28215
|
+
* `force_transition`.
|
|
28216
|
+
*
|
|
28217
|
+
* @see transition_impl
|
|
28218
|
+
* @see _fire
|
|
28219
|
+
*
|
|
28220
|
+
* @internal
|
|
28221
|
+
*
|
|
28222
|
+
*/
|
|
28223
|
+
_fire_hook_rejection(hook_name, fromState, newState, fromAction, oldData, newData, wasForced) {
|
|
28224
|
+
// Every hook veto in transition_impl's pre-commit pipeline exits through
|
|
28225
|
+
// here, so this is the single close point for the reentrancy guard on the
|
|
28226
|
+
// rejection path: clear it before firing the event so a `rejection` listener
|
|
28227
|
+
// may itself transition (the outer transition is abandoned, not reverted).
|
|
28228
|
+
// #1953
|
|
28229
|
+
this._committing_transition = false;
|
|
28230
|
+
this._fire('rejection', {
|
|
28231
|
+
from: fromState,
|
|
28232
|
+
to: newState,
|
|
28233
|
+
action: fromAction,
|
|
28234
|
+
data: oldData,
|
|
28235
|
+
next_data: newData,
|
|
28236
|
+
reason: 'hook',
|
|
28237
|
+
hook_name,
|
|
28238
|
+
forced: wasForced
|
|
28239
|
+
});
|
|
28240
|
+
}
|
|
28241
|
+
/*********
|
|
28242
|
+
*
|
|
28243
|
+
* Fire the FSL boundary-hook actions for a single, already-committed state
|
|
28244
|
+
* change. In FSL, `do` is a synonym for `action`, so `on enter &g do 'X';`
|
|
28245
|
+
* means "when the machine crosses INTO group `g`, dispatch machine action
|
|
28246
|
+
* `X`" — and likewise `on exit` / plain-state subjects. This is the runtime
|
|
28247
|
+
* that fires those parked hooks.
|
|
28248
|
+
*
|
|
28249
|
+
* Crossing semantics (statechart convention — exits before enters):
|
|
28250
|
+
*
|
|
28251
|
+
* 1. `prev_groups` / `next_groups` are the deep (transitive) group sets of
|
|
28252
|
+
* the old and new states, from `_state_to_groups`.
|
|
28253
|
+
* 2. **Exits** fire first: every group in `prev_groups \ next_groups` with an
|
|
28254
|
+
* `onExit`, plus the plain `prev_state`'s `onExit` (when the state name
|
|
28255
|
+
* actually changed).
|
|
28256
|
+
* 3. **Enters** fire next: every group in `next_groups \ prev_groups` with an
|
|
28257
|
+
* `onEnter`, plus the plain `next_state`'s `onEnter` (when the state name
|
|
28258
|
+
* changed).
|
|
28259
|
+
* 4. A group present in BOTH sets is a transition *within* that group and
|
|
28260
|
+
* fires neither of its boundary hooks. `prev_state === next_state` fires
|
|
28261
|
+
* nothing at all.
|
|
28262
|
+
* 5. "Fire its action" is `this.action(label)`. If that action is not valid
|
|
28263
|
+
* from the current state, `action` is a safe no-op (returns `false`) — an
|
|
28264
|
+
* inapplicable boundary action never throws.
|
|
28265
|
+
* 6. Multi-membership and nesting both fan out naturally: a state in groups
|
|
28266
|
+
* A and B fires both; crossing an inner and an outer boundary fires both
|
|
28267
|
+
* levels.
|
|
28268
|
+
*
|
|
28269
|
+
* Because firing an action can drive a further transition (which crosses
|
|
28270
|
+
* more boundaries, which fires more actions), this is a bounded
|
|
28271
|
+
* run-to-completion: `_boundary_depth` tracks the live cascade depth and a
|
|
28272
|
+
* cascade deeper than `_boundary_depth_limit` throws a {@link JssmError}
|
|
28273
|
+
* rather than overflowing the stack or hanging. The limit defaults to 100
|
|
28274
|
+
* and is configurable via the `boundary_depth_limit` constructor option.
|
|
28275
|
+
*
|
|
28276
|
+
* @param prev_state The state the machine was in before this commit.
|
|
28277
|
+
* @param next_state The state the machine is in now (already committed).
|
|
28278
|
+
*
|
|
28279
|
+
* @throws {JssmError} If cascaded boundary firing exceeds `_boundary_depth_limit`
|
|
28280
|
+
* (a probable infinite loop).
|
|
28281
|
+
*
|
|
28282
|
+
* @see action
|
|
28283
|
+
* @see transition_impl
|
|
28284
|
+
*
|
|
28285
|
+
* @internal
|
|
28286
|
+
*
|
|
28287
|
+
*/
|
|
28288
|
+
_fire_boundary_actions(prev_state, next_state) {
|
|
28289
|
+
var _a, _b, _c, _d, _e, _f;
|
|
28290
|
+
// Nothing crosses a boundary when the state name is unchanged.
|
|
28291
|
+
if (prev_state === next_state) {
|
|
28292
|
+
return;
|
|
28293
|
+
}
|
|
28294
|
+
// Skip entirely for machines that declared no boundary hooks at all — the
|
|
28295
|
+
// overwhelming common case, and it keeps the hot transition path free of
|
|
28296
|
+
// set arithmetic.
|
|
28297
|
+
if (this._group_hooks.size === 0 && this._state_hooks.size === 0) {
|
|
28298
|
+
return;
|
|
28299
|
+
}
|
|
28300
|
+
if (this._boundary_depth >= this._boundary_depth_limit) {
|
|
28301
|
+
throw new JssmError(this, `boundary-hook action cascade exceeded depth limit (${this._boundary_depth_limit}) `
|
|
28302
|
+
+ `crossing from ${JSON.stringify(prev_state)} to ${JSON.stringify(next_state)} `
|
|
28303
|
+
+ `(possible infinite loop)`);
|
|
28304
|
+
}
|
|
28305
|
+
const prev_groups = (_a = this._state_to_groups.get(prev_state)) !== null && _a !== void 0 ? _a : empty_string_set;
|
|
28306
|
+
const next_groups = (_b = this._state_to_groups.get(next_state)) !== null && _b !== void 0 ? _b : empty_string_set;
|
|
28307
|
+
// The labels to dispatch, gathered before any firing so that re-entrant
|
|
28308
|
+
// transitions caused by an early action cannot perturb which boundaries the
|
|
28309
|
+
// *current* crossing fires. Exits precede enters (statechart convention).
|
|
28310
|
+
const labels = [];
|
|
28311
|
+
// Exits: groups left (in prev but not next), then the plain prev state.
|
|
28312
|
+
for (const group of prev_groups) {
|
|
28313
|
+
if (next_groups.has(group)) {
|
|
28314
|
+
continue;
|
|
28315
|
+
}
|
|
28316
|
+
const label = (_c = this._group_hooks.get(group)) === null || _c === void 0 ? void 0 : _c.onExit;
|
|
28317
|
+
if (label !== undefined) {
|
|
28318
|
+
labels.push(label);
|
|
28319
|
+
}
|
|
28320
|
+
}
|
|
28321
|
+
const prev_state_exit = (_d = this._state_hooks.get(prev_state)) === null || _d === void 0 ? void 0 : _d.onExit;
|
|
28322
|
+
if (prev_state_exit !== undefined) {
|
|
28323
|
+
labels.push(prev_state_exit);
|
|
28324
|
+
}
|
|
28325
|
+
// Enters: groups entered (in next but not prev), then the plain next state.
|
|
28326
|
+
for (const group of next_groups) {
|
|
28327
|
+
if (prev_groups.has(group)) {
|
|
28328
|
+
continue;
|
|
28329
|
+
}
|
|
28330
|
+
const label = (_e = this._group_hooks.get(group)) === null || _e === void 0 ? void 0 : _e.onEnter;
|
|
28331
|
+
if (label !== undefined) {
|
|
28332
|
+
labels.push(label);
|
|
28333
|
+
}
|
|
28334
|
+
}
|
|
28335
|
+
const next_state_enter = (_f = this._state_hooks.get(next_state)) === null || _f === void 0 ? void 0 : _f.onEnter;
|
|
28336
|
+
if (next_state_enter !== undefined) {
|
|
28337
|
+
labels.push(next_state_enter);
|
|
28338
|
+
}
|
|
28339
|
+
if (labels.length === 0) {
|
|
28340
|
+
return;
|
|
28341
|
+
}
|
|
28342
|
+
// Each dispatched action re-enters transition_impl, which (on success) calls
|
|
28343
|
+
// back here for the boundary it just crossed. The depth counter brackets
|
|
28344
|
+
// the whole fan-out so a self-perpetuating cascade is bounded, not infinite.
|
|
28345
|
+
this._boundary_depth += 1;
|
|
28346
|
+
try {
|
|
28347
|
+
for (const label of labels) {
|
|
28348
|
+
this.action(label); // safe no-op (returns false) if inapplicable here
|
|
28349
|
+
}
|
|
28350
|
+
}
|
|
28351
|
+
finally {
|
|
28352
|
+
this._boundary_depth -= 1;
|
|
28353
|
+
}
|
|
28354
|
+
}
|
|
28076
28355
|
/*********
|
|
28077
28356
|
*
|
|
28078
28357
|
* Shared transition core used by {@link transition}, {@link force_transition},
|
|
@@ -28230,7 +28509,7 @@ class Machine {
|
|
|
28230
28509
|
// when nothing is subscribed. Gate is read at fire time, so a listener
|
|
28231
28510
|
// registered inside a pre-hook still receives the event. #671
|
|
28232
28511
|
if (wasAction && this._event_listener_count !== 0) {
|
|
28233
|
-
|
|
28512
|
+
this._fire('action', {
|
|
28234
28513
|
action: newStateOrAction,
|
|
28235
28514
|
from: this._state,
|
|
28236
28515
|
to: newState,
|
|
@@ -28250,7 +28529,7 @@ class Machine {
|
|
|
28250
28529
|
// Open the pre-commit window: from here until the commit below, any
|
|
28251
28530
|
// reentrant transition_impl call (a hook transitioning the machine)
|
|
28252
28531
|
// throws instead of being silently reverted. The `finally` below closes
|
|
28253
|
-
// it on every exit path;
|
|
28532
|
+
// it on every exit path; _fire_hook_rejection additionally clears it
|
|
28254
28533
|
// before firing the rejection event so a rejection listener may itself
|
|
28255
28534
|
// transition. The pipeline body is intentionally left at its original
|
|
28256
28535
|
// indentation to keep this fix's diff focused. #1953
|
|
@@ -28261,7 +28540,7 @@ class Machine {
|
|
|
28261
28540
|
if (this._pre_everything_hook !== undefined) {
|
|
28262
28541
|
const outcome = abstract_everything_hook_step(this._pre_everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'pre everything' }));
|
|
28263
28542
|
if (!outcome.pass) {
|
|
28264
|
-
|
|
28543
|
+
this._fire_hook_rejection('pre everything', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28265
28544
|
return false;
|
|
28266
28545
|
}
|
|
28267
28546
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28272,7 +28551,7 @@ class Machine {
|
|
|
28272
28551
|
// 1a. any action hook
|
|
28273
28552
|
const outcome = abstract_hook_step(this._any_action_hook, hook_args);
|
|
28274
28553
|
if (!outcome.pass) {
|
|
28275
|
-
|
|
28554
|
+
this._fire_hook_rejection('any action', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28276
28555
|
return false;
|
|
28277
28556
|
}
|
|
28278
28557
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28281,7 +28560,7 @@ class Machine {
|
|
|
28281
28560
|
// 1b. global specific action hook
|
|
28282
28561
|
const outcome2 = abstract_hook_step(this._global_action_hooks.get(actionId), hook_args);
|
|
28283
28562
|
if (!outcome2.pass) {
|
|
28284
|
-
|
|
28563
|
+
this._fire_hook_rejection('global action', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28285
28564
|
return false;
|
|
28286
28565
|
}
|
|
28287
28566
|
if (_update_hook_fields(hook_args, outcome2)) {
|
|
@@ -28298,7 +28577,7 @@ class Machine {
|
|
|
28298
28577
|
if (this._any_transition_hook !== undefined) {
|
|
28299
28578
|
const outcome = abstract_hook_step(this._any_transition_hook, hook_args);
|
|
28300
28579
|
if (!outcome.pass) {
|
|
28301
|
-
|
|
28580
|
+
this._fire_hook_rejection('any transition', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28302
28581
|
return false;
|
|
28303
28582
|
}
|
|
28304
28583
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28309,7 +28588,7 @@ class Machine {
|
|
|
28309
28588
|
if (this._has_exit_hooks) {
|
|
28310
28589
|
const outcome = abstract_hook_step(this._exit_hooks.get(this._state_id), hook_args);
|
|
28311
28590
|
if (!outcome.pass) {
|
|
28312
|
-
|
|
28591
|
+
this._fire_hook_rejection('exit', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28313
28592
|
return false;
|
|
28314
28593
|
}
|
|
28315
28594
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28326,7 +28605,7 @@ class Machine {
|
|
|
28326
28605
|
const nh = byPair === undefined ? undefined : byPair.get(actionId);
|
|
28327
28606
|
const outcome = abstract_hook_step(nh, hook_args);
|
|
28328
28607
|
if (!outcome.pass) {
|
|
28329
|
-
|
|
28608
|
+
this._fire_hook_rejection('named', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28330
28609
|
return false;
|
|
28331
28610
|
}
|
|
28332
28611
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28339,7 +28618,7 @@ class Machine {
|
|
|
28339
28618
|
const h = this._hooks.get(pre_pair_id);
|
|
28340
28619
|
const outcome = abstract_hook_step(h, hook_args);
|
|
28341
28620
|
if (!outcome.pass) {
|
|
28342
|
-
|
|
28621
|
+
this._fire_hook_rejection('hook', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28343
28622
|
return false;
|
|
28344
28623
|
}
|
|
28345
28624
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28351,7 +28630,7 @@ class Machine {
|
|
|
28351
28630
|
if (trans_type === 'legal') {
|
|
28352
28631
|
const outcome = abstract_hook_step(this._standard_transition_hook, hook_args);
|
|
28353
28632
|
if (!outcome.pass) {
|
|
28354
|
-
|
|
28633
|
+
this._fire_hook_rejection('standard transition', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28355
28634
|
return false;
|
|
28356
28635
|
}
|
|
28357
28636
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28362,7 +28641,7 @@ class Machine {
|
|
|
28362
28641
|
else if (trans_type === 'main') {
|
|
28363
28642
|
const outcome = abstract_hook_step(this._main_transition_hook, hook_args);
|
|
28364
28643
|
if (!outcome.pass) {
|
|
28365
|
-
|
|
28644
|
+
this._fire_hook_rejection('main transition', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28366
28645
|
return false;
|
|
28367
28646
|
}
|
|
28368
28647
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28373,7 +28652,7 @@ class Machine {
|
|
|
28373
28652
|
else if (trans_type === 'forced') {
|
|
28374
28653
|
const outcome = abstract_hook_step(this._forced_transition_hook, hook_args);
|
|
28375
28654
|
if (!outcome.pass) {
|
|
28376
|
-
|
|
28655
|
+
this._fire_hook_rejection('forced transition', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28377
28656
|
return false;
|
|
28378
28657
|
}
|
|
28379
28658
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28384,7 +28663,7 @@ class Machine {
|
|
|
28384
28663
|
if (this._has_entry_hooks) {
|
|
28385
28664
|
const outcome = abstract_hook_step(this._entry_hooks.get(newStateId), hook_args);
|
|
28386
28665
|
if (!outcome.pass) {
|
|
28387
|
-
|
|
28666
|
+
this._fire_hook_rejection('entry', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28388
28667
|
return false;
|
|
28389
28668
|
}
|
|
28390
28669
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28395,7 +28674,7 @@ class Machine {
|
|
|
28395
28674
|
if (this._everything_hook !== undefined) {
|
|
28396
28675
|
const outcome = abstract_everything_hook_step(this._everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'everything' }));
|
|
28397
28676
|
if (!outcome.pass) {
|
|
28398
|
-
|
|
28677
|
+
this._fire_hook_rejection('everything', fromState, newState, fromAction, oldData, newData, wasForced);
|
|
28399
28678
|
return false;
|
|
28400
28679
|
}
|
|
28401
28680
|
if (_update_hook_fields(hook_args, outcome)) {
|
|
@@ -28462,7 +28741,7 @@ class Machine {
|
|
|
28462
28741
|
// when nothing is subscribed. A listener still receives the event
|
|
28463
28742
|
// because the gate is read at fire time. #671
|
|
28464
28743
|
if (this._event_listener_count !== 0) {
|
|
28465
|
-
|
|
28744
|
+
this._fire('rejection', {
|
|
28466
28745
|
from: fromState,
|
|
28467
28746
|
to: newStateOrAction, // we never resolved a real target
|
|
28468
28747
|
action: fromAction,
|
|
@@ -28563,16 +28842,16 @@ class Machine {
|
|
|
28563
28842
|
// listening only to 'transition' previously paid for the exit/entry/
|
|
28564
28843
|
// data-change/terminal/complete allocations _fire then discarded.
|
|
28565
28844
|
// Gates read at fire time, like the outer count, preserving #671.
|
|
28566
|
-
if (
|
|
28567
|
-
|
|
28845
|
+
if (this._has_subscribers('exit')) {
|
|
28846
|
+
this._fire('exit', {
|
|
28568
28847
|
state: fromState,
|
|
28569
28848
|
to: newState,
|
|
28570
28849
|
action: fromAction,
|
|
28571
28850
|
data: newData_after
|
|
28572
28851
|
});
|
|
28573
28852
|
}
|
|
28574
|
-
if (
|
|
28575
|
-
|
|
28853
|
+
if (this._has_subscribers('transition')) {
|
|
28854
|
+
this._fire('transition', {
|
|
28576
28855
|
from: fromState,
|
|
28577
28856
|
to: newState,
|
|
28578
28857
|
action: fromAction,
|
|
@@ -28582,16 +28861,16 @@ class Machine {
|
|
|
28582
28861
|
forced: wasForced
|
|
28583
28862
|
});
|
|
28584
28863
|
}
|
|
28585
|
-
if (
|
|
28586
|
-
|
|
28864
|
+
if (this._has_subscribers('entry')) {
|
|
28865
|
+
this._fire('entry', {
|
|
28587
28866
|
state: newState,
|
|
28588
28867
|
from: fromState,
|
|
28589
28868
|
action: fromAction,
|
|
28590
28869
|
data: newData_after
|
|
28591
28870
|
});
|
|
28592
28871
|
}
|
|
28593
|
-
if ((oldData !== newData_after) &&
|
|
28594
|
-
|
|
28872
|
+
if ((oldData !== newData_after) && this._has_subscribers('data-change')) {
|
|
28873
|
+
this._fire('data-change', {
|
|
28595
28874
|
from: fromState,
|
|
28596
28875
|
to: newState,
|
|
28597
28876
|
action: fromAction,
|
|
@@ -28605,18 +28884,18 @@ class Machine {
|
|
|
28605
28884
|
// each redo has_state plus its own map walk. Same predicates:
|
|
28606
28885
|
// terminal = no exits, complete = the constructor-set flag. #735
|
|
28607
28886
|
const new_state_rec = this._states.get(newState);
|
|
28608
|
-
if ((new_state_rec.to.length === 0) &&
|
|
28609
|
-
|
|
28887
|
+
if ((new_state_rec.to.length === 0) && this._has_subscribers('terminal')) {
|
|
28888
|
+
this._fire('terminal', { state: newState, data: newData_after });
|
|
28610
28889
|
}
|
|
28611
|
-
if (new_state_rec.complete &&
|
|
28612
|
-
|
|
28890
|
+
if (new_state_rec.complete && this._has_subscribers('complete')) {
|
|
28891
|
+
this._fire('complete', { state: newState, data: newData_after });
|
|
28613
28892
|
}
|
|
28614
28893
|
}
|
|
28615
28894
|
// FSL boundary-hook actions (`on enter/exit &g do 'X'`) fire after the
|
|
28616
28895
|
// state is committed and after the observation events, matching the
|
|
28617
28896
|
// statechart "exits before enters" convention. Cascades are depth-bounded
|
|
28618
28897
|
// inside the helper.
|
|
28619
|
-
|
|
28898
|
+
this._fire_boundary_actions(fromState, newState);
|
|
28620
28899
|
// Clear the departed state's `after` timer and re-establish the new state's,
|
|
28621
28900
|
// now that the transition has actually committed. This clear runs only on a
|
|
28622
28901
|
// successful commit -- a hook that VETOES the transition returns above, so
|
|
@@ -29574,7 +29853,7 @@ class Machine {
|
|
|
29574
29853
|
this._after_any_hook({ data: this._data, next_data: this._data });
|
|
29575
29854
|
}
|
|
29576
29855
|
}
|
|
29577
|
-
|
|
29856
|
+
this._fire('timeout', { from: from_state, to: next_state, after_time });
|
|
29578
29857
|
this.go(next_state);
|
|
29579
29858
|
}, after_time);
|
|
29580
29859
|
this._timeout_target = next_state;
|
|
@@ -29648,69 +29927,6 @@ _Machine_instances = new WeakSet(), _Machine_unsubscribe_entry = function _Machi
|
|
|
29648
29927
|
set.add(entry);
|
|
29649
29928
|
this._event_listener_count++;
|
|
29650
29929
|
return () => { __classPrivateFieldGet(this, _Machine_instances, "m", _Machine_unsubscribe_entry).call(this, set, entry); };
|
|
29651
|
-
}, _Machine_fire_one = function _Machine_fire_one(entry, set, name, detail) {
|
|
29652
|
-
// filter check
|
|
29653
|
-
if (entry.filter !== undefined) {
|
|
29654
|
-
for (const [k, v] of Object.entries(entry.filter)) {
|
|
29655
|
-
if (v !== detail[k]) {
|
|
29656
|
-
return;
|
|
29657
|
-
}
|
|
29658
|
-
}
|
|
29659
|
-
}
|
|
29660
|
-
// once removal happens BEFORE invocation so a throwing handler still
|
|
29661
|
-
// gets removed and so re-entrant `on` calls during the handler see
|
|
29662
|
-
// the post-removal state.
|
|
29663
|
-
if (entry.once) {
|
|
29664
|
-
__classPrivateFieldGet(this, _Machine_instances, "m", _Machine_unsubscribe_entry).call(this, set, entry);
|
|
29665
|
-
}
|
|
29666
|
-
try {
|
|
29667
|
-
entry.handler(detail);
|
|
29668
|
-
}
|
|
29669
|
-
catch (error) {
|
|
29670
|
-
if (name === 'error' || this._firing_error) {
|
|
29671
|
-
// surface to stderr as a last resort but never recurse;
|
|
29672
|
-
// `console` is in the JS standard library and present in every
|
|
29673
|
-
// supported runtime, so guarding it would just add an untestable
|
|
29674
|
-
// branch. See #638.
|
|
29675
|
-
console.error(error);
|
|
29676
|
-
}
|
|
29677
|
-
else {
|
|
29678
|
-
this._firing_error = true;
|
|
29679
|
-
try {
|
|
29680
|
-
__classPrivateFieldGet(this, _Machine_instances, "m", _Machine_fire).call(this, 'error', {
|
|
29681
|
-
error: error,
|
|
29682
|
-
source_event: name,
|
|
29683
|
-
source_detail: detail,
|
|
29684
|
-
handler: entry.handler
|
|
29685
|
-
});
|
|
29686
|
-
}
|
|
29687
|
-
finally {
|
|
29688
|
-
this._firing_error = false;
|
|
29689
|
-
}
|
|
29690
|
-
}
|
|
29691
|
-
}
|
|
29692
|
-
}, _Machine_has_subscribers = function _Machine_has_subscribers(name) {
|
|
29693
|
-
const set = this._event_handlers.get(name);
|
|
29694
|
-
return (set !== undefined) && (set.size > 0);
|
|
29695
|
-
}, _Machine_fire = function _Machine_fire(name, detail) {
|
|
29696
|
-
const set = this._event_handlers.get(name);
|
|
29697
|
-
if (set === undefined || set.size === 0) {
|
|
29698
|
-
return;
|
|
29699
|
-
}
|
|
29700
|
-
// Fast-path: single subscriber — capture entry before invoking so that
|
|
29701
|
-
// even if the handler mutates `set` (via off/once auto-removal) we hold a
|
|
29702
|
-
// stable reference. Behaviorally identical to a 1-element snapshot.
|
|
29703
|
-
if (set.size === 1) {
|
|
29704
|
-
const only = set.values().next().value;
|
|
29705
|
-
__classPrivateFieldGet(this, _Machine_instances, "m", _Machine_fire_one).call(this, only, set, name, detail);
|
|
29706
|
-
return;
|
|
29707
|
-
}
|
|
29708
|
-
// General path: snapshot so handlers can `off()` mid-loop without
|
|
29709
|
-
// disturbing iteration.
|
|
29710
|
-
const entries = [...set];
|
|
29711
|
-
for (const entry of entries) {
|
|
29712
|
-
__classPrivateFieldGet(this, _Machine_instances, "m", _Machine_fire_one).call(this, entry, set, name, detail);
|
|
29713
|
-
}
|
|
29714
29930
|
}, _Machine_validate_hook_description = function _Machine_validate_hook_description(HookDesc) {
|
|
29715
29931
|
const required = hook_required_fields[HookDesc.kind];
|
|
29716
29932
|
if (required === undefined) {
|
|
@@ -29782,89 +29998,6 @@ _Machine_instances = new WeakSet(), _Machine_unsubscribe_entry = function _Machi
|
|
|
29782
29998
|
this._pre_post_everything_hook !== undefined,
|
|
29783
29999
|
this._post_everything_hook !== undefined,
|
|
29784
30000
|
].includes(true);
|
|
29785
|
-
}, _Machine_fire_hook_rejection = function _Machine_fire_hook_rejection(hook_name, fromState, newState, fromAction, oldData, newData, wasForced) {
|
|
29786
|
-
// Every hook veto in transition_impl's pre-commit pipeline exits through
|
|
29787
|
-
// here, so this is the single close point for the reentrancy guard on the
|
|
29788
|
-
// rejection path: clear it before firing the event so a `rejection` listener
|
|
29789
|
-
// may itself transition (the outer transition is abandoned, not reverted).
|
|
29790
|
-
// #1953
|
|
29791
|
-
this._committing_transition = false;
|
|
29792
|
-
__classPrivateFieldGet(this, _Machine_instances, "m", _Machine_fire).call(this, 'rejection', {
|
|
29793
|
-
from: fromState,
|
|
29794
|
-
to: newState,
|
|
29795
|
-
action: fromAction,
|
|
29796
|
-
data: oldData,
|
|
29797
|
-
next_data: newData,
|
|
29798
|
-
reason: 'hook',
|
|
29799
|
-
hook_name,
|
|
29800
|
-
forced: wasForced
|
|
29801
|
-
});
|
|
29802
|
-
}, _Machine_fire_boundary_actions = function _Machine_fire_boundary_actions(prev_state, next_state) {
|
|
29803
|
-
var _a, _b, _c, _d, _e, _f;
|
|
29804
|
-
// Nothing crosses a boundary when the state name is unchanged.
|
|
29805
|
-
if (prev_state === next_state) {
|
|
29806
|
-
return;
|
|
29807
|
-
}
|
|
29808
|
-
// Skip entirely for machines that declared no boundary hooks at all — the
|
|
29809
|
-
// overwhelming common case, and it keeps the hot transition path free of
|
|
29810
|
-
// set arithmetic.
|
|
29811
|
-
if (this._group_hooks.size === 0 && this._state_hooks.size === 0) {
|
|
29812
|
-
return;
|
|
29813
|
-
}
|
|
29814
|
-
if (this._boundary_depth >= this._boundary_depth_limit) {
|
|
29815
|
-
throw new JssmError(this, `boundary-hook action cascade exceeded depth limit (${this._boundary_depth_limit}) `
|
|
29816
|
-
+ `crossing from ${JSON.stringify(prev_state)} to ${JSON.stringify(next_state)} `
|
|
29817
|
-
+ `(possible infinite loop)`);
|
|
29818
|
-
}
|
|
29819
|
-
const prev_groups = (_a = this._state_to_groups.get(prev_state)) !== null && _a !== void 0 ? _a : empty_string_set;
|
|
29820
|
-
const next_groups = (_b = this._state_to_groups.get(next_state)) !== null && _b !== void 0 ? _b : empty_string_set;
|
|
29821
|
-
// The labels to dispatch, gathered before any firing so that re-entrant
|
|
29822
|
-
// transitions caused by an early action cannot perturb which boundaries the
|
|
29823
|
-
// *current* crossing fires. Exits precede enters (statechart convention).
|
|
29824
|
-
const labels = [];
|
|
29825
|
-
// Exits: groups left (in prev but not next), then the plain prev state.
|
|
29826
|
-
for (const group of prev_groups) {
|
|
29827
|
-
if (next_groups.has(group)) {
|
|
29828
|
-
continue;
|
|
29829
|
-
}
|
|
29830
|
-
const label = (_c = this._group_hooks.get(group)) === null || _c === void 0 ? void 0 : _c.onExit;
|
|
29831
|
-
if (label !== undefined) {
|
|
29832
|
-
labels.push(label);
|
|
29833
|
-
}
|
|
29834
|
-
}
|
|
29835
|
-
const prev_state_exit = (_d = this._state_hooks.get(prev_state)) === null || _d === void 0 ? void 0 : _d.onExit;
|
|
29836
|
-
if (prev_state_exit !== undefined) {
|
|
29837
|
-
labels.push(prev_state_exit);
|
|
29838
|
-
}
|
|
29839
|
-
// Enters: groups entered (in next but not prev), then the plain next state.
|
|
29840
|
-
for (const group of next_groups) {
|
|
29841
|
-
if (prev_groups.has(group)) {
|
|
29842
|
-
continue;
|
|
29843
|
-
}
|
|
29844
|
-
const label = (_e = this._group_hooks.get(group)) === null || _e === void 0 ? void 0 : _e.onEnter;
|
|
29845
|
-
if (label !== undefined) {
|
|
29846
|
-
labels.push(label);
|
|
29847
|
-
}
|
|
29848
|
-
}
|
|
29849
|
-
const next_state_enter = (_f = this._state_hooks.get(next_state)) === null || _f === void 0 ? void 0 : _f.onEnter;
|
|
29850
|
-
if (next_state_enter !== undefined) {
|
|
29851
|
-
labels.push(next_state_enter);
|
|
29852
|
-
}
|
|
29853
|
-
if (labels.length === 0) {
|
|
29854
|
-
return;
|
|
29855
|
-
}
|
|
29856
|
-
// Each dispatched action re-enters transition_impl, which (on success) calls
|
|
29857
|
-
// back here for the boundary it just crossed. The depth counter brackets
|
|
29858
|
-
// the whole fan-out so a self-perpetuating cascade is bounded, not infinite.
|
|
29859
|
-
this._boundary_depth += 1;
|
|
29860
|
-
try {
|
|
29861
|
-
for (const label of labels) {
|
|
29862
|
-
this.action(label); // safe no-op (returns false) if inapplicable here
|
|
29863
|
-
}
|
|
29864
|
-
}
|
|
29865
|
-
finally {
|
|
29866
|
-
this._boundary_depth -= 1;
|
|
29867
|
-
}
|
|
29868
30001
|
}, _Machine_resolved_themes = function _Machine_resolved_themes() {
|
|
29869
30002
|
const themes = [];
|
|
29870
30003
|
for (const th of this._themes) {
|