@hkdigital/lib-sveltekit 0.2.8 → 0.2.9

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 (243) hide show
  1. package/README.md +135 -135
  2. package/dist/assets/autospuiten/car-paint-picker.js +41 -41
  3. package/dist/assets/autospuiten/labels.js +7 -7
  4. package/dist/classes/cache/IndexedDbCache.js +1407 -1407
  5. package/dist/classes/cache/MemoryResponseCache.js +138 -138
  6. package/dist/classes/cache/index.js +5 -5
  7. package/dist/classes/cache/typedef.js +41 -41
  8. package/dist/classes/data/IterableTree.js +243 -243
  9. package/dist/classes/data/Selector.js +190 -190
  10. package/dist/classes/data/index.js +2 -2
  11. package/dist/classes/events/EventEmitter.js +275 -275
  12. package/dist/classes/events/index.js +2 -2
  13. package/dist/classes/index.js +4 -4
  14. package/dist/classes/logging/Logger.js +158 -158
  15. package/dist/classes/logging/constants.js +18 -18
  16. package/dist/classes/logging/index.js +4 -4
  17. package/dist/classes/promise/HkPromise.js +377 -377
  18. package/dist/classes/promise/index.js +1 -1
  19. package/dist/classes/services/ServiceBase.js +409 -409
  20. package/dist/classes/services/ServiceManager.js +1114 -1114
  21. package/dist/classes/services/constants.js +12 -12
  22. package/dist/classes/services/index.js +5 -5
  23. package/dist/classes/stores/SubscribersCount.js +107 -107
  24. package/dist/classes/stores/index.js +1 -1
  25. package/dist/classes/streams/LogTransformStream.js +19 -19
  26. package/dist/classes/streams/ServerEventsStore.js +110 -110
  27. package/dist/classes/streams/TimeStampSource.js +26 -26
  28. package/dist/classes/streams/index.js +3 -3
  29. package/dist/classes/svelte/audio/AudioLoader.svelte.js +58 -58
  30. package/dist/classes/svelte/audio/AudioScene.svelte.js +324 -324
  31. package/dist/classes/svelte/audio/mocks.js +35 -35
  32. package/dist/classes/svelte/finite-state-machine/FiniteStateMachine.svelte.js +133 -133
  33. package/dist/classes/svelte/finite-state-machine/index.js +1 -1
  34. package/dist/classes/svelte/image/ImageLoader.svelte.js +45 -45
  35. package/dist/classes/svelte/image/ImageScene.svelte.js +249 -249
  36. package/dist/classes/svelte/image/ImageVariantsLoader.svelte.js +152 -152
  37. package/dist/classes/svelte/image/index.js +4 -4
  38. package/dist/classes/svelte/image/mocks.js +35 -35
  39. package/dist/classes/svelte/image/typedef.js +8 -8
  40. package/dist/classes/svelte/index.js +14 -14
  41. package/dist/classes/svelte/loading-state-machine/LoadingStateMachine.svelte.js +109 -109
  42. package/dist/classes/svelte/loading-state-machine/constants.js +16 -16
  43. package/dist/classes/svelte/loading-state-machine/index.js +3 -3
  44. package/dist/classes/svelte/network-loader/NetworkLoader.svelte.js +338 -338
  45. package/dist/classes/svelte/network-loader/constants.js +3 -3
  46. package/dist/classes/svelte/network-loader/index.js +3 -3
  47. package/dist/classes/svelte/network-loader/mocks.js +30 -30
  48. package/dist/classes/svelte/network-loader/typedef.js +8 -8
  49. package/dist/components/area/HkArea.svelte +49 -49
  50. package/dist/components/area/HkGridArea.svelte +77 -77
  51. package/dist/components/area/index.js +2 -2
  52. package/dist/components/buttons/button/Button.svelte +82 -82
  53. package/dist/components/buttons/button-icon-steeze/SteezeIconButton.svelte +30 -30
  54. package/dist/components/buttons/button-text/TextButton.svelte +21 -21
  55. package/dist/components/buttons/index.js +3 -3
  56. package/dist/components/debug/debug-panel-design-scaling/DebugPanelDesignScaling.svelte +146 -146
  57. package/dist/components/debug/index.js +1 -1
  58. package/dist/components/drag-drop/DragController.js +44 -44
  59. package/dist/components/drag-drop/DragDropContext.svelte +112 -110
  60. package/dist/components/drag-drop/Draggable.svelte +498 -512
  61. package/dist/components/drag-drop/{Dropzone.svelte → DropZone.svelte} +258 -258
  62. package/dist/components/drag-drop/DropZoneArea.svelte +119 -119
  63. package/dist/components/drag-drop/DropZoneList.svelte +125 -125
  64. package/dist/components/drag-drop/actions.d.ts +6 -0
  65. package/dist/components/drag-drop/actions.js +18 -0
  66. package/dist/components/drag-drop/drag-state.svelte.js +319 -319
  67. package/dist/components/drag-drop/index.js +7 -7
  68. package/dist/components/drag-drop/util.js +85 -85
  69. package/dist/components/hkdev/blocks/TextBlock.svelte +46 -46
  70. package/dist/components/hkdev/buttons/CheckButton.svelte +62 -62
  71. package/dist/components/icons/HkIcon.svelte +86 -86
  72. package/dist/components/icons/HkTabIcon.svelte +116 -116
  73. package/dist/components/icons/SteezeIcon.svelte +97 -97
  74. package/dist/components/icons/index.js +6 -6
  75. package/dist/components/icons/typedef.js +16 -16
  76. package/dist/components/index.js +2 -2
  77. package/dist/components/inputs/index.js +1 -1
  78. package/dist/components/inputs/text-input/TestTextInput.svelte__ +102 -102
  79. package/dist/components/inputs/text-input/TextInput.svelte +223 -223
  80. package/dist/components/inputs/text-input/TextInput.svelte___ +83 -83
  81. package/dist/components/inputs/text-input/assets/IconInvalid.svelte +14 -14
  82. package/dist/components/inputs/text-input/assets/IconValid.svelte +12 -12
  83. package/dist/components/layout/grid-layers/GridLayers.svelte +63 -63
  84. package/dist/components/layout/grid-layers/GridLayers.svelte__heightFrom__ +372 -0
  85. package/dist/components/layout/grid-layers/util.js +74 -74
  86. package/dist/components/layout/index.js +1 -1
  87. package/dist/components/panels/index.js +1 -1
  88. package/dist/components/panels/panel/Panel.svelte +43 -43
  89. package/dist/components/rows/index.js +3 -3
  90. package/dist/components/rows/panel-grid-row/PanelGridRow.svelte +104 -104
  91. package/dist/components/rows/panel-row-2/PanelRow2.svelte +40 -40
  92. package/dist/components/tab-bar/HkTabBar.state.svelte.js +149 -149
  93. package/dist/components/tab-bar/HkTabBar.svelte +74 -74
  94. package/dist/components/tab-bar/HkTabBarSelector.state.svelte.js +93 -93
  95. package/dist/components/tab-bar/HkTabBarSelector.svelte +49 -49
  96. package/dist/components/tab-bar/index.js +17 -17
  97. package/dist/components/tab-bar/typedef.js +11 -11
  98. package/dist/config/imagetools-config.js +189 -189
  99. package/dist/config/imagetools.d.ts +72 -72
  100. package/dist/constants/bases.js +13 -13
  101. package/dist/constants/errors/api.js +9 -9
  102. package/dist/constants/errors/generic.js +5 -5
  103. package/dist/constants/errors/index.js +3 -3
  104. package/dist/constants/errors/jwt.js +5 -5
  105. package/dist/constants/http/headers.js +6 -6
  106. package/dist/constants/http/index.js +2 -2
  107. package/dist/constants/http/methods.js +2 -2
  108. package/dist/constants/index.js +3 -3
  109. package/dist/constants/mime/application.js +5 -5
  110. package/dist/constants/mime/audio.js +13 -13
  111. package/dist/constants/mime/image.js +3 -3
  112. package/dist/constants/mime/index.js +4 -4
  113. package/dist/constants/mime/text.js +2 -2
  114. package/dist/constants/regexp/index.js +31 -31
  115. package/dist/constants/regexp/inspiratie.js__ +95 -95
  116. package/dist/constants/regexp/text.js +49 -49
  117. package/dist/constants/regexp/user.js +32 -32
  118. package/dist/constants/regexp/web.js +3 -3
  119. package/dist/constants/state-labels/drag-states.js +6 -6
  120. package/dist/constants/state-labels/drop-states.js +6 -6
  121. package/dist/constants/state-labels/input-states.js +11 -11
  122. package/dist/constants/state-labels/submit-states.js +4 -4
  123. package/dist/constants/time.js +28 -28
  124. package/dist/css/utilities.css +43 -43
  125. package/dist/design/design-config.js +73 -73
  126. package/dist/design/tailwind-theme-extend.js +158 -158
  127. package/dist/features/button-group/ButtonGroup.svelte +82 -82
  128. package/dist/features/button-group/typedef.js +10 -10
  129. package/dist/features/compare-left-right/CompareLeftRight.svelte +179 -179
  130. package/dist/features/compare-left-right/index.js +1 -1
  131. package/dist/features/game-box/GameBox.svelte +577 -577
  132. package/dist/features/game-box/gamebox.util.js +83 -83
  133. package/dist/features/hk-app-layout/HkAppLayout.state.svelte.js +25 -25
  134. package/dist/features/hk-app-layout/HkAppLayout.svelte +251 -251
  135. package/dist/features/image-box/ImageBox.svelte +210 -210
  136. package/dist/features/image-box/index.js +5 -5
  137. package/dist/features/image-box/typedef.js +32 -32
  138. package/dist/features/index.js +23 -23
  139. package/dist/features/presenter/ImageSlide.svelte +64 -64
  140. package/dist/features/presenter/Presenter.state.svelte.js +638 -638
  141. package/dist/features/presenter/Presenter.svelte +142 -142
  142. package/dist/features/presenter/constants.js +7 -7
  143. package/dist/features/presenter/index.js +10 -10
  144. package/dist/features/presenter/typedef.js +106 -106
  145. package/dist/features/presenter/util.js +210 -210
  146. package/dist/features/virtual-viewport/VirtualViewport.svelte +196 -196
  147. package/dist/schemas/index.js +1 -1
  148. package/dist/schemas/validate-url.js +180 -180
  149. package/dist/server/index.js +1 -1
  150. package/dist/server/logger.js +94 -94
  151. package/dist/states/index.js +1 -1
  152. package/dist/states/navigation.svelte.js +55 -55
  153. package/dist/stores/index.js +1 -1
  154. package/dist/stores/theme.js +80 -80
  155. package/dist/themes/hkdev/components/blocks/text-block.css +41 -41
  156. package/dist/themes/hkdev/components/boxes/game-box.css +12 -12
  157. package/dist/themes/hkdev/components/buttons/button-icon-steeze.css +22 -22
  158. package/dist/themes/hkdev/components/buttons/button-text.css +32 -32
  159. package/dist/themes/hkdev/components/buttons/button.css +146 -146
  160. package/dist/themes/hkdev/components/buttons/skip-button.css +6 -6
  161. package/dist/themes/hkdev/components/drag-drop/draggable.css +73 -73
  162. package/dist/themes/hkdev/components/drag-drop/drop-zone.css +48 -48
  163. package/dist/themes/hkdev/components/icons/icon-steeze.css +22 -22
  164. package/dist/themes/hkdev/components/inputs/text-input.css +104 -104
  165. package/dist/themes/hkdev/components/panels/panel.css +27 -27
  166. package/dist/themes/hkdev/components/rows/panel-grid-row.css +6 -6
  167. package/dist/themes/hkdev/components/rows/panel-row-2.css +7 -7
  168. package/dist/themes/hkdev/components.css +53 -53
  169. package/dist/themes/hkdev/debug.css +1 -1
  170. package/dist/themes/hkdev/global/layout.css +39 -39
  171. package/dist/themes/hkdev/global/on-colors.css +53 -53
  172. package/dist/themes/hkdev/globals.css +11 -11
  173. package/dist/themes/hkdev/responsive.css +12 -12
  174. package/dist/themes/hkdev/theme-ext.js +15 -15
  175. package/dist/themes/hkdev/theme.js +235 -235
  176. package/dist/themes/index.js +1 -1
  177. package/dist/typedef/context.js +6 -6
  178. package/dist/typedef/drag.js +25 -25
  179. package/dist/typedef/drop.js +12 -12
  180. package/dist/typedef/image.js +38 -38
  181. package/dist/typedef/index.js +4 -4
  182. package/dist/util/array/index.js +436 -436
  183. package/dist/util/bases/base58.js +262 -262
  184. package/dist/util/bases/index.js +1 -1
  185. package/dist/util/compare/index.js +247 -247
  186. package/dist/util/css/css-vars.js +83 -83
  187. package/dist/util/css/index.js +1 -1
  188. package/dist/util/design-system/components/states.js +22 -22
  189. package/dist/util/design-system/css/clamp.js +66 -66
  190. package/dist/util/design-system/css/root-design-vars.js +102 -102
  191. package/dist/util/design-system/index.js +5 -5
  192. package/dist/util/design-system/layout/scaling.js +228 -228
  193. package/dist/util/design-system/skeleton.js +208 -208
  194. package/dist/util/design-system/tailwind.js +288 -288
  195. package/dist/util/env/index.js +9 -9
  196. package/dist/util/expect/arrays.js +47 -47
  197. package/dist/util/expect/index.js +259 -259
  198. package/dist/util/expect/primitives.js +55 -55
  199. package/dist/util/expect/url.js +60 -60
  200. package/dist/util/function/index.js +218 -218
  201. package/dist/util/geo/index.js +26 -26
  202. package/dist/util/http/caching.js +263 -263
  203. package/dist/util/http/errors.js +97 -97
  204. package/dist/util/http/headers.js +75 -75
  205. package/dist/util/http/http-request.js +379 -379
  206. package/dist/util/http/index.js +22 -22
  207. package/dist/util/http/json-request.js +224 -224
  208. package/dist/util/http/mocks.js +65 -65
  209. package/dist/util/http/response.js +294 -294
  210. package/dist/util/http/test-data__/content-length-test-hkdigital-small.V4HfZyBQ.avif +0 -0
  211. package/dist/util/http/typedef.js +93 -93
  212. package/dist/util/http/url.js +52 -52
  213. package/dist/util/image/index.js +86 -86
  214. package/dist/util/index.js +2 -2
  215. package/dist/util/is/index.js +140 -140
  216. package/dist/util/iterate/index.js +234 -234
  217. package/dist/util/object/index.js +1361 -1361
  218. package/dist/util/singleton/index.js +97 -97
  219. package/dist/util/string/array-path.js +75 -75
  220. package/dist/util/string/convert.js +54 -54
  221. package/dist/util/string/fs.js +226 -226
  222. package/dist/util/string/index.js +5 -5
  223. package/dist/util/string/interpolate.js +61 -61
  224. package/dist/util/string/pad.js +10 -10
  225. package/dist/util/svelte/index.js +4 -4
  226. package/dist/util/svelte/loading/loading-tracker.svelte.js +108 -108
  227. package/dist/util/svelte/observe/index.js +49 -49
  228. package/dist/util/svelte/state-context/index.js +117 -117
  229. package/dist/util/svelte/wait/index.js +38 -38
  230. package/dist/util/sveltekit/index.js +1 -1
  231. package/dist/util/sveltekit/route-folders/index.js +101 -101
  232. package/dist/util/time/index.js +323 -323
  233. package/dist/util/unique/index.js +249 -249
  234. package/dist/valibot/date.js__ +10 -10
  235. package/dist/valibot/index.js +9 -9
  236. package/dist/valibot/url.js +95 -95
  237. package/dist/valibot/user.js +23 -23
  238. package/dist/zod/all.js +33 -33
  239. package/dist/zod/generic.js +11 -11
  240. package/dist/zod/javascript.js +32 -32
  241. package/dist/zod/user.js +16 -16
  242. package/dist/zod/web.js +52 -52
  243. package/package.json +112 -112
@@ -1,409 +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
- }
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
+ }