@hkdigital/lib-core 0.5.50 → 0.5.52

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.
@@ -1,3 +1,6 @@
1
+ /** @typedef {import('../../../generic/typedef.js').ErrorDetails} ErrorDetails */
2
+ /** @typedef {import('../../typedef.js').LogEvent} LogEvent */
3
+ /** @typedef {import('../../typedef.js').LogEventData} LogEventData */
1
4
  /** @typedef {import('../../typedef.js').LogLevel} LogLevel */
2
5
  /**
3
6
  * Logger class for consistent logging
@@ -63,7 +66,7 @@ export default class Logger extends EventEmitter {
63
66
  */
64
67
  error(errorOrMessage: Error | ErrorEvent | PromiseRejectionEvent | string, errorOrDetails?: Error | ErrorDetails, details?: ErrorDetails, ...args: any[]): boolean;
65
68
  /**
66
- * Create a child logger with additional context
69
+ * New logger with same name, namespaced context, no event forwarding
67
70
  *
68
71
  * @param {string} namespace
69
72
  * Namespace of the context (needed for chaining contexts)
@@ -72,17 +75,33 @@ export default class Logger extends EventEmitter {
72
75
  * @returns {Logger} New logger instance with merged context
73
76
  */
74
77
  context(namespace: string, additionalContext: Object): Logger;
78
+ /**
79
+ * Create a child logger that emits events on the current logger
80
+ *
81
+ * @param {string} name
82
+ * Child logger name (will be appended to parent name)
83
+ *
84
+ * @param {object} [options]
85
+ * @param {LogLevel} [options.level] - Child logger log level
86
+ *
87
+ * @returns {Logger} New child logger instance
88
+ */
89
+ child(name: string, { level }?: {
90
+ level?: import("../../typedef.js").LogLevel | undefined;
91
+ }): Logger;
75
92
  /**
76
93
  * Log an LogEvent emitted by an event emitter
77
94
  *
78
95
  * E.g. an event that was created by another Logger instance and should be
79
96
  * forwarded to this logger.
80
97
  *
81
- * @param {import('../../typedef.js').LogEventData} eventData
98
+ * @param {LogEventData} eventData
82
99
  */
83
- logFromEvent(eventData: import("../../typedef.js").LogEventData): false | undefined;
100
+ logFromEvent(eventData: LogEventData): false | undefined;
84
101
  #private;
85
102
  }
86
103
  export type ErrorDetails = import("../../../generic/typedef.js").ErrorDetails;
104
+ export type LogEvent = import("../../typedef.js").LogEvent;
105
+ export type LogEventData = import("../../typedef.js").LogEventData;
87
106
  export type LogLevel = import("../../typedef.js").LogLevel;
88
107
  import { EventEmitter } from '../../../generic/events.js';
@@ -32,8 +32,6 @@
32
32
  * logger.setLevel(DEBUG); // Now debug messages will also be logged
33
33
  */
34
34
 
35
- /** @typedef {import('../../../generic/typedef.js').ErrorDetails} ErrorDetails */
36
-
37
35
  import * as expect from '../../../util/expect.js';
38
36
 
39
37
  import { EventEmitter } from '../../../generic/events.js';
@@ -61,6 +59,11 @@ import { exportNotNullish } from '../../../util/object.js';
61
59
  import * as is from '../../../util/is.js';
62
60
  import { HttpError } from '../../../network/errors.js';
63
61
 
62
+ /** @typedef {import('../../../generic/typedef.js').ErrorDetails} ErrorDetails */
63
+
64
+ /** @typedef {import('../../typedef.js').LogEvent} LogEvent */
65
+ /** @typedef {import('../../typedef.js').LogEventData} LogEventData */
66
+
64
67
  /** @typedef {import('../../typedef.js').LogLevel} LogLevel */
65
68
 
66
69
  /**
@@ -220,7 +223,7 @@ export default class Logger extends EventEmitter {
220
223
  }
221
224
 
222
225
  /**
223
- * Create a child logger with additional context
226
+ * New logger with same name, namespaced context, no event forwarding
224
227
  *
225
228
  * @param {string} namespace
226
229
  * Namespace of the context (needed for chaining contexts)
@@ -241,13 +244,46 @@ export default class Logger extends EventEmitter {
241
244
  return new Logger(this.name, this.level, mergedContext);
242
245
  }
243
246
 
247
+ /**
248
+ * Create a child logger that emits events on the current logger
249
+ *
250
+ * @param {string} name
251
+ * Child logger name (will be appended to parent name)
252
+ *
253
+ * @param {object} [options]
254
+ * @param {LogLevel} [options.level] - Child logger log level
255
+ *
256
+ * @returns {Logger} New child logger instance
257
+ */
258
+ child(name, { level } = {}) {
259
+ if (typeof name !== 'string') {
260
+ throw new Error('Invalid child logger name');
261
+ }
262
+
263
+ const context = {
264
+ ...this.#defaultContext
265
+ };
266
+
267
+ const childName = `${this.name}:${name}`;
268
+
269
+ const child = new Logger(childName, level ?? this.level, context);
270
+
271
+ // Forward all log events from child to parent
272
+ child.on(LOG, ( /** @type {LogEvent} */ logEvent) => {
273
+ this.emit(logEvent.level, logEvent);
274
+ this.emit(LOG, logEvent);
275
+ });
276
+
277
+ return child;
278
+ }
279
+
244
280
  /**
245
281
  * Log an LogEvent emitted by an event emitter
246
282
  *
247
283
  * E.g. an event that was created by another Logger instance and should be
248
284
  * forwarded to this logger.
249
285
  *
250
- * @param {import('../../typedef.js').LogEventData} eventData
286
+ * @param {LogEventData} eventData
251
287
  */
252
288
  logFromEvent(eventData) {
253
289
  expect.object(eventData);
@@ -295,9 +331,9 @@ export default class Logger extends EventEmitter {
295
331
  }
296
332
 
297
333
  /**
298
- * Internal event loggin method
334
+ * Internal event logging method
299
335
  *
300
- * @param {import('../../typedef.js').LogEvent} logEvent
336
+ * @param {LogEvent} logEvent
301
337
  */
302
338
  #logEvent(logEvent) {
303
339
  // Emit as both specific level event and generic 'log' event
@@ -344,6 +380,7 @@ export default class Logger extends EventEmitter {
344
380
  'colno'
345
381
  ]);
346
382
 
383
+ // @ts-ignore
347
384
  errorEventDetails.type = 'ErrorEvent';
348
385
 
349
386
  let cause = errorEvent.error;
@@ -111,20 +111,13 @@ export default class PageMachine {
111
111
  * Synchronize machine state with URL path
112
112
  *
113
113
  * Call this in a $effect that watches $page.url.pathname
114
- * Automatically tracks visited states
114
+ * Automatically tracks visited states and triggers onEnter hooks
115
115
  *
116
116
  * @param {string} currentPath - Current URL pathname
117
117
  *
118
118
  * @returns {boolean} True if state was changed
119
119
  */
120
120
  syncFromPath(currentPath: string): boolean;
121
- /**
122
- * Set the current state directly
123
- * Handles onEnter hooks and auto-transitions
124
- *
125
- * @param {string} newState - Target state
126
- */
127
- setState(newState: string): Promise<void>;
128
121
  /**
129
122
  * Get route path for a given state
130
123
  *
@@ -241,7 +241,7 @@ export default class PageMachine {
241
241
  * Synchronize machine state with URL path
242
242
  *
243
243
  * Call this in a $effect that watches $page.url.pathname
244
- * Automatically tracks visited states
244
+ * Automatically tracks visited states and triggers onEnter hooks
245
245
  *
246
246
  * @param {string} currentPath - Current URL pathname
247
247
  *
@@ -251,13 +251,11 @@ export default class PageMachine {
251
251
  const targetState = this.#getStateFromPath(currentPath);
252
252
 
253
253
  if (targetState && targetState !== this.#current) {
254
- const oldState = this.#current;
255
- this.#current = targetState;
256
- this.#visitedStates.add(targetState);
257
- this.#revision++;
258
-
259
254
  // Log state transition from URL sync
260
- this.logger?.debug(`syncFromPath (url): ${oldState} → ${targetState}`);
255
+ this.logger?.debug(`syncFromPath: ${currentPath} → targetState: ${targetState}`);
256
+
257
+ // Use #setState to handle onEnter hooks
258
+ this.#setState(targetState);
261
259
 
262
260
  return true;
263
261
  }
@@ -266,12 +264,16 @@ export default class PageMachine {
266
264
  }
267
265
 
268
266
  /**
269
- * Set the current state directly
267
+ * Set the current state directly (internal use only)
270
268
  * Handles onEnter hooks and auto-transitions
271
269
  *
270
+ * Note: This is private to enforce URL-first navigation.
271
+ * To change state, navigate to the URL using switchToPage()
272
+ * and let syncFromPath() update the state.
273
+ *
272
274
  * @param {string} newState - Target state
273
275
  */
274
- async setState(newState) {
276
+ async #setState(newState) {
275
277
  if (newState === this.#current || this.#isTransitioning) {
276
278
  return;
277
279
  }
@@ -301,7 +303,7 @@ export default class PageMachine {
301
303
  if (!doneCalled && nextState && nextState !== newState) {
302
304
  doneCalled = true;
303
305
  this.#isTransitioning = false;
304
- this.setState(nextState);
306
+ this.#setState(nextState);
305
307
  }
306
308
  };
307
309
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-core",
3
- "version": "0.5.50",
3
+ "version": "0.5.52",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"