@hkdigital/lib-core 0.4.24 → 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 (25) 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/states/NetworkLoader.svelte.d.ts +6 -0
  8. package/dist/network/states/NetworkLoader.svelte.js +15 -2
  9. package/dist/services/service-base/ServiceBase.d.ts +12 -8
  10. package/dist/services/service-base/ServiceBase.js +8 -6
  11. package/dist/state/machines/finite-state-machine/FiniteStateMachine.svelte.d.ts +3 -7
  12. package/dist/state/machines/finite-state-machine/FiniteStateMachine.svelte.js +57 -27
  13. package/dist/state/machines/finite-state-machine/README.md +17 -15
  14. package/dist/state/machines/finite-state-machine/constants.d.ts +13 -0
  15. package/dist/state/machines/finite-state-machine/constants.js +15 -0
  16. package/dist/state/machines/finite-state-machine/index.d.ts +1 -0
  17. package/dist/state/machines/finite-state-machine/index.js +1 -0
  18. package/dist/state/machines/finite-state-machine/typedef.js +5 -5
  19. package/dist/state/machines/loading-state-machine/LoadingStateMachine.svelte.d.ts +12 -0
  20. package/dist/state/machines/loading-state-machine/LoadingStateMachine.svelte.js +27 -2
  21. package/dist/state/machines/loading-state-machine/README.md +66 -18
  22. package/dist/state/machines/loading-state-machine/constants.d.ts +2 -0
  23. package/dist/state/machines/loading-state-machine/constants.js +2 -0
  24. package/package.json +1 -1
  25. package/dist/logging/internal/adapters/pino.js__ +0 -260
@@ -13,7 +13,8 @@ import {
13
13
  ERROR,
14
14
  LOADED,
15
15
  UNLOAD,
16
- INITIAL
16
+ INITIAL,
17
+ CANCEL
17
18
  } from '../../state/machines.js';
18
19
 
19
20
  import * as expect from '../../util/expect.js';
@@ -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
@@ -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,8 +1,3 @@
1
- /**
2
- * Initial code borrowed from:
3
- *
4
- * @see {@link https://runed.dev/docs/utilities/finite-state-machine}
5
- */
6
1
  /** @typedef {import('./typedef.js').StateTransitionMetadata} StateTransitionMetadata */
7
2
  /** @typedef {import('./typedef.js').OnEnterCallback} OnEnterCallback */
8
3
  /** @typedef {import('./typedef.js').OnExitCallback} OnExitCallback */
@@ -13,9 +8,9 @@
13
8
  */
14
9
  export function isLifecycleFnMeta(meta: any): boolean;
15
10
  /**
16
- * Defines a Finite State Machine
11
+ * Defines a Finite State Machine that extends EventEmitter
17
12
  */
18
- export default class FiniteStateMachine {
13
+ export default class FiniteStateMachine extends EventEmitter {
19
14
  /**
20
15
  * Constructor
21
16
  *
@@ -58,3 +53,4 @@ export default class FiniteStateMachine {
58
53
  export type StateTransitionMetadata = import("./typedef.js").StateTransitionMetadata;
59
54
  export type OnEnterCallback = import("./typedef.js").OnEnterCallback;
60
55
  export type OnExitCallback = import("./typedef.js").OnExitCallback;
56
+ import EventEmitter from '../../../generic/events/classes/EventEmitter.js';
@@ -4,6 +4,10 @@
4
4
  * @see {@link https://runed.dev/docs/utilities/finite-state-machine}
5
5
  */
6
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
+
7
11
  /** @typedef {import('./typedef.js').StateTransitionMetadata} StateTransitionMetadata */
8
12
  /** @typedef {import('./typedef.js').OnEnterCallback} OnEnterCallback */
9
13
  /** @typedef {import('./typedef.js').OnExitCallback} OnExitCallback */
@@ -25,9 +29,9 @@ export function isLifecycleFnMeta(meta) {
25
29
  }
26
30
 
27
31
  /**
28
- * Defines a Finite State Machine
32
+ * Defines a Finite State Machine that extends EventEmitter
29
33
  */
30
- export default class FiniteStateMachine {
34
+ export default class FiniteStateMachine extends EventEmitter {
31
35
  #current = $state();
32
36
  states;
33
37
  #timeout = {};
@@ -38,6 +42,9 @@ export default class FiniteStateMachine {
38
42
  /** @type {OnExitCallback | null} */
39
43
  onexit = null;
40
44
 
45
+ /** @type {boolean} */
46
+ #enableConsoleWarnings = !isTestEnv;
47
+
41
48
  /**
42
49
  * Constructor
43
50
  *
@@ -45,16 +52,22 @@ export default class FiniteStateMachine {
45
52
  * @param {{ [key: string]: { [key: string]: (string|((...args: any[])=>void)) } }} states
46
53
  */
47
54
  constructor(initial, states) {
55
+ super();
48
56
  this.#current = initial;
49
57
  this.states = states;
50
58
 
51
59
  // synthetically trigger _enter for the initial state.
52
- this.#dispatch('_enter', {
60
+ const initialMetadata = {
53
61
  from: null,
54
62
  to: initial,
55
63
  event: null,
56
64
  args: []
57
- });
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 });
58
71
  }
59
72
 
60
73
  /**
@@ -70,45 +83,61 @@ export default class FiniteStateMachine {
70
83
 
71
84
  // Call onexit callback before leaving current state
72
85
  this.onexit?.(this.#current, metadata);
73
-
74
- this.#dispatch('_exit', metadata);
86
+
87
+ // Emit EXIT event for external listeners
88
+ this.emit(EXIT, { state: this.#current, metadata });
89
+
90
+ this.#executeAction('_exit', metadata);
75
91
  this.#current = newState;
76
- this.#dispatch('_enter', metadata);
77
-
92
+ this.#executeAction('_enter', metadata);
93
+
94
+ // Emit ENTER event for external listeners
95
+ this.emit(ENTER, { state: newState, metadata });
96
+
78
97
  // Call onenter callback after state change
79
98
  this.onenter?.(newState, metadata);
80
99
  }
81
100
 
82
101
  /**
83
- * Dispatch an event
102
+ * Execute an action for the given event
84
103
  *
85
104
  * @param {string} event
86
105
  * @param {any} args
87
106
  */
88
- #dispatch(event, ...args) {
107
+ #executeAction(event, ...args) {
89
108
  const action =
90
109
  this.states[this.#current]?.[event] ?? this.states['*']?.[event];
110
+
91
111
  if (action instanceof Function) {
92
- if (event === '_enter' || event === '_exit') {
93
- if (isLifecycleFnMeta(args[0])) {
94
- action(args[0]);
95
- } else {
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) {
96
133
  console.warn(
97
- 'Invalid metadata passed to lifecycle function of the FSM.'
134
+ 'No action defined for event',
135
+ event,
136
+ 'in state',
137
+ this.#current
98
138
  );
99
139
  }
100
- } else {
101
- return action(...args);
102
140
  }
103
- } else if (typeof action === 'string') {
104
- return action;
105
- } else if (event !== '_enter' && event !== '_exit') {
106
- console.warn(
107
- 'No action defined for event',
108
- event,
109
- 'in state',
110
- this.#current
111
- );
112
141
  }
113
142
  }
114
143
  /**
@@ -118,7 +147,8 @@ export default class FiniteStateMachine {
118
147
  * @param {any[]} args
119
148
  */
120
149
  send(event, ...args) {
121
- const newState = this.#dispatch(event, ...args);
150
+ const newState = this.#executeAction(event, ...args);
151
+
122
152
  if (newState && newState !== this.#current) {
123
153
  this.#transition(newState, event, args);
124
154
  }
@@ -39,7 +39,7 @@ console.log(machine.current); // 'paused'
39
39
  ## Constructor
40
40
 
41
41
  ```javascript
42
- new FiniteStateMachine(initialState, states)
42
+ new FiniteStateMachine(initialState, states);
43
43
  ```
44
44
 
45
45
  - `initialState`: The starting state (string)
@@ -160,7 +160,7 @@ const machine = new FiniteStateMachine('idle', {
160
160
  resume: 'running'
161
161
  },
162
162
  '*': {
163
- reset: 'idle', // Available from any state
163
+ reset: 'idle', // Available from any state
164
164
  error: 'error'
165
165
  },
166
166
  error: {
@@ -171,7 +171,7 @@ const machine = new FiniteStateMachine('idle', {
171
171
 
172
172
  ### Same-State Transitions
173
173
 
174
- Same-state transitions (e.g., `idle → idle`) do NOT trigger enter/exit
174
+ Same-state transitions (e.g., `idle → idle`) do NOT trigger enter/exit
175
175
  callbacks. The state remains unchanged.
176
176
 
177
177
  ```javascript
@@ -247,7 +247,7 @@ machine.onenter = (state, metadata) => {
247
247
  The callbacks are executed in this specific order during state transitions:
248
248
 
249
249
  1. **`onexit`** - Called before leaving current state
250
- 2. **`_exit`** - Individual state exit callback
250
+ 2. **`_exit`** - Individual state exit callback
251
251
  3. **`_enter`** - Individual state enter callback
252
252
  4. **`onenter`** - Called after entering new state
253
253
 
@@ -268,7 +268,7 @@ machine.onexit = (state) => console.log(`3. onexit ${state}`);
268
268
  machine.onenter = (state) => console.log(`6. onenter ${state}`);
269
269
 
270
270
  // Initial state triggers _enter and onenter
271
- // Output:
271
+ // Output:
272
272
  // 2. idle _enter
273
273
  // 6. onenter idle
274
274
 
@@ -276,7 +276,7 @@ machine.send('start');
276
276
  // Output:
277
277
  // 3. onexit idle
278
278
  // 4. idle _exit
279
- // 5. loading _enter
279
+ // 5. loading _enter
280
280
  // 6. onenter loading
281
281
  ```
282
282
 
@@ -294,6 +294,7 @@ When using FiniteStateMachine with Svelte's reactive derived state, use this pat
294
294
  ### Pattern: Separate onenter from Reactive Monitoring
295
295
 
296
296
  **✅ Use `onexit` and `onenter` for immediate state actions:**
297
+
297
298
  ```javascript
298
299
  const machine = new FiniteStateMachine('idle', {
299
300
  idle: { start: 'loading' },
@@ -326,6 +327,7 @@ machine.onenter = (state) => {
326
327
  ```
327
328
 
328
329
  **✅ Use `$effect` for reactive state monitoring:**
330
+
329
331
  ```javascript
330
332
  // Monitor derived/computed values and trigger transitions when conditions are met
331
333
  $effect(() => {
@@ -341,8 +343,8 @@ $effect(() => {
341
343
  ### Why This Pattern?
342
344
 
343
345
  - **`onexit`**: Handles cleanup and teardown when leaving states
344
- - **`onenter`**: Handles setup and initialization when entering states
345
- - **`$effect`**: Handles reactive monitoring of derived/computed values over time
346
+ - **`onenter`**: Handles setup and initialization when entering states
347
+ - **`$effect`**: Handles reactive monitoring of derived/computed values over time
346
348
  - **Avoids timing issues**: Doesn't check completion status immediately on state entry
347
349
  - **Leverages Svelte reactivity**: Automatically responds to changes in reactive variables
348
350
  - **Clean separation**: State machine handles discrete transitions, effects handle continuous monitoring
@@ -365,9 +367,9 @@ export default class TaskProcessor {
365
367
  finished: { reset: 'idle' },
366
368
  failed: { retry: 'processing', reset: 'idle' }
367
369
  });
368
-
370
+
369
371
  #tasks = $state([]);
370
-
372
+
371
373
  // Derived progress calculation
372
374
  #progress = $derived.by(() => {
373
375
  let completed = 0;
@@ -419,19 +421,19 @@ export default class TaskProcessor {
419
421
  // Component.svelte
420
422
  <script>
421
423
  import { FiniteStateMachine } from '$lib/state/classes.js';
422
-
424
+
423
425
  const machine = new FiniteStateMachine('idle', {
424
426
  idle: { start: 'loading' },
425
427
  loading: { complete: 'loaded', error: 'error' },
426
428
  loaded: { reset: 'idle' },
427
429
  error: { retry: 'loading', reset: 'idle' }
428
430
  });
429
-
431
+
430
432
  // Reactive state updates
431
433
  $effect(() => {
432
434
  console.log('State changed to:', machine.current);
433
435
  });
434
-
436
+
435
437
  // Handle state-specific actions
436
438
  machine.onexit = (state) => {
437
439
  switch (state) {
@@ -475,7 +477,7 @@ console.log(machine.current); // Still 'idle'
475
477
 
476
478
  ## Best Practices
477
479
 
478
- 1. **Clear state names**: Use descriptive state names like `loading`, `error`,
480
+ 1. **Clear state names**: Use descriptive state names like `loading`, `error`,
479
481
  `authenticated` rather than generic ones
480
482
  2. **Minimal state count**: Keep the number of states manageable
481
483
  3. **Explicit transitions**: Define all valid transitions explicitly
@@ -542,4 +544,4 @@ The state machine includes comprehensive tests covering:
542
544
  - Immediate state access
543
545
  - Callback execution order
544
546
 
545
- See `FiniteStateMachine.test.js` for detailed examples.
547
+ See `FiniteStateMachine.test.js` for detailed examples.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview Constants for FiniteStateMachine events
3
+ */
4
+ /**
5
+ * Event emitted when entering a state
6
+ * @type {string}
7
+ */
8
+ export const ENTER: string;
9
+ /**
10
+ * Event emitted when exiting a state
11
+ * @type {string}
12
+ */
13
+ export const EXIT: string;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @fileoverview Constants for FiniteStateMachine events
3
+ */
4
+
5
+ /**
6
+ * Event emitted when entering a state
7
+ * @type {string}
8
+ */
9
+ export const ENTER = 'enter';
10
+
11
+ /**
12
+ * Event emitted when exiting a state
13
+ * @type {string}
14
+ */
15
+ export const EXIT = 'exit';
@@ -1 +1,2 @@
1
1
  export { default as FiniteStateMachine } from "./FiniteStateMachine.svelte.js";
2
+ export * from "./constants.js";
@@ -1 +1,2 @@
1
1
  export { default as FiniteStateMachine } from './FiniteStateMachine.svelte.js';
2
+ export * from './constants.js';
@@ -4,25 +4,25 @@
4
4
 
5
5
  /**
6
6
  * Metadata object passed to state transition callbacks
7
- *
7
+ *
8
8
  * @typedef {object} StateTransitionMetadata
9
9
  * @property {string} from - The state being exited
10
- * @property {string} to - The state being entered
10
+ * @property {string} to - The state being entered
11
11
  * @property {string} event - The event that triggered the transition
12
12
  * @property {any[]} args - Arguments passed to the send() method
13
13
  */
14
14
 
15
15
  /**
16
16
  * Callback function called when entering a state
17
- *
17
+ *
18
18
  * @typedef {function(string, StateTransitionMetadata): void} OnEnterCallback
19
19
  */
20
20
 
21
21
  /**
22
22
  * Callback function called when exiting a state
23
- *
23
+ *
24
24
  * @typedef {function(string, StateTransitionMetadata): void} OnExitCallback
25
25
  */
26
26
 
27
27
  // Export types for external use (this is just for JSDoc, no actual exports needed)
28
- export {};
28
+ export {};
@@ -5,6 +5,18 @@ export default class LoadingStateMachine extends FiniteStateMachine {
5
5
  constructor();
6
6
  /** The last error */
7
7
  get error(): Error;
8
+ /**
9
+ * Transition to timeout state
10
+ * - Only valid when currently loading
11
+ * - Useful for external timeout management
12
+ */
13
+ doTimeout(): void;
14
+ /**
15
+ * Transition to cancelled state
16
+ * - Only valid when currently loading
17
+ * - Useful for external cancellation management
18
+ */
19
+ doCancel(): void;
8
20
  #private;
9
21
  }
10
22
  import FiniteStateMachine from '../finite-state-machine/FiniteStateMachine.svelte.js';
@@ -10,6 +10,7 @@ import {
10
10
  STATE_LOADED,
11
11
  STATE_CANCELLED,
12
12
  STATE_ERROR,
13
+ STATE_TIMEOUT,
13
14
 
14
15
  // > Signals
15
16
  INITIAL,
@@ -17,7 +18,8 @@ import {
17
18
  CANCEL,
18
19
  ERROR,
19
20
  LOADED,
20
- UNLOAD
21
+ UNLOAD,
22
+ TIMEOUT
21
23
  } from './constants.js';
22
24
 
23
25
  /**
@@ -41,7 +43,8 @@ export default class LoadingStateMachine extends FiniteStateMachine {
41
43
  // },
42
44
  [CANCEL]: STATE_CANCELLED,
43
45
  [ERROR]: STATE_ERROR,
44
- [LOADED]: STATE_LOADED
46
+ [LOADED]: STATE_LOADED,
47
+ [TIMEOUT]: STATE_TIMEOUT
45
48
  },
46
49
  [STATE_LOADED]: {
47
50
  // _enter: () => {
@@ -58,6 +61,10 @@ export default class LoadingStateMachine extends FiniteStateMachine {
58
61
  [LOAD]: STATE_LOADING,
59
62
  [UNLOAD]: STATE_UNLOADING
60
63
  },
64
+ [STATE_TIMEOUT]: {
65
+ [LOAD]: STATE_LOADING,
66
+ [UNLOAD]: STATE_UNLOADING
67
+ },
61
68
  [STATE_ERROR]: {
62
69
  _enter: ({ /*from, to, event,*/ args }) => {
63
70
  if (args[0] instanceof Error) {
@@ -86,4 +93,22 @@ export default class LoadingStateMachine extends FiniteStateMachine {
86
93
  get error() {
87
94
  return this.#error;
88
95
  }
96
+
97
+ /**
98
+ * Transition to timeout state
99
+ * - Only valid when currently loading
100
+ * - Useful for external timeout management
101
+ */
102
+ doTimeout() {
103
+ this.send(TIMEOUT);
104
+ }
105
+
106
+ /**
107
+ * Transition to cancelled state
108
+ * - Only valid when currently loading
109
+ * - Useful for external cancellation management
110
+ */
111
+ doCancel() {
112
+ this.send(CANCEL);
113
+ }
89
114
  }