@hkdigital/lib-sveltekit 0.2.6 → 0.2.8

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