@hkdigital/lib-core 0.3.8 → 0.3.10

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,10 +1,3 @@
1
- /**
2
- * @typedef {object} Options
3
- * @property {boolean} walkArrays
4
- * @property {boolean} ignoreEmptyObjectLeaves
5
- * @property {boolean} expandPathKeys
6
- * @property {boolean} outputIntermediateNodes
7
- */
8
1
  export default class IterableTree {
9
2
  /**
10
3
  * Construct an object that can be used to iterate paths and values in
@@ -13,13 +6,10 @@ export default class IterableTree {
13
6
  * - Iterates "own properties" only (not inherited properties)
14
7
  *
15
8
  * @param {object} obj - Object to iterate
16
- * @param {Options} [options]
9
+ * @param {import('./typedef.js').IterableTreeOptions} [options]
17
10
  * @param {string[]} [_parentArrPath]
18
- *
19
- *
20
- * @return {Iterator} iterable object
21
11
  */
22
- constructor(obj: object, options?: Options, _parentArrPath?: string[]);
12
+ constructor(obj: object, options?: import("./typedef.js").IterableTreeOptions, _parentArrPath?: string[]);
23
13
  obj: any;
24
14
  _parentArrPath: string[];
25
15
  /**
@@ -43,9 +33,3 @@ export default class IterableTree {
43
33
  values(): Iterator<any, any, any>;
44
34
  #private;
45
35
  }
46
- export type Options = {
47
- walkArrays: boolean;
48
- ignoreEmptyObjectLeaves: boolean;
49
- expandPathKeys: boolean;
50
- outputIntermediateNodes: boolean;
51
- };
@@ -6,20 +6,9 @@ import { PATH_SEPARATOR } from '../../util/object/index.js';
6
6
 
7
7
  /* ------------------------------------------------------------------ Typedef */
8
8
 
9
- /**
10
- * @typedef {object} Options
11
- * @property {boolean} walkArrays
12
- * @property {boolean} ignoreEmptyObjectLeaves
13
- * @property {boolean} expandPathKeys
14
- * @property {boolean} outputIntermediateNodes
15
- */
16
-
17
9
  /* ------------------------------------------------------------------ Exports */
18
10
 
19
11
  export default class IterableTree {
20
- /**
21
- * @type {Options}
22
- */
23
12
  #options;
24
13
 
25
14
  /**
@@ -29,11 +18,8 @@ export default class IterableTree {
29
18
  * - Iterates "own properties" only (not inherited properties)
30
19
  *
31
20
  * @param {object} obj - Object to iterate
32
- * @param {Options} [options]
21
+ * @param {import('./typedef.js').IterableTreeOptions} [options]
33
22
  * @param {string[]} [_parentArrPath]
34
- *
35
- *
36
- * @return {Iterator} iterable object
37
23
  */
38
24
  constructor(obj, options, _parentArrPath) {
39
25
  //super( ...arguments );
@@ -68,7 +54,7 @@ export default class IterableTree {
68
54
  const obj = this.obj;
69
55
  const parentArrPath = this._parentArrPath;
70
56
 
71
- const options = this.options;
57
+ const options = this.#options;
72
58
 
73
59
  let { expandPathKeys, ignoreEmptyObjectLeaves, outputIntermediateNodes } = this.#options;
74
60
 
@@ -0,0 +1,8 @@
1
+ declare const _default: {};
2
+ export default _default;
3
+ export type IterableTreeOptions = {
4
+ walkArrays: boolean;
5
+ ignoreEmptyObjectLeaves: boolean;
6
+ expandPathKeys: boolean;
7
+ outputIntermediateNodes: boolean;
8
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @typedef {object} IterableTreeOptions
3
+ * @property {boolean} walkArrays
4
+ * @property {boolean} ignoreEmptyObjectLeaves
5
+ * @property {boolean} expandPathKeys
6
+ * @property {boolean} outputIntermediateNodes
7
+ */
8
+
9
+ export default {};
@@ -1,34 +1,34 @@
1
1
  /**
2
2
  * @fileoverview Simple event emitter implementation to support event-based architecture
3
- * in service management and other parts of the application.
4
- *
3
+ * in service management and other parts of the application.
4
+ *
5
5
  * This implementation provides standard event publishing and subscription methods
6
6
  * with support for namespaced events, wildcard listeners, and callback removal.
7
7
  *
8
8
  * @example
9
9
  * // Basic usage
10
10
  * import { EventEmitter } from './EventEmitter.js';
11
- *
11
+ *
12
12
  * // Create an emitter
13
13
  * const events = new EventEmitter();
14
- *
14
+ *
15
15
  * // Subscribe to events
16
16
  * const unsubscribe = events.on('data-loaded', (data) => {
17
17
  * console.log('Data loaded:', data);
18
18
  * });
19
- *
19
+ *
20
20
  * // Subscribe to all events with a specific prefix
21
21
  * events.on('database:*', ({ event, data }) => {
22
22
  * console.log(`Database event ${event}:`, data);
23
23
  * });
24
- *
24
+ *
25
25
  * // Emit events
26
26
  * events.emit('data-loaded', { items: [1, 2, 3] });
27
27
  * events.emit('database:connected', { connectionId: 'abc123' });
28
- *
28
+ *
29
29
  * // Clean up when done
30
30
  * unsubscribe();
31
- *
31
+ *
32
32
  * // Or remove all listeners
33
33
  * events.removeAllListeners();
34
34
  */
@@ -47,7 +47,7 @@ export default class EventEmitter {
47
47
  * @private
48
48
  */
49
49
  this.eventHandlers = new Map();
50
-
50
+
51
51
  /**
52
52
  * Map to store wildcard event handlers (events ending with *)
53
53
  * @type {Map<string, Set<Function>>}
@@ -55,21 +55,21 @@ export default class EventEmitter {
55
55
  */
56
56
  this.wildcardHandlers = new Map();
57
57
  }
58
-
58
+
59
59
  /**
60
60
  * Register an event handler
61
- *
61
+ *
62
62
  * @param {string} eventName - Event name to listen for. Can use wildcard (*)
63
63
  * at the end to listen to all events with a specific prefix.
64
64
  * @param {Function} handler - Handler function to call when event is emitted
65
65
  * @returns {Function} Function to remove this specific handler
66
- *
66
+ *
67
67
  * @example
68
68
  * // Listen for a specific event
69
69
  * emitter.on('userLoggedIn', (user) => {
70
70
  * console.log(`User logged in: ${user.name}`);
71
71
  * });
72
- *
72
+ *
73
73
  * // Listen for all events with a prefix
74
74
  * emitter.on('api:*', ({ event, data }) => {
75
75
  * console.log(`API event ${event}:`, data);
@@ -79,37 +79,37 @@ export default class EventEmitter {
79
79
  if (typeof handler !== 'function') {
80
80
  throw new TypeError('Event handler must be a function');
81
81
  }
82
-
82
+
83
83
  // Handle wildcard listeners
84
84
  if (eventName.endsWith('*')) {
85
85
  const prefix = eventName.slice(0, -1);
86
-
86
+
87
87
  if (!this.wildcardHandlers.has(prefix)) {
88
88
  this.wildcardHandlers.set(prefix, new Set());
89
89
  }
90
-
90
+
91
91
  this.wildcardHandlers.get(prefix).add(handler);
92
-
92
+
93
93
  return () => this.off(eventName, handler);
94
94
  }
95
-
95
+
96
96
  // Handle normal listeners
97
97
  if (!this.eventHandlers.has(eventName)) {
98
98
  this.eventHandlers.set(eventName, new Set());
99
99
  }
100
-
100
+
101
101
  this.eventHandlers.get(eventName).add(handler);
102
-
102
+
103
103
  return () => this.off(eventName, handler);
104
104
  }
105
-
105
+
106
106
  /**
107
107
  * Register a one-time event handler that will be removed after first execution
108
- *
108
+ *
109
109
  * @param {string} eventName - Event name to listen for
110
110
  * @param {Function} handler - Handler function to call when event is emitted
111
111
  * @returns {Function} Function to remove this specific handler
112
- *
112
+ *
113
113
  * @example
114
114
  * emitter.once('initialization', () => {
115
115
  * console.log('Initialization happened');
@@ -119,22 +119,22 @@ export default class EventEmitter {
119
119
  if (typeof handler !== 'function') {
120
120
  throw new TypeError('Event handler must be a function');
121
121
  }
122
-
122
+
123
123
  const wrapper = (...args) => {
124
124
  this.off(eventName, wrapper);
125
125
  handler(...args);
126
126
  };
127
-
127
+
128
128
  return this.on(eventName, wrapper);
129
129
  }
130
-
130
+
131
131
  /**
132
132
  * Remove an event handler
133
- *
133
+ *
134
134
  * @param {string} eventName - Event name the handler was registered for
135
135
  * @param {Function} handler - Handler function to remove
136
136
  * @returns {boolean} True if the handler was removed, false otherwise
137
- *
137
+ *
138
138
  * @example
139
139
  * const handler = (data) => console.log(data);
140
140
  * emitter.on('data', handler);
@@ -145,34 +145,34 @@ export default class EventEmitter {
145
145
  if (eventName.endsWith('*')) {
146
146
  const prefix = eventName.slice(0, -1);
147
147
  const handlers = this.wildcardHandlers.get(prefix);
148
-
148
+
149
149
  if (handlers) {
150
150
  return handlers.delete(handler);
151
151
  }
152
-
152
+
153
153
  return false;
154
154
  }
155
-
155
+
156
156
  // Handle normal listeners
157
157
  const handlers = this.eventHandlers.get(eventName);
158
-
158
+
159
159
  if (handlers) {
160
160
  return handlers.delete(handler);
161
161
  }
162
-
162
+
163
163
  return false;
164
164
  }
165
-
165
+
166
166
  /**
167
167
  * Remove all event handlers for a specific event
168
- *
168
+ *
169
169
  * @param {string} [eventName] - Event name to remove handlers for.
170
170
  * If not provided, removes all handlers for all events.
171
- *
171
+ *
172
172
  * @example
173
173
  * // Remove all 'data' event handlers
174
174
  * emitter.removeAllListeners('data');
175
- *
175
+ *
176
176
  * // Remove all event handlers
177
177
  * emitter.removeAllListeners();
178
178
  */
@@ -191,85 +191,83 @@ export default class EventEmitter {
191
191
  this.wildcardHandlers.clear();
192
192
  }
193
193
  }
194
-
194
+
195
195
  /**
196
196
  * Emit an event
197
- *
197
+ *
198
198
  * @param {string} eventName - Name of the event to emit
199
199
  * @param {*} data - Data to pass to event handlers
200
200
  * @returns {boolean} True if there were handlers for this event, false otherwise
201
- *
201
+ *
202
202
  * @example
203
203
  * emitter.emit('dataLoaded', { users: [...] });
204
204
  */
205
205
  emit(eventName, data) {
206
206
  let handled = false;
207
-
207
+
208
208
  // Call specific event handlers
209
209
  const handlers = this.eventHandlers.get(eventName);
210
210
  if (handlers && handlers.size > 0) {
211
- handlers.forEach(handler => handler(data));
211
+ handlers.forEach((handler) => handler(data));
212
212
  handled = true;
213
213
  }
214
-
214
+
215
215
  // Call matching wildcard handlers
216
216
  this.wildcardHandlers.forEach((handlers, prefix) => {
217
217
  if (eventName.startsWith(prefix)) {
218
- handlers.forEach(handler =>
219
- handler({ event: eventName, data })
220
- );
218
+ handlers.forEach((handler) => handler({ event: eventName, data }));
221
219
  handled = true;
222
220
  }
223
221
  });
224
-
222
+
225
223
  return handled;
226
224
  }
227
-
225
+
228
226
  /**
229
227
  * Get the number of listeners for a specific event
230
- *
228
+ *
231
229
  * @param {string} eventName - Event name to count listeners for
232
230
  * @returns {number} Number of listeners for this event
233
- *
231
+ *
234
232
  * @example
235
233
  * const count = emitter.listenerCount('data');
236
234
  * console.log(`There are ${count} data event listeners`);
237
235
  */
238
236
  listenerCount(eventName) {
239
237
  let count = 0;
240
-
238
+
241
239
  // Count specific event handlers
242
240
  const handlers = this.eventHandlers.get(eventName);
243
241
  if (handlers) {
244
242
  count += handlers.size;
245
243
  }
246
-
244
+
247
245
  // Count matching wildcard handlers
248
246
  this.wildcardHandlers.forEach((handlers, prefix) => {
249
247
  if (eventName.startsWith(prefix)) {
250
248
  count += handlers.size;
251
249
  }
252
250
  });
253
-
251
+
254
252
  return count;
255
253
  }
256
-
254
+
257
255
  /**
258
256
  * Get all registered event names
259
- *
257
+ *
260
258
  * @returns {string[]} Array of event names that have listeners
261
- *
259
+ *
262
260
  * @example
263
261
  * console.log('Events with listeners:', emitter.eventNames());
264
262
  */
265
263
  eventNames() {
266
264
  const events = [...this.eventHandlers.keys()];
267
-
265
+
268
266
  // Add wildcard events
269
267
  this.wildcardHandlers.forEach((_, prefix) => {
270
268
  events.push(`${prefix}*`);
271
269
  });
272
-
270
+
273
271
  return events;
274
272
  }
275
273
  }
@@ -22,4 +22,5 @@ export class PinoAdapter {
22
22
  * @returns {PinoAdapter} New adapter instance with context
23
23
  */
24
24
  child(context: any): PinoAdapter;
25
+ #private;
25
26
  }
@@ -9,12 +9,16 @@ import { dev } from '$app/environment';
9
9
  * Pino adapter that bridges Logger events to pino
10
10
  */
11
11
  export class PinoAdapter {
12
+ #projectRoot = null;
13
+
12
14
  /**
13
15
  * Create a new PinoAdapter
14
16
  *
15
17
  * @param {Object} [options] - Pino configuration options
16
18
  */
17
19
  constructor(options = {}) {
20
+ // Determine project root once for stack trace cleaning
21
+ this.#projectRoot = import.meta.env.VITE_PROJECT_ROOT || process.cwd();
18
22
  const baseOptions = {
19
23
  serializers: {
20
24
  err: (err) => {
@@ -23,14 +27,24 @@ export class PinoAdapter {
23
27
  let isFirst = true;
24
28
 
25
29
  while (current) {
30
+ /** @type {import('./typedef').ErrorSummary} */
26
31
  const serialized = {
27
32
  name: current.name,
28
33
  message: current.message,
29
34
  ...(isFirst &&
30
35
  this.pino.level === 'debug' && {
31
- stack: current.stack
36
+ stack: this.#cleanStackTrace(current.stack)
32
37
  })
33
38
  };
39
+
40
+ // Include HttpError-specific properties
41
+ if (current.status !== undefined) {
42
+ serialized.status = current.status;
43
+ }
44
+ if (current.details !== undefined) {
45
+ serialized.details = current.details;
46
+ }
47
+
34
48
  chain.push(serialized);
35
49
  current = current.cause;
36
50
  isFirst = false;
@@ -56,6 +70,28 @@ export class PinoAdapter {
56
70
  this.pino = pino({ ...baseOptions, ...devOptions, ...options });
57
71
  }
58
72
 
73
+ /**
74
+ * Clean stack trace by removing project root path
75
+ *
76
+ * @param {string} stack - Original stack trace
77
+ * @returns {string} Cleaned stack trace
78
+ */
79
+ #cleanStackTrace(stack) {
80
+ if (!stack || !this.#projectRoot) {
81
+ return stack;
82
+ }
83
+
84
+ // Escape special regex characters in the project root path
85
+ const escapedRoot = this.#projectRoot.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
86
+
87
+ // Replace project root path with relative path, but only after "at " to avoid
88
+ // accidental replacements in error messages or other parts of the stack
89
+ // Match the project root followed by a path separator
90
+ const regex = new RegExp(`(\\s+at\\s+.*\\()${escapedRoot}[\\/\\\\]`, 'g');
91
+
92
+ return stack.replace(regex, '$1');
93
+ }
94
+
59
95
  /**
60
96
  * Handle log events from Logger
61
97
  *
@@ -0,0 +1,24 @@
1
+ declare const _default: {};
2
+ export default _default;
3
+ export type ErrorSummary = {
4
+ /**
5
+ * - The name of the exception/error
6
+ */
7
+ name: string;
8
+ /**
9
+ * - The error message
10
+ */
11
+ message: string;
12
+ /**
13
+ * Stack trace (in debug mode for first exception)
14
+ */
15
+ stack?: string;
16
+ /**
17
+ * - HTTP status code (for HttpError instances)
18
+ */
19
+ status?: number;
20
+ /**
21
+ * - Additional error details (for HttpError instances)
22
+ */
23
+ details?: any;
24
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @typedef {Object} ErrorSummary
3
+ * @property {string} name - The name of the exception/error
4
+ * @property {string} message - The error message
5
+ * @property {string} [stack] Stack trace (in debug mode for first exception)
6
+ * @property {number} [status] - HTTP status code (for HttpError instances)
7
+ * @property {*} [details] - Additional error details (for HttpError instances)
8
+ */
9
+
10
+ export default {};
@@ -95,15 +95,6 @@ export class ServiceManager extends EventEmitter {
95
95
  stopAll(options?: StopOptions): Promise<{
96
96
  [x: string]: boolean;
97
97
  }>;
98
- /**
99
- * Stop services sequentially
100
- *
101
- * @private
102
- * @param {string[]} serviceNames - Ordered list of service names
103
- * @param {Map<string, boolean>} results - Results map to populate
104
- * @param {StopOptions} options - Stop options
105
- */
106
- private _stopAllSequentially;
107
98
  /**
108
99
  * Get health status for all services
109
100
  *
@@ -133,39 +124,15 @@ export class ServiceManager extends EventEmitter {
133
124
  * @returns {string[]} Array of service names
134
125
  */
135
126
  getServicesByTag(tag: string): string[];
136
- /**
137
- * Setup logging configuration based on config.dev
138
- *
139
- * @private
140
- */
141
- private _setupLogging;
142
- /**
143
- * Get the appropriate log level for a service
144
- *
145
- * @private
146
- * @param {string} name - Service name
147
- *
148
- * @returns {string|undefined} Log level or undefined
149
- */
150
- private _getServiceLogLevel;
151
127
  /**
152
128
  * Attach event listeners to forward service events
153
129
  *
154
- * @private
155
130
  * @param {string} name - Service name
156
131
  * @param {import('../service-base/typedef.js').ServiceInstance} instance
157
132
  * Service instance
158
133
  */
159
- private _attachServiceEvents;
160
- /**
161
- * Sort services by dependencies using topological sort
162
- *
163
- * @private
164
- *
165
- * @returns {string[]} Service names in dependency order
166
- * @throws {Error} If circular dependencies are detected
167
- */
168
- private _topologicalSort;
134
+ _attachServiceEvents(name: string, instance: import("../service-base/typedef.js").ServiceInstance): void;
135
+ #private;
169
136
  }
170
137
  export default ServiceManager;
171
138
  export type ServiceConstructor = import("./typedef.js").ServiceConstructor;