jssm 5.133.0 → 5.134.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 +7 -7
- package/dist/cdn/viz.js +421 -2
- package/dist/cli/fsl-render.cjs +1 -1
- package/dist/cli/fsl.cjs +1 -1
- package/dist/deno/README.md +7 -7
- package/dist/deno/jssm.d.ts +131 -1
- package/dist/deno/jssm.js +1 -1
- package/dist/deno/jssm_types.d.ts +208 -1
- 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 +337 -0
- package/jssm.es6.d.ts +337 -0
- package/jssm_viz.es5.d.cts +337 -0
- package/jssm_viz.es6.d.ts +337 -0
- package/package.json +1 -1
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.
|
|
21
|
+
* Generated for version 5.134.0 at 5/27/2026, 10:16:05 PM
|
|
22
22
|
|
|
23
23
|
-->
|
|
24
|
-
# jssm 5.
|
|
24
|
+
# jssm 5.134.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,
|
|
284
|
+
library.** 6,220 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,
|
|
417
|
+
***6,220 tests***, run 57,007 times.
|
|
418
418
|
|
|
419
|
-
- 5,
|
|
420
|
-
- 513 fuzz tests with 4.
|
|
421
|
-
- 4,
|
|
419
|
+
- 5,707 specs with 100.0% coverage
|
|
420
|
+
- 513 fuzz tests with 4.0% coverage
|
|
421
|
+
- 4,856 TypeScript lines - 1.3 tests per line, 11.7 generated tests per line
|
|
422
422
|
|
|
423
423
|
[](https://github.com/StoneCypher/jssm/actions)
|
|
424
424
|
[](https://www.npmjs.com/package/jssm)
|
package/dist/cdn/viz.js
CHANGED
|
@@ -21352,7 +21352,7 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
21352
21352
|
* Useful for runtime diagnostics and for embedding in serialized machine
|
|
21353
21353
|
* snapshots so that deserializers can detect version-skew.
|
|
21354
21354
|
*/
|
|
21355
|
-
const version = "5.
|
|
21355
|
+
const version = "5.134.0";
|
|
21356
21356
|
|
|
21357
21357
|
// whargarbl lots of these return arrays could/should be sets
|
|
21358
21358
|
const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
|
|
@@ -21646,6 +21646,8 @@ class Machine {
|
|
|
21646
21646
|
this._timeout_target = undefined;
|
|
21647
21647
|
this._timeout_target_time = undefined;
|
|
21648
21648
|
this._after_mapping = new Map();
|
|
21649
|
+
this._event_handlers = new Map();
|
|
21650
|
+
this._firing_error = false;
|
|
21649
21651
|
// consolidate the state declarations
|
|
21650
21652
|
if (state_declaration) {
|
|
21651
21653
|
state_declaration.map((state_decl) => {
|
|
@@ -22971,6 +22973,141 @@ class Machine {
|
|
|
22971
22973
|
has_completes() {
|
|
22972
22974
|
return this.states().some((x) => this.state_is_complete(x));
|
|
22973
22975
|
}
|
|
22976
|
+
on(name, filterOrFn, maybeFn) {
|
|
22977
|
+
return this._subscribe(name, filterOrFn, maybeFn, false);
|
|
22978
|
+
}
|
|
22979
|
+
once(name, filterOrFn, maybeFn) {
|
|
22980
|
+
return this._subscribe(name, filterOrFn, maybeFn, true);
|
|
22981
|
+
}
|
|
22982
|
+
/**
|
|
22983
|
+
* Remove a previously-registered event handler. Match is by reference —
|
|
22984
|
+
* the same function value passed to {@link Machine.on} or
|
|
22985
|
+
* {@link Machine.once}. Returns `true` if a subscription was found and
|
|
22986
|
+
* removed, `false` otherwise.
|
|
22987
|
+
*
|
|
22988
|
+
* ```typescript
|
|
22989
|
+
* const fn = (e: any) => console.log(e);
|
|
22990
|
+
* m.on('transition', fn);
|
|
22991
|
+
* m.off('transition', fn); // true
|
|
22992
|
+
* m.off('transition', fn); // false
|
|
22993
|
+
* ```
|
|
22994
|
+
*
|
|
22995
|
+
* @param name The event name.
|
|
22996
|
+
* @param handler The handler reference to remove.
|
|
22997
|
+
* @returns `true` if removed, `false` if no match was registered.
|
|
22998
|
+
*/
|
|
22999
|
+
off(name, handler) {
|
|
23000
|
+
const set = this._event_handlers.get(name);
|
|
23001
|
+
if (set === undefined) {
|
|
23002
|
+
return false;
|
|
23003
|
+
}
|
|
23004
|
+
for (const entry of set) {
|
|
23005
|
+
if (entry.handler === handler) {
|
|
23006
|
+
set.delete(entry);
|
|
23007
|
+
return true;
|
|
23008
|
+
}
|
|
23009
|
+
}
|
|
23010
|
+
return false;
|
|
23011
|
+
}
|
|
23012
|
+
/**
|
|
23013
|
+
* Shared registration core used by {@link Machine.on} and
|
|
23014
|
+
* {@link Machine.once}. Normalizes the optional filter argument and
|
|
23015
|
+
* installs the entry into the per-event subscription set.
|
|
23016
|
+
*
|
|
23017
|
+
* @internal
|
|
23018
|
+
*/
|
|
23019
|
+
_subscribe(name, filterOrFn, maybeFn, once) {
|
|
23020
|
+
let filter;
|
|
23021
|
+
let handler;
|
|
23022
|
+
if (typeof filterOrFn === 'function') {
|
|
23023
|
+
filter = undefined;
|
|
23024
|
+
handler = filterOrFn;
|
|
23025
|
+
}
|
|
23026
|
+
else {
|
|
23027
|
+
filter = filterOrFn;
|
|
23028
|
+
handler = maybeFn;
|
|
23029
|
+
}
|
|
23030
|
+
if (typeof handler !== 'function') {
|
|
23031
|
+
throw new JssmError(this, `event handler for "${name}" must be a function`);
|
|
23032
|
+
}
|
|
23033
|
+
let set = this._event_handlers.get(name);
|
|
23034
|
+
if (set === undefined) {
|
|
23035
|
+
set = new Set();
|
|
23036
|
+
this._event_handlers.set(name, set);
|
|
23037
|
+
}
|
|
23038
|
+
const entry = { handler, filter, once };
|
|
23039
|
+
set.add(entry);
|
|
23040
|
+
return () => { set.delete(entry); };
|
|
23041
|
+
}
|
|
23042
|
+
/**
|
|
23043
|
+
* Dispatch an event to every registered subscriber in registration
|
|
23044
|
+
* order. Filters are checked first; non-matching handlers are skipped
|
|
23045
|
+
* without invoking the handler. Exceptions thrown by a handler are
|
|
23046
|
+
* caught and re-emitted as an `error` event so subsequent handlers
|
|
23047
|
+
* still run.
|
|
23048
|
+
*
|
|
23049
|
+
* Re-entry into the `error` event itself is guarded — if an `error`
|
|
23050
|
+
* handler throws, the new exception is swallowed rather than rebroadcast
|
|
23051
|
+
* to avoid an infinite loop.
|
|
23052
|
+
*
|
|
23053
|
+
* @internal
|
|
23054
|
+
*/
|
|
23055
|
+
_fire(name, detail) {
|
|
23056
|
+
const set = this._event_handlers.get(name);
|
|
23057
|
+
if (set === undefined || set.size === 0) {
|
|
23058
|
+
return;
|
|
23059
|
+
}
|
|
23060
|
+
// Snapshot so handlers can `off()` mid-loop without disturbing iteration.
|
|
23061
|
+
const entries = Array.from(set);
|
|
23062
|
+
for (const entry of entries) {
|
|
23063
|
+
// filter check
|
|
23064
|
+
if (entry.filter !== undefined) {
|
|
23065
|
+
let matched = true;
|
|
23066
|
+
for (const k of Object.keys(entry.filter)) {
|
|
23067
|
+
if (entry.filter[k] !== detail[k]) {
|
|
23068
|
+
matched = false;
|
|
23069
|
+
break;
|
|
23070
|
+
}
|
|
23071
|
+
}
|
|
23072
|
+
if (!matched) {
|
|
23073
|
+
continue;
|
|
23074
|
+
}
|
|
23075
|
+
}
|
|
23076
|
+
// once removal happens BEFORE invocation so a throwing handler still
|
|
23077
|
+
// gets removed and so re-entrant `on` calls during the handler see
|
|
23078
|
+
// the post-removal state.
|
|
23079
|
+
if (entry.once) {
|
|
23080
|
+
set.delete(entry);
|
|
23081
|
+
}
|
|
23082
|
+
try {
|
|
23083
|
+
entry.handler(detail);
|
|
23084
|
+
}
|
|
23085
|
+
catch (err) {
|
|
23086
|
+
if (name === 'error' || this._firing_error) {
|
|
23087
|
+
// surface to stderr as a last resort but never recurse;
|
|
23088
|
+
// `console` is in the JS standard library and present in every
|
|
23089
|
+
// supported runtime, so guarding it would just add an untestable
|
|
23090
|
+
// branch. See #638.
|
|
23091
|
+
// eslint-disable-next-line no-console
|
|
23092
|
+
console.error(err);
|
|
23093
|
+
}
|
|
23094
|
+
else {
|
|
23095
|
+
this._firing_error = true;
|
|
23096
|
+
try {
|
|
23097
|
+
this._fire('error', {
|
|
23098
|
+
error: err,
|
|
23099
|
+
source_event: name,
|
|
23100
|
+
source_detail: detail,
|
|
23101
|
+
handler: entry.handler
|
|
23102
|
+
});
|
|
23103
|
+
}
|
|
23104
|
+
finally {
|
|
23105
|
+
this._firing_error = false;
|
|
23106
|
+
}
|
|
23107
|
+
}
|
|
23108
|
+
}
|
|
23109
|
+
}
|
|
23110
|
+
}
|
|
22974
23111
|
/** Low-level hook registration. Installs a handler described by a
|
|
22975
23112
|
* {@link HookDescription} into the appropriate internal map. Prefer the
|
|
22976
23113
|
* convenience wrappers ({@link hook}, {@link hook_entry}, etc.) over
|
|
@@ -23137,6 +23274,179 @@ class Machine {
|
|
|
23137
23274
|
default:
|
|
23138
23275
|
throw new JssmError(this, `Unknown hook type ${HookDesc.kind}, should be impossible`);
|
|
23139
23276
|
}
|
|
23277
|
+
// fire the registration event for inspector tools (#638)
|
|
23278
|
+
this._fire('hook-registration', { description: HookDesc });
|
|
23279
|
+
}
|
|
23280
|
+
/**
|
|
23281
|
+
* Remove a previously-registered hook described by a
|
|
23282
|
+
* {@link HookDescription}. Match is by `kind` + identifying keys
|
|
23283
|
+
* (`from`/`to`/`action`/etc.), not by handler reference — there is one
|
|
23284
|
+
* hook per slot in the registry, so the description uniquely identifies
|
|
23285
|
+
* which one to clear. Fires a `hook-removal` event for inspector tools.
|
|
23286
|
+
*
|
|
23287
|
+
* This is the symmetric counterpart of {@link Machine.set_hook} for the
|
|
23288
|
+
* event-bridging use case (#638). Reasoning about hooks via observation
|
|
23289
|
+
* events requires being able to observe their disappearance too.
|
|
23290
|
+
*
|
|
23291
|
+
* ```typescript
|
|
23292
|
+
* const m = sm`a -> b;`;
|
|
23293
|
+
* const fn = () => true;
|
|
23294
|
+
* m.set_hook({ kind: 'hook', from: 'a', to: 'b', handler: fn });
|
|
23295
|
+
* m.remove_hook({ kind: 'hook', from: 'a', to: 'b', handler: fn });
|
|
23296
|
+
* ```
|
|
23297
|
+
*
|
|
23298
|
+
* @param HookDesc - A hook descriptor identifying the hook to remove.
|
|
23299
|
+
* @returns `true` if a hook was removed, `false` otherwise.
|
|
23300
|
+
*/
|
|
23301
|
+
remove_hook(HookDesc) {
|
|
23302
|
+
let removed = false;
|
|
23303
|
+
switch (HookDesc.kind) {
|
|
23304
|
+
case 'hook': {
|
|
23305
|
+
const inner = this._hooks.get(HookDesc.from);
|
|
23306
|
+
if (inner !== undefined && inner.has(HookDesc.to)) {
|
|
23307
|
+
inner.delete(HookDesc.to);
|
|
23308
|
+
removed = true;
|
|
23309
|
+
}
|
|
23310
|
+
break;
|
|
23311
|
+
}
|
|
23312
|
+
case 'named': {
|
|
23313
|
+
const inner = this._named_hooks.get(HookDesc.from);
|
|
23314
|
+
const inner2 = inner === undefined ? undefined : inner.get(HookDesc.to);
|
|
23315
|
+
if (inner2 !== undefined && inner2.has(HookDesc.action)) {
|
|
23316
|
+
inner2.delete(HookDesc.action);
|
|
23317
|
+
removed = true;
|
|
23318
|
+
}
|
|
23319
|
+
break;
|
|
23320
|
+
}
|
|
23321
|
+
case 'global action':
|
|
23322
|
+
removed = this._global_action_hooks.delete(HookDesc.action);
|
|
23323
|
+
break;
|
|
23324
|
+
case 'any action':
|
|
23325
|
+
if (this._any_action_hook !== undefined) {
|
|
23326
|
+
this._any_action_hook = undefined;
|
|
23327
|
+
removed = true;
|
|
23328
|
+
}
|
|
23329
|
+
break;
|
|
23330
|
+
case 'standard transition':
|
|
23331
|
+
if (this._standard_transition_hook !== undefined) {
|
|
23332
|
+
this._standard_transition_hook = undefined;
|
|
23333
|
+
removed = true;
|
|
23334
|
+
}
|
|
23335
|
+
break;
|
|
23336
|
+
case 'main transition':
|
|
23337
|
+
if (this._main_transition_hook !== undefined) {
|
|
23338
|
+
this._main_transition_hook = undefined;
|
|
23339
|
+
removed = true;
|
|
23340
|
+
}
|
|
23341
|
+
break;
|
|
23342
|
+
case 'forced transition':
|
|
23343
|
+
if (this._forced_transition_hook !== undefined) {
|
|
23344
|
+
this._forced_transition_hook = undefined;
|
|
23345
|
+
removed = true;
|
|
23346
|
+
}
|
|
23347
|
+
break;
|
|
23348
|
+
case 'any transition':
|
|
23349
|
+
if (this._any_transition_hook !== undefined) {
|
|
23350
|
+
this._any_transition_hook = undefined;
|
|
23351
|
+
removed = true;
|
|
23352
|
+
}
|
|
23353
|
+
break;
|
|
23354
|
+
case 'entry':
|
|
23355
|
+
removed = this._entry_hooks.delete(HookDesc.to);
|
|
23356
|
+
break;
|
|
23357
|
+
case 'exit':
|
|
23358
|
+
removed = this._exit_hooks.delete(HookDesc.from);
|
|
23359
|
+
break;
|
|
23360
|
+
case 'after':
|
|
23361
|
+
removed = this._after_hooks.delete(HookDesc.from);
|
|
23362
|
+
break;
|
|
23363
|
+
case 'post hook': {
|
|
23364
|
+
const inner = this._post_hooks.get(HookDesc.from);
|
|
23365
|
+
if (inner !== undefined && inner.has(HookDesc.to)) {
|
|
23366
|
+
inner.delete(HookDesc.to);
|
|
23367
|
+
removed = true;
|
|
23368
|
+
}
|
|
23369
|
+
break;
|
|
23370
|
+
}
|
|
23371
|
+
case 'post named': {
|
|
23372
|
+
const inner = this._post_named_hooks.get(HookDesc.from);
|
|
23373
|
+
const inner2 = inner === undefined ? undefined : inner.get(HookDesc.to);
|
|
23374
|
+
if (inner2 !== undefined && inner2.has(HookDesc.action)) {
|
|
23375
|
+
inner2.delete(HookDesc.action);
|
|
23376
|
+
removed = true;
|
|
23377
|
+
}
|
|
23378
|
+
break;
|
|
23379
|
+
}
|
|
23380
|
+
case 'post global action':
|
|
23381
|
+
removed = this._post_global_action_hooks.delete(HookDesc.action);
|
|
23382
|
+
break;
|
|
23383
|
+
case 'post any action':
|
|
23384
|
+
if (this._post_any_action_hook !== undefined) {
|
|
23385
|
+
this._post_any_action_hook = undefined;
|
|
23386
|
+
removed = true;
|
|
23387
|
+
}
|
|
23388
|
+
break;
|
|
23389
|
+
case 'post standard transition':
|
|
23390
|
+
if (this._post_standard_transition_hook !== undefined) {
|
|
23391
|
+
this._post_standard_transition_hook = undefined;
|
|
23392
|
+
removed = true;
|
|
23393
|
+
}
|
|
23394
|
+
break;
|
|
23395
|
+
case 'post main transition':
|
|
23396
|
+
if (this._post_main_transition_hook !== undefined) {
|
|
23397
|
+
this._post_main_transition_hook = undefined;
|
|
23398
|
+
removed = true;
|
|
23399
|
+
}
|
|
23400
|
+
break;
|
|
23401
|
+
case 'post forced transition':
|
|
23402
|
+
if (this._post_forced_transition_hook !== undefined) {
|
|
23403
|
+
this._post_forced_transition_hook = undefined;
|
|
23404
|
+
removed = true;
|
|
23405
|
+
}
|
|
23406
|
+
break;
|
|
23407
|
+
case 'post any transition':
|
|
23408
|
+
if (this._post_any_transition_hook !== undefined) {
|
|
23409
|
+
this._post_any_transition_hook = undefined;
|
|
23410
|
+
removed = true;
|
|
23411
|
+
}
|
|
23412
|
+
break;
|
|
23413
|
+
case 'post entry':
|
|
23414
|
+
removed = this._post_entry_hooks.delete(HookDesc.to);
|
|
23415
|
+
break;
|
|
23416
|
+
case 'post exit':
|
|
23417
|
+
removed = this._post_exit_hooks.delete(HookDesc.from);
|
|
23418
|
+
break;
|
|
23419
|
+
case 'pre everything':
|
|
23420
|
+
if (this._pre_everything_hook !== undefined) {
|
|
23421
|
+
this._pre_everything_hook = undefined;
|
|
23422
|
+
removed = true;
|
|
23423
|
+
}
|
|
23424
|
+
break;
|
|
23425
|
+
case 'everything':
|
|
23426
|
+
if (this._everything_hook !== undefined) {
|
|
23427
|
+
this._everything_hook = undefined;
|
|
23428
|
+
removed = true;
|
|
23429
|
+
}
|
|
23430
|
+
break;
|
|
23431
|
+
case 'pre post everything':
|
|
23432
|
+
if (this._pre_post_everything_hook !== undefined) {
|
|
23433
|
+
this._pre_post_everything_hook = undefined;
|
|
23434
|
+
removed = true;
|
|
23435
|
+
}
|
|
23436
|
+
break;
|
|
23437
|
+
case 'post everything':
|
|
23438
|
+
if (this._post_everything_hook !== undefined) {
|
|
23439
|
+
this._post_everything_hook = undefined;
|
|
23440
|
+
removed = true;
|
|
23441
|
+
}
|
|
23442
|
+
break;
|
|
23443
|
+
default:
|
|
23444
|
+
throw new JssmError(this, `Unknown hook type ${HookDesc.kind}, should be impossible`);
|
|
23445
|
+
}
|
|
23446
|
+
if (removed) {
|
|
23447
|
+
this._fire('hook-removal', { description: HookDesc });
|
|
23448
|
+
}
|
|
23449
|
+
return removed;
|
|
23140
23450
|
}
|
|
23141
23451
|
/** Register a pre-transition hook on a specific edge. Fires before
|
|
23142
23452
|
* transitioning from `from` to `to`. If the handler returns `false`, the
|
|
@@ -23478,8 +23788,25 @@ class Machine {
|
|
|
23478
23788
|
override(newState, newData) {
|
|
23479
23789
|
if (this.allows_override) {
|
|
23480
23790
|
if (this._states.has(newState)) {
|
|
23791
|
+
const fromState = this._state;
|
|
23792
|
+
const oldData = this._data;
|
|
23481
23793
|
this._state = newState;
|
|
23482
23794
|
this._data = newData;
|
|
23795
|
+
this._fire('override', {
|
|
23796
|
+
from: fromState,
|
|
23797
|
+
to: newState,
|
|
23798
|
+
old_data: oldData,
|
|
23799
|
+
new_data: newData
|
|
23800
|
+
});
|
|
23801
|
+
if (oldData !== newData) {
|
|
23802
|
+
this._fire('data-change', {
|
|
23803
|
+
from: fromState,
|
|
23804
|
+
to: newState,
|
|
23805
|
+
old_data: oldData,
|
|
23806
|
+
new_data: newData,
|
|
23807
|
+
cause: 'override'
|
|
23808
|
+
});
|
|
23809
|
+
}
|
|
23483
23810
|
}
|
|
23484
23811
|
else {
|
|
23485
23812
|
throw new JssmError(this, `Cannot override state to "${newState}", a state that does not exist`);
|
|
@@ -23568,6 +23895,21 @@ class Machine {
|
|
|
23568
23895
|
forced: wasForced,
|
|
23569
23896
|
trans_type
|
|
23570
23897
|
};
|
|
23898
|
+
// 'action' event fires when an action is attempted, regardless of whether
|
|
23899
|
+
// it ultimately succeeds — matches the issue spec for observation events.
|
|
23900
|
+
if (wasAction) {
|
|
23901
|
+
this._fire('action', {
|
|
23902
|
+
action: newStateOrAction,
|
|
23903
|
+
from: this._state,
|
|
23904
|
+
to: newState,
|
|
23905
|
+
data: this._data,
|
|
23906
|
+
next_data: newData
|
|
23907
|
+
});
|
|
23908
|
+
}
|
|
23909
|
+
// Captured pre-transition source state so 'data-change' detail and similar
|
|
23910
|
+
// events can name where we came from.
|
|
23911
|
+
const fromState = this._state;
|
|
23912
|
+
const oldData = this._data;
|
|
23571
23913
|
if (valid) {
|
|
23572
23914
|
if (this._has_hooks) {
|
|
23573
23915
|
// once validity is known, clear old 'after' timeout clause
|
|
@@ -23580,10 +23922,23 @@ class Machine {
|
|
|
23580
23922
|
}
|
|
23581
23923
|
}
|
|
23582
23924
|
let data_changed = false;
|
|
23925
|
+
const fire_rejection = (hook_name) => {
|
|
23926
|
+
this._fire('rejection', {
|
|
23927
|
+
from: fromState,
|
|
23928
|
+
to: newState,
|
|
23929
|
+
action: fromAction,
|
|
23930
|
+
data: oldData,
|
|
23931
|
+
next_data: newData,
|
|
23932
|
+
reason: 'hook',
|
|
23933
|
+
hook_name,
|
|
23934
|
+
forced: wasForced
|
|
23935
|
+
});
|
|
23936
|
+
};
|
|
23583
23937
|
// 0. pre everything hook (fires before all other pre-hooks)
|
|
23584
23938
|
if (this._pre_everything_hook !== undefined) {
|
|
23585
23939
|
const outcome = abstract_everything_hook_step(this._pre_everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'pre everything' }));
|
|
23586
23940
|
if (outcome.pass === false) {
|
|
23941
|
+
fire_rejection('pre everything');
|
|
23587
23942
|
return false;
|
|
23588
23943
|
}
|
|
23589
23944
|
update_fields(outcome);
|
|
@@ -23592,12 +23947,14 @@ class Machine {
|
|
|
23592
23947
|
// 1a. any action hook
|
|
23593
23948
|
const outcome = abstract_hook_step(this._any_action_hook, hook_args);
|
|
23594
23949
|
if (outcome.pass === false) {
|
|
23950
|
+
fire_rejection('any action');
|
|
23595
23951
|
return false;
|
|
23596
23952
|
}
|
|
23597
23953
|
update_fields(outcome);
|
|
23598
23954
|
// 1b. global specific action hook
|
|
23599
23955
|
const outcome2 = abstract_hook_step(this._global_action_hooks.get(newStateOrAction), hook_args);
|
|
23600
23956
|
if (outcome2.pass === false) {
|
|
23957
|
+
fire_rejection('global action');
|
|
23601
23958
|
return false;
|
|
23602
23959
|
}
|
|
23603
23960
|
update_fields(outcome2);
|
|
@@ -23613,6 +23970,7 @@ class Machine {
|
|
|
23613
23970
|
if (this._any_transition_hook !== undefined) {
|
|
23614
23971
|
const outcome = abstract_hook_step(this._any_transition_hook, hook_args);
|
|
23615
23972
|
if (outcome.pass === false) {
|
|
23973
|
+
fire_rejection('any transition');
|
|
23616
23974
|
return false;
|
|
23617
23975
|
}
|
|
23618
23976
|
update_fields(outcome);
|
|
@@ -23621,6 +23979,7 @@ class Machine {
|
|
|
23621
23979
|
if (this._has_exit_hooks) {
|
|
23622
23980
|
const outcome = abstract_hook_step(this._exit_hooks.get(this._state), hook_args);
|
|
23623
23981
|
if (outcome.pass === false) {
|
|
23982
|
+
fire_rejection('exit');
|
|
23624
23983
|
return false;
|
|
23625
23984
|
}
|
|
23626
23985
|
update_fields(outcome);
|
|
@@ -23635,6 +23994,7 @@ class Machine {
|
|
|
23635
23994
|
const nh = byAct === undefined ? undefined : byAct.get(newStateOrAction);
|
|
23636
23995
|
const outcome = abstract_hook_step(nh, hook_args);
|
|
23637
23996
|
if (outcome.pass === false) {
|
|
23997
|
+
fire_rejection('named');
|
|
23638
23998
|
return false;
|
|
23639
23999
|
}
|
|
23640
24000
|
update_fields(outcome);
|
|
@@ -23647,6 +24007,7 @@ class Machine {
|
|
|
23647
24007
|
const h = byTo === undefined ? undefined : byTo.get(newState);
|
|
23648
24008
|
const outcome = abstract_hook_step(h, hook_args);
|
|
23649
24009
|
if (outcome.pass === false) {
|
|
24010
|
+
fire_rejection('hook');
|
|
23650
24011
|
return false;
|
|
23651
24012
|
}
|
|
23652
24013
|
update_fields(outcome);
|
|
@@ -23656,6 +24017,7 @@ class Machine {
|
|
|
23656
24017
|
if (trans_type === 'legal') {
|
|
23657
24018
|
const outcome = abstract_hook_step(this._standard_transition_hook, hook_args);
|
|
23658
24019
|
if (outcome.pass === false) {
|
|
24020
|
+
fire_rejection('standard transition');
|
|
23659
24021
|
return false;
|
|
23660
24022
|
}
|
|
23661
24023
|
update_fields(outcome);
|
|
@@ -23664,6 +24026,7 @@ class Machine {
|
|
|
23664
24026
|
if (trans_type === 'main') {
|
|
23665
24027
|
const outcome = abstract_hook_step(this._main_transition_hook, hook_args);
|
|
23666
24028
|
if (outcome.pass === false) {
|
|
24029
|
+
fire_rejection('main transition');
|
|
23667
24030
|
return false;
|
|
23668
24031
|
}
|
|
23669
24032
|
update_fields(outcome);
|
|
@@ -23672,6 +24035,7 @@ class Machine {
|
|
|
23672
24035
|
if (trans_type === 'forced') {
|
|
23673
24036
|
const outcome = abstract_hook_step(this._forced_transition_hook, hook_args);
|
|
23674
24037
|
if (outcome.pass === false) {
|
|
24038
|
+
fire_rejection('forced transition');
|
|
23675
24039
|
return false;
|
|
23676
24040
|
}
|
|
23677
24041
|
update_fields(outcome);
|
|
@@ -23680,6 +24044,7 @@ class Machine {
|
|
|
23680
24044
|
if (this._has_entry_hooks) {
|
|
23681
24045
|
const outcome = abstract_hook_step(this._entry_hooks.get(newState), hook_args);
|
|
23682
24046
|
if (outcome.pass === false) {
|
|
24047
|
+
fire_rejection('entry');
|
|
23683
24048
|
return false;
|
|
23684
24049
|
}
|
|
23685
24050
|
update_fields(outcome);
|
|
@@ -23688,6 +24053,7 @@ class Machine {
|
|
|
23688
24053
|
if (this._everything_hook !== undefined) {
|
|
23689
24054
|
const outcome = abstract_everything_hook_step(this._everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'everything' }));
|
|
23690
24055
|
if (outcome.pass === false) {
|
|
24056
|
+
fire_rejection('everything');
|
|
23691
24057
|
return false;
|
|
23692
24058
|
}
|
|
23693
24059
|
update_fields(outcome);
|
|
@@ -23723,6 +24089,15 @@ class Machine {
|
|
|
23723
24089
|
// not valid
|
|
23724
24090
|
}
|
|
23725
24091
|
else {
|
|
24092
|
+
this._fire('rejection', {
|
|
24093
|
+
from: fromState,
|
|
24094
|
+
to: newStateOrAction,
|
|
24095
|
+
action: fromAction,
|
|
24096
|
+
data: oldData,
|
|
24097
|
+
next_data: newData,
|
|
24098
|
+
reason: 'invalid',
|
|
24099
|
+
forced: wasForced
|
|
24100
|
+
});
|
|
23726
24101
|
return false;
|
|
23727
24102
|
}
|
|
23728
24103
|
// posthooks begin here
|
|
@@ -23805,6 +24180,47 @@ class Machine {
|
|
|
23805
24180
|
this._post_everything_hook(Object.assign(Object.assign({}, hook_args), { hook_name: 'post everything' }));
|
|
23806
24181
|
}
|
|
23807
24182
|
}
|
|
24183
|
+
// observation events: fire after the state has been committed and all
|
|
24184
|
+
// hooks (pre and post) have completed, matching the semantics that
|
|
24185
|
+
// events observe rather than intercept (#638).
|
|
24186
|
+
const newData_after = this._data;
|
|
24187
|
+
this._fire('exit', {
|
|
24188
|
+
state: fromState,
|
|
24189
|
+
to: newState,
|
|
24190
|
+
action: fromAction,
|
|
24191
|
+
data: newData_after
|
|
24192
|
+
});
|
|
24193
|
+
this._fire('transition', {
|
|
24194
|
+
from: fromState,
|
|
24195
|
+
to: newState,
|
|
24196
|
+
action: fromAction,
|
|
24197
|
+
data: newData_after,
|
|
24198
|
+
next_data: newData,
|
|
24199
|
+
trans_type,
|
|
24200
|
+
forced: wasForced
|
|
24201
|
+
});
|
|
24202
|
+
this._fire('entry', {
|
|
24203
|
+
state: newState,
|
|
24204
|
+
from: fromState,
|
|
24205
|
+
action: fromAction,
|
|
24206
|
+
data: newData_after
|
|
24207
|
+
});
|
|
24208
|
+
if (oldData !== newData_after) {
|
|
24209
|
+
this._fire('data-change', {
|
|
24210
|
+
from: fromState,
|
|
24211
|
+
to: newState,
|
|
24212
|
+
action: fromAction,
|
|
24213
|
+
old_data: oldData,
|
|
24214
|
+
new_data: newData_after,
|
|
24215
|
+
cause: 'transition'
|
|
24216
|
+
});
|
|
24217
|
+
}
|
|
24218
|
+
if (this.state_is_terminal(newState)) {
|
|
24219
|
+
this._fire('terminal', { state: newState, data: newData_after });
|
|
24220
|
+
}
|
|
24221
|
+
if (this.state_is_complete(newState)) {
|
|
24222
|
+
this._fire('complete', { state: newState, data: newData_after });
|
|
24223
|
+
}
|
|
23808
24224
|
// possibly re-establish new 'after' clause
|
|
23809
24225
|
this.auto_set_state_timeout();
|
|
23810
24226
|
return true;
|
|
@@ -24433,14 +24849,17 @@ class Machine {
|
|
|
24433
24849
|
// this is enforced by the "after mapping runs normally with very short time" tests in after_mapping.spec
|
|
24434
24850
|
// we'll mark it no-check so that our coverage numbers aren't wrecked
|
|
24435
24851
|
/* istanbul ignore next */
|
|
24852
|
+
/* v8 ignore next 10 */
|
|
24436
24853
|
() => {
|
|
24854
|
+
const from_state = this.state();
|
|
24437
24855
|
this.clear_state_timeout();
|
|
24438
24856
|
if (this._has_after_hooks) {
|
|
24439
|
-
const ah = this._after_hooks.get(
|
|
24857
|
+
const ah = this._after_hooks.get(from_state);
|
|
24440
24858
|
if (ah !== undefined) {
|
|
24441
24859
|
ah({ data: this._data, next_data: this._data });
|
|
24442
24860
|
}
|
|
24443
24861
|
}
|
|
24862
|
+
this._fire('timeout', { from: from_state, to: next_state, after_time });
|
|
24444
24863
|
this.go(next_state);
|
|
24445
24864
|
}, after_time);
|
|
24446
24865
|
this._timeout_target = next_state;
|