@hkdigital/lib-sveltekit 0.1.70 → 0.1.72

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 (31) hide show
  1. package/dist/classes/cache/IndexedDbCache.d.ts +212 -0
  2. package/dist/classes/cache/IndexedDbCache.js +673 -0
  3. package/dist/classes/cache/MemoryResponseCache.d.ts +101 -14
  4. package/dist/classes/cache/MemoryResponseCache.js +97 -12
  5. package/dist/classes/cache/index.d.ts +1 -1
  6. package/dist/classes/cache/index.js +2 -1
  7. package/dist/classes/events/EventEmitter.d.ts +142 -0
  8. package/dist/classes/events/EventEmitter.js +275 -0
  9. package/dist/classes/events/index.d.ts +1 -0
  10. package/dist/classes/events/index.js +2 -0
  11. package/dist/classes/logging/Logger.d.ts +74 -0
  12. package/dist/classes/logging/Logger.js +158 -0
  13. package/dist/classes/logging/constants.d.ts +14 -0
  14. package/dist/classes/logging/constants.js +18 -0
  15. package/dist/classes/logging/index.d.ts +2 -0
  16. package/dist/classes/logging/index.js +4 -0
  17. package/dist/classes/services/ServiceBase.d.ts +153 -0
  18. package/dist/classes/services/ServiceBase.js +409 -0
  19. package/dist/classes/services/ServiceManager.d.ts +350 -0
  20. package/dist/classes/services/ServiceManager.js +1114 -0
  21. package/dist/classes/services/constants.d.ts +11 -0
  22. package/dist/classes/services/constants.js +12 -0
  23. package/dist/classes/services/index.d.ts +3 -0
  24. package/dist/classes/services/index.js +5 -0
  25. package/dist/util/env/index.d.ts +1 -0
  26. package/dist/util/env/index.js +9 -0
  27. package/dist/util/http/caching.js +24 -12
  28. package/dist/util/http/http-request.js +12 -7
  29. package/package.json +2 -1
  30. package/dist/classes/cache/PersistentResponseCache.d.ts +0 -46
  31. /package/dist/classes/cache/{PersistentResponseCache.js → PersistentResponseCache.js__} +0 -0
@@ -0,0 +1,409 @@
1
+ /**
2
+ * @fileoverview Base service class with lifecycle management and logging.
3
+ *
4
+ * ServiceBase provides a standardized lifecycle (initialize, start, stop,
5
+ * destroy) with state transitions, error handling, and integrated logging.
6
+ * Services should extend this class and override the protected _init, _start,
7
+ * _stop, and _destroy methods to implement their specific functionality.
8
+ *
9
+ * @example
10
+ * // Creating a service
11
+ * import { ServiceBase } from './ServiceBase.js';
12
+ *
13
+ * class DatabaseService extends ServiceBase {
14
+ * constructor() {
15
+ * super('database');
16
+ * this.connection = null;
17
+ * }
18
+ *
19
+ * async _init(config) {
20
+ * this.config = config;
21
+ * this.logger.debug('Database configured', { config });
22
+ * }
23
+ *
24
+ * async _start() {
25
+ * this.connection = await createConnection(this.config);
26
+ * this.logger.info('Database connected', { id: this.connection.id });
27
+ * }
28
+ *
29
+ * async _stop() {
30
+ * await this.connection.close();
31
+ * this.connection = null;
32
+ * this.logger.info('Database disconnected');
33
+ * }
34
+ * }
35
+ *
36
+ * // Using a service
37
+ * const db = new DatabaseService();
38
+ *
39
+ * await db.initialize({ host: 'localhost', port: 27017 });
40
+ * await db.start();
41
+ *
42
+ * // Listen for state changes
43
+ * db.on('stateChanged', ({ oldState, newState }) => {
44
+ * console.log(`Database service: ${oldState} -> ${newState}`);
45
+ * });
46
+ *
47
+ * // Later...
48
+ * await db.stop();
49
+ * await db.destroy();
50
+ */
51
+
52
+ import { EventEmitter } from '../events';
53
+ import { Logger, INFO } from '../logging';
54
+
55
+ import {
56
+ CREATED,
57
+ INITIALIZING,
58
+ INITIALIZED,
59
+ STARTING,
60
+ RUNNING,
61
+ STOPPING,
62
+ STOPPED,
63
+ DESTROYING,
64
+ DESTROYED,
65
+ ERROR,
66
+ RECOVERING
67
+ } from './constants';
68
+
69
+ /**
70
+ * Base class for all services
71
+ */
72
+ export default class ServiceBase {
73
+ /**
74
+ * Create a new service
75
+ *
76
+ * @param {string} name - Service name
77
+ * @param {Object} [options] - Service options
78
+ * @param {string} [options.logLevel=INFO] - Initial log level
79
+ */
80
+ constructor(name, options = {}) {
81
+ /**
82
+ * Service name
83
+ * @type {string}
84
+ */
85
+ this.name = name;
86
+
87
+ /**
88
+ * Event emitter for service events
89
+ * @type {EventEmitter}
90
+ */
91
+ this.events = new EventEmitter();
92
+
93
+ /**
94
+ * Current service state
95
+ * @type {string}
96
+ */
97
+ this.state = CREATED;
98
+
99
+ /**
100
+ * Last error that occurred
101
+ * @type {Error|null}
102
+ */
103
+ this.error = null;
104
+
105
+ /**
106
+ * Last stable state before error
107
+ * @type {string|null}
108
+ * @private
109
+ */
110
+ this._preErrorState = null;
111
+
112
+ /**
113
+ * Service logger
114
+ * @type {Logger}
115
+ */
116
+ this.logger = new Logger(name, options.logLevel || INFO);
117
+
118
+ // Set the initial state through _setState to ensure
119
+ // the event is emitted consistently
120
+ this._setState(CREATED);
121
+ }
122
+
123
+ /**
124
+ * Set the service log level
125
+ *
126
+ * @param {string} level - New log level
127
+ * @returns {boolean} True if level was set, false if invalid
128
+ */
129
+ setLogLevel(level) {
130
+ return this.logger.setLevel(level);
131
+ }
132
+
133
+ /**
134
+ * Initialize the service
135
+ *
136
+ * @param {Object} [config] - Service configuration
137
+ * @returns {Promise<boolean>} True if initialized successfully
138
+ */
139
+ async initialize(config = {}) {
140
+ try {
141
+ this._setState(INITIALIZING);
142
+ this.logger.debug('Initializing service', { config });
143
+
144
+ await this._init(config);
145
+
146
+ this._setState(INITIALIZED);
147
+ this.logger.info('Service initialized');
148
+ return true;
149
+ } catch (error) {
150
+ this._setError('initialization', error);
151
+ return false;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Start the service
157
+ *
158
+ * @returns {Promise<boolean>} True if started successfully
159
+ */
160
+ async start() {
161
+ // Check if service can be started
162
+ if (this.state !== INITIALIZED && this.state !== STOPPED) {
163
+ this._setError(
164
+ 'startup',
165
+ new Error(`Cannot start service in state: ${this.state}`)
166
+ );
167
+ return false;
168
+ }
169
+
170
+ try {
171
+ this._setState(STARTING);
172
+ this.logger.debug('Starting service');
173
+
174
+ await this._start();
175
+
176
+ this._setState(RUNNING);
177
+ this.logger.info('Service started');
178
+ return true;
179
+ } catch (error) {
180
+ this._setError('startup', error);
181
+ return false;
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Stop the service
187
+ *
188
+ * @returns {Promise<boolean>} True if stopped successfully
189
+ */
190
+ async stop() {
191
+ // Check if service can be stopped
192
+ if (this.state !== RUNNING) {
193
+ this._setError(
194
+ 'stopping',
195
+ new Error(`Cannot stop service in state: ${this.state}`)
196
+ );
197
+ return false;
198
+ }
199
+
200
+ try {
201
+ this._setState(STOPPING);
202
+ this.logger.debug('Stopping service');
203
+
204
+ await this._stop();
205
+
206
+ this._setState(STOPPED);
207
+ this.logger.info('Service stopped');
208
+ return true;
209
+ } catch (error) {
210
+ this._setError('stopping', error);
211
+ return false;
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Recover the service
217
+ *
218
+ * @returns {Promise<boolean>} True if stopped successfully
219
+ */
220
+ async recover() {
221
+ if (this.state !== ERROR) {
222
+ this.logger.warn(`Can only recover from ERROR state, current state: ${this.state}`);
223
+ return false;
224
+ }
225
+
226
+ try {
227
+ this._setState(RECOVERING);
228
+ this.logger.info('Attempting service recovery');
229
+
230
+ const targetState = this._preErrorState;
231
+
232
+ // Allow service-specific recovery logic
233
+ await this._recover();
234
+
235
+ // this._setState(targetState);
236
+ if( this.state !== ERROR )
237
+ {
238
+ // Clear
239
+ this._preErrorState = null;
240
+ }
241
+
242
+ // Clear error
243
+ this.error = null;
244
+
245
+
246
+ // If recovery successful, return to initialized state
247
+ this._setState(INITIALIZED);
248
+ this.logger.info('Service recovery successful');
249
+
250
+
251
+ return true;
252
+ } catch (error) {
253
+ this._setError('recovery', error);
254
+ return false;
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Destroy the service
260
+ *
261
+ * @returns {Promise<boolean>} True if destroyed successfully
262
+ */
263
+ async destroy() {
264
+ try {
265
+ this._setState(DESTROYING);
266
+ this.logger.debug('Destroying service');
267
+
268
+ await this._destroy();
269
+
270
+ this._setState(DESTROYED);
271
+ this.logger.info('Service destroyed');
272
+
273
+ // Clean up event listeners
274
+ this.events.removeAllListeners();
275
+ this.logger.removeAllListeners();
276
+
277
+ return true;
278
+ } catch (error) {
279
+ this._setError('destruction', error);
280
+ return false;
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Add an event listener
286
+ *
287
+ * @param {string} eventName - Event name
288
+ * @param {Function} handler - Event handler
289
+ * @returns {Function} Unsubscribe function
290
+ */
291
+ on(eventName, handler) {
292
+ return this.events.on(eventName, handler);
293
+ }
294
+
295
+ /**
296
+ * Emit an event
297
+ *
298
+ * @param {string} eventName - Event name
299
+ * @param {*} data - Event data
300
+ * @returns {boolean} True if event had listeners
301
+ */
302
+ emit(eventName, data) {
303
+ return this.events.emit(eventName, data);
304
+ }
305
+
306
+ // Protected methods to be overridden by subclasses
307
+
308
+ /**
309
+ * Initialize the service (to be overridden)
310
+ *
311
+ * @protected
312
+ * @param {Object} config - Service configuration
313
+ * @returns {Promise<void>}
314
+ */
315
+ async _init(config) {
316
+ // Default implementation does nothing
317
+ }
318
+
319
+ /**
320
+ * Start the service (to be overridden)
321
+ *
322
+ * @protected
323
+ * @returns {Promise<void>}
324
+ */
325
+ async _start() {
326
+ // Default implementation does nothing
327
+ }
328
+
329
+ /**
330
+ * Stop the service (to be overridden)
331
+ *
332
+ * @protected
333
+ * @returns {Promise<void>}
334
+ */
335
+ async _stop() {
336
+ // Default implementation does nothing
337
+ }
338
+
339
+ /**
340
+ * Destroy the service (to be overridden)
341
+ *
342
+ * @protected
343
+ * @returns {Promise<void>}
344
+ */
345
+ async _destroy() {
346
+ // Default implementation does nothing
347
+ }
348
+
349
+ /**
350
+ * Recover the service from an error (to be overridden)
351
+ *
352
+ * @protected
353
+ * @returns {Promise<void>}
354
+ */
355
+ async _recover() {
356
+ // @note the user implementation is responsible for setting the target state
357
+ this._setState( this._preErrorState );
358
+ }
359
+
360
+ // Private helper methods
361
+
362
+ /**
363
+ * Set the service state
364
+ *
365
+ * @private
366
+ * @param {string} state - New state
367
+ */
368
+ _setState(state) {
369
+ const oldState = this.state;
370
+ this.state = state;
371
+
372
+ this.logger.debug(`State changed from ${oldState} to ${state}`);
373
+
374
+ this.events.emit('stateChanged', {
375
+ service: this.name,
376
+ oldState,
377
+ newState: state
378
+ });
379
+ }
380
+
381
+ /**
382
+ * Set an error state
383
+ *
384
+ * @private
385
+ * @param {string} operation - Operation that failed
386
+ * @param {Error} error - Error that occurred
387
+ */
388
+ _setError(operation, error) {
389
+
390
+ if (this.state !== ERROR) {
391
+ // Store current state before transitioning to ERROR
392
+ this._preErrorState = this.state;
393
+ }
394
+
395
+ this.error = error;
396
+ this._setState(ERROR);
397
+
398
+ this.logger.error(`${operation} error`, {
399
+ error: error.message,
400
+ stack: error.stack
401
+ });
402
+
403
+ this.events.emit('error', {
404
+ service: this.name,
405
+ operation,
406
+ error
407
+ });
408
+ }
409
+ }