@hkdigital/lib-core 0.4.23 → 0.4.25
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/dist/logging/internal/adapters/pino.d.ts +7 -3
- package/dist/logging/internal/adapters/pino.js +200 -67
- package/dist/logging/internal/transports/pretty-transport.d.ts +17 -0
- package/dist/logging/internal/transports/pretty-transport.js +104 -0
- package/dist/logging/internal/transports/test-transport.d.ts +19 -0
- package/dist/logging/internal/transports/test-transport.js +79 -0
- package/dist/network/loaders/audio/AudioScene.svelte.js +2 -2
- package/dist/network/loaders/image/ImageScene.svelte.js +18 -28
- package/dist/network/states/NetworkLoader.svelte.d.ts +7 -1
- package/dist/network/states/NetworkLoader.svelte.js +17 -4
- package/dist/services/README.md +23 -0
- package/dist/services/service-base/ServiceBase.d.ts +12 -8
- package/dist/services/service-base/ServiceBase.js +8 -6
- package/dist/state/classes.d.ts +0 -2
- package/dist/state/classes.js +0 -2
- package/dist/state/{classes → machines}/finite-state-machine/FiniteStateMachine.svelte.d.ts +13 -7
- package/dist/state/machines/finite-state-machine/FiniteStateMachine.svelte.js +181 -0
- package/dist/state/machines/finite-state-machine/README.md +547 -0
- package/dist/state/machines/finite-state-machine/constants.d.ts +13 -0
- package/dist/state/machines/finite-state-machine/constants.js +15 -0
- package/dist/state/{classes → machines}/finite-state-machine/index.d.ts +2 -1
- package/dist/state/{classes → machines}/finite-state-machine/index.js +2 -1
- package/dist/state/machines/finite-state-machine/typedef.d.ts +29 -0
- package/dist/state/machines/finite-state-machine/typedef.js +28 -0
- package/dist/state/machines/loading-state-machine/LoadingStateMachine.svelte.d.ts +22 -0
- package/dist/state/{classes → machines}/loading-state-machine/LoadingStateMachine.svelte.js +34 -29
- package/dist/state/machines/loading-state-machine/README.md +592 -0
- package/dist/state/{classes → machines}/loading-state-machine/constants.d.ts +2 -0
- package/dist/state/{classes → machines}/loading-state-machine/constants.js +2 -0
- package/dist/state/machines/typedef.d.ts +1 -0
- package/dist/state/machines/typedef.js +1 -0
- package/dist/state/machines.d.ts +2 -0
- package/dist/state/machines.js +2 -0
- package/dist/state/typedef.d.ts +1 -0
- package/dist/state/typedef.js +1 -0
- package/dist/ui/components/game-box/README.md +245 -0
- package/package.json +1 -1
- package/dist/logging/internal/adapters/pino.js__ +0 -260
- package/dist/state/classes/finite-state-machine/FiniteStateMachine.svelte.js +0 -133
- package/dist/state/classes/loading-state-machine/LoadingStateMachine.svelte.d.ts +0 -12
- /package/dist/state/{classes → machines}/loading-state-machine/index.d.ts +0 -0
- /package/dist/state/{classes → machines}/loading-state-machine/index.js +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as expect from '../../../util/expect.js';
|
|
4
4
|
|
|
5
|
-
import { LoadingStateMachine } from '../../../state/
|
|
5
|
+
import { LoadingStateMachine } from '../../../state/machines.js';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
STATE_INITIAL,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
STATE_ERROR,
|
|
14
14
|
LOAD,
|
|
15
15
|
LOADED
|
|
16
|
-
} from '../../../state/
|
|
16
|
+
} from '../../../state/machines.js';
|
|
17
17
|
|
|
18
18
|
import ImageLoader from './ImageLoader.svelte.js';
|
|
19
19
|
|
|
@@ -74,30 +74,28 @@ export default class ImageScene {
|
|
|
74
74
|
};
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
#sourcesLoaded = $derived( this.#progress.sourcesLoaded );
|
|
78
|
+
#numberOfSources = $derived( this.#progress.numberOfSources );
|
|
79
|
+
|
|
77
80
|
/**
|
|
78
81
|
* Construct ImageScene
|
|
79
82
|
*/
|
|
80
83
|
constructor() {
|
|
81
84
|
const state = this.#state;
|
|
82
85
|
|
|
83
|
-
$effect(() => {
|
|
86
|
+
$effect( () => {
|
|
84
87
|
if (state.current === STATE_LOADING) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// JSON.stringify($state.snapshot(this.#progress))
|
|
88
|
-
// );
|
|
89
|
-
|
|
90
|
-
const { sourcesLoaded, numberOfSources } = this.#progress;
|
|
91
|
-
|
|
92
|
-
if (sourcesLoaded === numberOfSources) {
|
|
93
|
-
// console.log(`All [${numberOfSources}] sources loaded`);
|
|
88
|
+
if (this.#sourcesLoaded === this.#numberOfSources) {
|
|
89
|
+
// console.log(`All [${this.#numberOfSources}] sources loaded`);
|
|
94
90
|
this.#state.send(LOADED);
|
|
95
91
|
}
|
|
96
92
|
}
|
|
97
|
-
});
|
|
93
|
+
} );
|
|
98
94
|
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
state.onenter = ( state ) => {
|
|
96
|
+
// console.log('onenter', state );
|
|
97
|
+
|
|
98
|
+
switch (state) {
|
|
101
99
|
case STATE_LOADING:
|
|
102
100
|
{
|
|
103
101
|
// console.log('ImageScene:loading');
|
|
@@ -129,13 +127,13 @@ export default class ImageScene {
|
|
|
129
127
|
|
|
130
128
|
case STATE_ERROR:
|
|
131
129
|
{
|
|
132
|
-
console.log('ImageScene:error', state
|
|
130
|
+
console.log('ImageScene:error', state);
|
|
133
131
|
}
|
|
134
132
|
break;
|
|
135
133
|
} // end switch
|
|
136
134
|
|
|
137
|
-
this.state = state
|
|
138
|
-
}
|
|
135
|
+
this.state = state;
|
|
136
|
+
};
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
destroy() {
|
|
@@ -166,22 +164,14 @@ export default class ImageScene {
|
|
|
166
164
|
*/
|
|
167
165
|
load() {
|
|
168
166
|
this.#state.send(LOAD);
|
|
167
|
+
}
|
|
169
168
|
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
async #startLoading() {
|
|
172
170
|
for (const { imageLoader } of this.#imageSources) {
|
|
173
171
|
imageLoader.load();
|
|
174
172
|
}
|
|
175
173
|
}
|
|
176
174
|
|
|
177
|
-
async #startLoading() {
|
|
178
|
-
// console.log('#startLoading');
|
|
179
|
-
// FIXME: in unit test when moved to startloading it hangs!
|
|
180
|
-
// for (const { audioLoader } of this.#memorySources) {
|
|
181
|
-
// audioLoader.load();
|
|
182
|
-
// }
|
|
183
|
-
}
|
|
184
|
-
|
|
185
175
|
/**
|
|
186
176
|
* Get Image source
|
|
187
177
|
*
|
|
@@ -42,6 +42,12 @@ export default class NetworkLoader {
|
|
|
42
42
|
* Unoad all network data
|
|
43
43
|
*/
|
|
44
44
|
unload(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Abort the current loading operation
|
|
47
|
+
* - Only works when in STATE_LOADING
|
|
48
|
+
* - Aborts network requests and transitions to STATE_CANCELLED
|
|
49
|
+
*/
|
|
50
|
+
doAbort(): void;
|
|
45
51
|
/**
|
|
46
52
|
* Get network data size in bytes
|
|
47
53
|
* - Info comes from the content length response header
|
|
@@ -88,4 +94,4 @@ export default class NetworkLoader {
|
|
|
88
94
|
getObjectURL(): string;
|
|
89
95
|
#private;
|
|
90
96
|
}
|
|
91
|
-
import { LoadingStateMachine } from '../../state/
|
|
97
|
+
import { LoadingStateMachine } from '../../state/machines.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CONTENT_TYPE } from '../../constants/http.js';
|
|
2
2
|
|
|
3
|
-
import { LoadingStateMachine } from '../../state/
|
|
3
|
+
import { LoadingStateMachine } from '../../state/machines.js';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
STATE_INITIAL,
|
|
@@ -13,8 +13,9 @@ import {
|
|
|
13
13
|
ERROR,
|
|
14
14
|
LOADED,
|
|
15
15
|
UNLOAD,
|
|
16
|
-
INITIAL
|
|
17
|
-
|
|
16
|
+
INITIAL,
|
|
17
|
+
CANCEL
|
|
18
|
+
} from '../../state/machines.js';
|
|
18
19
|
|
|
19
20
|
import * as expect from '../../util/expect.js';
|
|
20
21
|
|
|
@@ -129,7 +130,10 @@ export default class NetworkLoader {
|
|
|
129
130
|
case STATE_CANCELLED:
|
|
130
131
|
{
|
|
131
132
|
// console.log('NetworkLoader:cancelled');
|
|
132
|
-
|
|
133
|
+
if (this._abortLoading) {
|
|
134
|
+
this._abortLoading();
|
|
135
|
+
this._abortLoading = null;
|
|
136
|
+
}
|
|
133
137
|
}
|
|
134
138
|
break;
|
|
135
139
|
|
|
@@ -157,6 +161,15 @@ export default class NetworkLoader {
|
|
|
157
161
|
this._state.send(UNLOAD);
|
|
158
162
|
}
|
|
159
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Abort the current loading operation
|
|
166
|
+
* - Only works when in STATE_LOADING
|
|
167
|
+
* - Aborts network requests and transitions to STATE_CANCELLED
|
|
168
|
+
*/
|
|
169
|
+
doAbort() {
|
|
170
|
+
this._state.send(CANCEL);
|
|
171
|
+
}
|
|
172
|
+
|
|
160
173
|
/**
|
|
161
174
|
* Get network data size in bytes
|
|
162
175
|
* - Info comes from the content length response header
|
package/dist/services/README.md
CHANGED
|
@@ -259,6 +259,29 @@ manager.on(SERVICE_ERROR, async ({ service, error }) => {
|
|
|
259
259
|
await manager.recoverService('database');
|
|
260
260
|
```
|
|
261
261
|
|
|
262
|
+
### Log Event Forwarding
|
|
263
|
+
|
|
264
|
+
Forward all service log events to a centralised logger:
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
import { ServiceManager, SERVICE_LOG } from '$lib/services/index.js';
|
|
268
|
+
import { createServerLogger } from '$lib/logging/index.js';
|
|
269
|
+
|
|
270
|
+
const manager = new ServiceManager();
|
|
271
|
+
const logger = createServerLogger('SystemLogger');
|
|
272
|
+
|
|
273
|
+
// Listen to all log events and forward them to the logger
|
|
274
|
+
manager.on(SERVICE_LOG, (logEvent) => {
|
|
275
|
+
logger.logFromEvent('manager:service:log', logEvent);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Register services
|
|
279
|
+
manager.register('database', DatabaseService, { ... });
|
|
280
|
+
manager.register('auth', AuthService, { ... });
|
|
281
|
+
|
|
282
|
+
await manager.startAll();
|
|
283
|
+
```
|
|
284
|
+
|
|
262
285
|
## Plugins
|
|
263
286
|
|
|
264
287
|
ServiceManager supports plugins e.g. to resolve service configurations dynamically.
|
|
@@ -156,36 +156,40 @@ export class ServiceBase extends EventEmitter {
|
|
|
156
156
|
/**
|
|
157
157
|
* Set the service state and emit event
|
|
158
158
|
*
|
|
159
|
-
* @
|
|
159
|
+
* @protected
|
|
160
|
+
*
|
|
160
161
|
* @param {ServiceState} newState - New state value
|
|
161
162
|
* @emits {StateChangeEvent} EVENT_STATE_CHANGED
|
|
162
163
|
*/
|
|
163
|
-
|
|
164
|
+
protected _setState(newState: ServiceState): void;
|
|
164
165
|
/**
|
|
165
166
|
* Set the service target state and emit event
|
|
166
167
|
*
|
|
167
|
-
* @
|
|
168
|
+
* @protected
|
|
169
|
+
*
|
|
168
170
|
* @param {ServiceState} newTargetState - New target state value
|
|
169
171
|
* @emits {TargetStateChangeEvent} EVENT_TARGET_STATE_CHANGED
|
|
170
172
|
*/
|
|
171
|
-
|
|
173
|
+
protected _setTargetState(newTargetState: ServiceState): void;
|
|
172
174
|
/**
|
|
173
175
|
* Set the health status and emit event if changed
|
|
174
176
|
*
|
|
175
|
-
* @
|
|
177
|
+
* @protected
|
|
178
|
+
*
|
|
176
179
|
* @param {boolean} healthy - New health status
|
|
177
180
|
* @emits {HealthChangeEvent} EVENT_HEALTH_CHANGED
|
|
178
181
|
*/
|
|
179
|
-
|
|
182
|
+
protected _setHealthy(healthy: boolean): void;
|
|
180
183
|
/**
|
|
181
184
|
* Set error state and emit error event
|
|
182
185
|
*
|
|
183
|
-
* @
|
|
186
|
+
* @protected
|
|
187
|
+
*
|
|
184
188
|
* @param {string} operation - Operation that failed
|
|
185
189
|
* @param {Error} error - Error that occurred
|
|
186
190
|
* @emits {ServiceErrorEvent} EVENT_ERROR
|
|
187
191
|
*/
|
|
188
|
-
|
|
192
|
+
protected _setError(operation: string, error: Error): void;
|
|
189
193
|
#private;
|
|
190
194
|
}
|
|
191
195
|
export default ServiceBase;
|
|
@@ -447,12 +447,11 @@ export class ServiceBase extends EventEmitter {
|
|
|
447
447
|
return {};
|
|
448
448
|
}
|
|
449
449
|
|
|
450
|
-
// Private methods
|
|
451
|
-
|
|
452
450
|
/**
|
|
453
451
|
* Set the service state and emit event
|
|
454
452
|
*
|
|
455
|
-
* @
|
|
453
|
+
* @protected
|
|
454
|
+
*
|
|
456
455
|
* @param {ServiceState} newState - New state value
|
|
457
456
|
* @emits {StateChangeEvent} EVENT_STATE_CHANGED
|
|
458
457
|
*/
|
|
@@ -473,7 +472,8 @@ export class ServiceBase extends EventEmitter {
|
|
|
473
472
|
/**
|
|
474
473
|
* Set the service target state and emit event
|
|
475
474
|
*
|
|
476
|
-
* @
|
|
475
|
+
* @protected
|
|
476
|
+
*
|
|
477
477
|
* @param {ServiceState} newTargetState - New target state value
|
|
478
478
|
* @emits {TargetStateChangeEvent} EVENT_TARGET_STATE_CHANGED
|
|
479
479
|
*/
|
|
@@ -493,7 +493,8 @@ export class ServiceBase extends EventEmitter {
|
|
|
493
493
|
/**
|
|
494
494
|
* Set the health status and emit event if changed
|
|
495
495
|
*
|
|
496
|
-
* @
|
|
496
|
+
* @protected
|
|
497
|
+
*
|
|
497
498
|
* @param {boolean} healthy - New health status
|
|
498
499
|
* @emits {HealthChangeEvent} EVENT_HEALTH_CHANGED
|
|
499
500
|
*/
|
|
@@ -515,7 +516,8 @@ export class ServiceBase extends EventEmitter {
|
|
|
515
516
|
/**
|
|
516
517
|
* Set error state and emit error event
|
|
517
518
|
*
|
|
518
|
-
* @
|
|
519
|
+
* @protected
|
|
520
|
+
*
|
|
519
521
|
* @param {string} operation - Operation that failed
|
|
520
522
|
* @param {Error} error - Error that occurred
|
|
521
523
|
* @emits {ServiceErrorEvent} EVENT_ERROR
|
package/dist/state/classes.d.ts
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
export { default as FiniteStateMachine } from "./classes/finite-state-machine/FiniteStateMachine.svelte.js";
|
|
2
|
-
export { default as LoadingStateMachine } from "./classes/loading-state-machine/LoadingStateMachine.svelte.js";
|
|
3
1
|
export { default as SubscribersCount } from "./classes/subscribers-count/SubscribersCount.js";
|
package/dist/state/classes.js
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
export { default as FiniteStateMachine } from './classes/finite-state-machine/FiniteStateMachine.svelte.js';
|
|
2
|
-
export { default as LoadingStateMachine } from './classes/loading-state-machine/LoadingStateMachine.svelte.js';
|
|
3
1
|
export { default as SubscribersCount } from './classes/subscribers-count/SubscribersCount.js';
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* @see {@link https://runed.dev/docs/utilities/finite-state-machine}
|
|
5
|
-
*/
|
|
1
|
+
/** @typedef {import('./typedef.js').StateTransitionMetadata} StateTransitionMetadata */
|
|
2
|
+
/** @typedef {import('./typedef.js').OnEnterCallback} OnEnterCallback */
|
|
3
|
+
/** @typedef {import('./typedef.js').OnExitCallback} OnExitCallback */
|
|
6
4
|
/**
|
|
7
5
|
* Check if the value is valid meta data
|
|
8
6
|
*
|
|
@@ -10,9 +8,9 @@
|
|
|
10
8
|
*/
|
|
11
9
|
export function isLifecycleFnMeta(meta: any): boolean;
|
|
12
10
|
/**
|
|
13
|
-
* Defines a Finite State Machine
|
|
11
|
+
* Defines a Finite State Machine that extends EventEmitter
|
|
14
12
|
*/
|
|
15
|
-
export default class FiniteStateMachine {
|
|
13
|
+
export default class FiniteStateMachine extends EventEmitter {
|
|
16
14
|
/**
|
|
17
15
|
* Constructor
|
|
18
16
|
*
|
|
@@ -29,6 +27,10 @@ export default class FiniteStateMachine {
|
|
|
29
27
|
[key: string]: string | ((...args: any[]) => void);
|
|
30
28
|
};
|
|
31
29
|
};
|
|
30
|
+
/** @type {OnEnterCallback | null} */
|
|
31
|
+
onenter: OnEnterCallback | null;
|
|
32
|
+
/** @type {OnExitCallback | null} */
|
|
33
|
+
onexit: OnExitCallback | null;
|
|
32
34
|
/**
|
|
33
35
|
* Triggers a new event and returns the new state.
|
|
34
36
|
*
|
|
@@ -48,3 +50,7 @@ export default class FiniteStateMachine {
|
|
|
48
50
|
get current(): any;
|
|
49
51
|
#private;
|
|
50
52
|
}
|
|
53
|
+
export type StateTransitionMetadata = import("./typedef.js").StateTransitionMetadata;
|
|
54
|
+
export type OnEnterCallback = import("./typedef.js").OnEnterCallback;
|
|
55
|
+
export type OnExitCallback = import("./typedef.js").OnExitCallback;
|
|
56
|
+
import EventEmitter from '../../../generic/events/classes/EventEmitter.js';
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initial code borrowed from:
|
|
3
|
+
*
|
|
4
|
+
* @see {@link https://runed.dev/docs/utilities/finite-state-machine}
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { isTestEnv } from '../../../util/env.js';
|
|
8
|
+
import EventEmitter from '../../../generic/events/classes/EventEmitter.js';
|
|
9
|
+
import { ENTER, EXIT } from './constants.js';
|
|
10
|
+
|
|
11
|
+
/** @typedef {import('./typedef.js').StateTransitionMetadata} StateTransitionMetadata */
|
|
12
|
+
/** @typedef {import('./typedef.js').OnEnterCallback} OnEnterCallback */
|
|
13
|
+
/** @typedef {import('./typedef.js').OnExitCallback} OnExitCallback */
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if the value is valid meta data
|
|
17
|
+
*
|
|
18
|
+
* @param {any} meta
|
|
19
|
+
*/
|
|
20
|
+
export function isLifecycleFnMeta(meta) {
|
|
21
|
+
return (
|
|
22
|
+
!!meta &&
|
|
23
|
+
typeof meta === 'object' &&
|
|
24
|
+
'to' in meta &&
|
|
25
|
+
'from' in meta &&
|
|
26
|
+
'event' in meta &&
|
|
27
|
+
'args' in meta
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Defines a Finite State Machine that extends EventEmitter
|
|
33
|
+
*/
|
|
34
|
+
export default class FiniteStateMachine extends EventEmitter {
|
|
35
|
+
#current = $state();
|
|
36
|
+
states;
|
|
37
|
+
#timeout = {};
|
|
38
|
+
|
|
39
|
+
/** @type {OnEnterCallback | null} */
|
|
40
|
+
onenter = null;
|
|
41
|
+
|
|
42
|
+
/** @type {OnExitCallback | null} */
|
|
43
|
+
onexit = null;
|
|
44
|
+
|
|
45
|
+
/** @type {boolean} */
|
|
46
|
+
#enableConsoleWarnings = !isTestEnv;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Constructor
|
|
50
|
+
*
|
|
51
|
+
* @param {string} initial
|
|
52
|
+
* @param {{ [key: string]: { [key: string]: (string|((...args: any[])=>void)) } }} states
|
|
53
|
+
*/
|
|
54
|
+
constructor(initial, states) {
|
|
55
|
+
super();
|
|
56
|
+
this.#current = initial;
|
|
57
|
+
this.states = states;
|
|
58
|
+
|
|
59
|
+
// synthetically trigger _enter for the initial state.
|
|
60
|
+
const initialMetadata = {
|
|
61
|
+
from: null,
|
|
62
|
+
to: initial,
|
|
63
|
+
event: null,
|
|
64
|
+
args: []
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
this.#executeAction('_enter', initialMetadata);
|
|
68
|
+
|
|
69
|
+
// Emit ENTER event for external listeners for initial state
|
|
70
|
+
this.emit(ENTER, { state: initial, metadata: initialMetadata });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Transition to new state
|
|
75
|
+
*
|
|
76
|
+
* @param {string} newState
|
|
77
|
+
* @param {string} event
|
|
78
|
+
* @param {any[]} [args]
|
|
79
|
+
*/
|
|
80
|
+
#transition(newState, event, args) {
|
|
81
|
+
/** @type {StateTransitionMetadata} */
|
|
82
|
+
const metadata = { from: this.#current, to: newState, event, args };
|
|
83
|
+
|
|
84
|
+
// Call onexit callback before leaving current state
|
|
85
|
+
this.onexit?.(this.#current, metadata);
|
|
86
|
+
|
|
87
|
+
// Emit EXIT event for external listeners
|
|
88
|
+
this.emit(EXIT, { state: this.#current, metadata });
|
|
89
|
+
|
|
90
|
+
this.#executeAction('_exit', metadata);
|
|
91
|
+
this.#current = newState;
|
|
92
|
+
this.#executeAction('_enter', metadata);
|
|
93
|
+
|
|
94
|
+
// Emit ENTER event for external listeners
|
|
95
|
+
this.emit(ENTER, { state: newState, metadata });
|
|
96
|
+
|
|
97
|
+
// Call onenter callback after state change
|
|
98
|
+
this.onenter?.(newState, metadata);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Execute an action for the given event
|
|
103
|
+
*
|
|
104
|
+
* @param {string} event
|
|
105
|
+
* @param {any} args
|
|
106
|
+
*/
|
|
107
|
+
#executeAction(event, ...args) {
|
|
108
|
+
const action =
|
|
109
|
+
this.states[this.#current]?.[event] ?? this.states['*']?.[event];
|
|
110
|
+
|
|
111
|
+
if (action instanceof Function) {
|
|
112
|
+
switch (event) {
|
|
113
|
+
// Internal lifecycle events
|
|
114
|
+
case ENTER:
|
|
115
|
+
case EXIT:
|
|
116
|
+
if (isLifecycleFnMeta(args[0])) {
|
|
117
|
+
return action(args[0]);
|
|
118
|
+
} else {
|
|
119
|
+
throw new Error(`Invalid metadata passed to lifecycle function`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Normal state events
|
|
123
|
+
default:
|
|
124
|
+
return action(...args);
|
|
125
|
+
}
|
|
126
|
+
} else if (typeof action === 'string') {
|
|
127
|
+
// No function execution => just return target state
|
|
128
|
+
return action;
|
|
129
|
+
} else {
|
|
130
|
+
// No action found - only warn for non-lifecycle events
|
|
131
|
+
if (event !== ENTER && event !== EXIT) {
|
|
132
|
+
if (this.#enableConsoleWarnings) {
|
|
133
|
+
console.warn(
|
|
134
|
+
'No action defined for event',
|
|
135
|
+
event,
|
|
136
|
+
'in state',
|
|
137
|
+
this.#current
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Triggers a new event and returns the new state.
|
|
145
|
+
*
|
|
146
|
+
* @param {string} event
|
|
147
|
+
* @param {any[]} args
|
|
148
|
+
*/
|
|
149
|
+
send(event, ...args) {
|
|
150
|
+
const newState = this.#executeAction(event, ...args);
|
|
151
|
+
|
|
152
|
+
if (newState && newState !== this.#current) {
|
|
153
|
+
this.#transition(newState, event, args);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return this.#current;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Debounces the triggering of an event.
|
|
160
|
+
*
|
|
161
|
+
* @param {number} wait
|
|
162
|
+
* @param {string} event
|
|
163
|
+
* @param {any[]} args
|
|
164
|
+
*/
|
|
165
|
+
async debounce(wait = 500, event, ...args) {
|
|
166
|
+
if (this.#timeout[event]) {
|
|
167
|
+
clearTimeout(this.#timeout[event]);
|
|
168
|
+
}
|
|
169
|
+
return new Promise((resolve) => {
|
|
170
|
+
this.#timeout[event] = setTimeout(() => {
|
|
171
|
+
delete this.#timeout[event];
|
|
172
|
+
resolve(this.send(event, ...args));
|
|
173
|
+
}, wait);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** The current state. */
|
|
178
|
+
get current() {
|
|
179
|
+
return this.#current;
|
|
180
|
+
}
|
|
181
|
+
}
|