@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.
Files changed (42) hide show
  1. package/dist/logging/internal/adapters/pino.d.ts +7 -3
  2. package/dist/logging/internal/adapters/pino.js +200 -67
  3. package/dist/logging/internal/transports/pretty-transport.d.ts +17 -0
  4. package/dist/logging/internal/transports/pretty-transport.js +104 -0
  5. package/dist/logging/internal/transports/test-transport.d.ts +19 -0
  6. package/dist/logging/internal/transports/test-transport.js +79 -0
  7. package/dist/network/loaders/audio/AudioScene.svelte.js +2 -2
  8. package/dist/network/loaders/image/ImageScene.svelte.js +18 -28
  9. package/dist/network/states/NetworkLoader.svelte.d.ts +7 -1
  10. package/dist/network/states/NetworkLoader.svelte.js +17 -4
  11. package/dist/services/README.md +23 -0
  12. package/dist/services/service-base/ServiceBase.d.ts +12 -8
  13. package/dist/services/service-base/ServiceBase.js +8 -6
  14. package/dist/state/classes.d.ts +0 -2
  15. package/dist/state/classes.js +0 -2
  16. package/dist/state/{classes → machines}/finite-state-machine/FiniteStateMachine.svelte.d.ts +13 -7
  17. package/dist/state/machines/finite-state-machine/FiniteStateMachine.svelte.js +181 -0
  18. package/dist/state/machines/finite-state-machine/README.md +547 -0
  19. package/dist/state/machines/finite-state-machine/constants.d.ts +13 -0
  20. package/dist/state/machines/finite-state-machine/constants.js +15 -0
  21. package/dist/state/{classes → machines}/finite-state-machine/index.d.ts +2 -1
  22. package/dist/state/{classes → machines}/finite-state-machine/index.js +2 -1
  23. package/dist/state/machines/finite-state-machine/typedef.d.ts +29 -0
  24. package/dist/state/machines/finite-state-machine/typedef.js +28 -0
  25. package/dist/state/machines/loading-state-machine/LoadingStateMachine.svelte.d.ts +22 -0
  26. package/dist/state/{classes → machines}/loading-state-machine/LoadingStateMachine.svelte.js +34 -29
  27. package/dist/state/machines/loading-state-machine/README.md +592 -0
  28. package/dist/state/{classes → machines}/loading-state-machine/constants.d.ts +2 -0
  29. package/dist/state/{classes → machines}/loading-state-machine/constants.js +2 -0
  30. package/dist/state/machines/typedef.d.ts +1 -0
  31. package/dist/state/machines/typedef.js +1 -0
  32. package/dist/state/machines.d.ts +2 -0
  33. package/dist/state/machines.js +2 -0
  34. package/dist/state/typedef.d.ts +1 -0
  35. package/dist/state/typedef.js +1 -0
  36. package/dist/ui/components/game-box/README.md +245 -0
  37. package/package.json +1 -1
  38. package/dist/logging/internal/adapters/pino.js__ +0 -260
  39. package/dist/state/classes/finite-state-machine/FiniteStateMachine.svelte.js +0 -133
  40. package/dist/state/classes/loading-state-machine/LoadingStateMachine.svelte.d.ts +0 -12
  41. /package/dist/state/{classes → machines}/loading-state-machine/index.d.ts +0 -0
  42. /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/classes.js';
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/classes/loading-state-machine/constants.js';
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
- // console.log(
86
- // 'progress',
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
- $effect(() => {
100
- switch (state.current) {
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.error);
130
+ console.log('ImageScene:error', state);
133
131
  }
134
132
  break;
135
133
  } // end switch
136
134
 
137
- this.state = state.current;
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
- // FIXME: in unit test when moved to startloading it hangs!
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/classes.js';
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/classes.js';
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
- } from '../../state/classes/loading-state-machine/constants.js';
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
- // TODO
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
@@ -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
- * @private
159
+ * @protected
160
+ *
160
161
  * @param {ServiceState} newState - New state value
161
162
  * @emits {StateChangeEvent} EVENT_STATE_CHANGED
162
163
  */
163
- private _setState;
164
+ protected _setState(newState: ServiceState): void;
164
165
  /**
165
166
  * Set the service target state and emit event
166
167
  *
167
- * @private
168
+ * @protected
169
+ *
168
170
  * @param {ServiceState} newTargetState - New target state value
169
171
  * @emits {TargetStateChangeEvent} EVENT_TARGET_STATE_CHANGED
170
172
  */
171
- private _setTargetState;
173
+ protected _setTargetState(newTargetState: ServiceState): void;
172
174
  /**
173
175
  * Set the health status and emit event if changed
174
176
  *
175
- * @private
177
+ * @protected
178
+ *
176
179
  * @param {boolean} healthy - New health status
177
180
  * @emits {HealthChangeEvent} EVENT_HEALTH_CHANGED
178
181
  */
179
- private _setHealthy;
182
+ protected _setHealthy(healthy: boolean): void;
180
183
  /**
181
184
  * Set error state and emit error event
182
185
  *
183
- * @private
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
- private _setError;
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
- * @private
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
- * @private
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
- * @private
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
- * @private
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
@@ -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,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
- * Initial code borrowed from:
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
+ }