@navios/di 0.3.1 → 0.4.1

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 (97) hide show
  1. package/README.md +67 -6
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/clover.xml +2659 -0
  5. package/coverage/coverage-final.json +46 -0
  6. package/coverage/docs/examples/basic-usage.mts.html +376 -0
  7. package/coverage/docs/examples/factory-pattern.mts.html +1039 -0
  8. package/coverage/docs/examples/index.html +176 -0
  9. package/coverage/docs/examples/injection-tokens.mts.html +760 -0
  10. package/coverage/docs/examples/request-scope-example.mts.html +847 -0
  11. package/coverage/docs/examples/service-lifecycle.mts.html +1162 -0
  12. package/coverage/favicon.png +0 -0
  13. package/coverage/index.html +236 -0
  14. package/coverage/lib/_tsup-dts-rollup.d.mts.html +2806 -0
  15. package/coverage/lib/index.d.mts.html +310 -0
  16. package/coverage/lib/index.html +131 -0
  17. package/coverage/prettify.css +1 -0
  18. package/coverage/prettify.js +2 -0
  19. package/coverage/sort-arrow-sprite.png +0 -0
  20. package/coverage/sorter.js +196 -0
  21. package/coverage/src/container.mts.html +586 -0
  22. package/coverage/src/decorators/factory.decorator.mts.html +322 -0
  23. package/coverage/src/decorators/index.html +146 -0
  24. package/coverage/src/decorators/index.mts.html +91 -0
  25. package/coverage/src/decorators/injectable.decorator.mts.html +394 -0
  26. package/coverage/src/enums/index.html +146 -0
  27. package/coverage/src/enums/index.mts.html +91 -0
  28. package/coverage/src/enums/injectable-scope.enum.mts.html +127 -0
  29. package/coverage/src/enums/injectable-type.enum.mts.html +97 -0
  30. package/coverage/src/errors/errors.enum.mts.html +109 -0
  31. package/coverage/src/errors/factory-not-found.mts.html +109 -0
  32. package/coverage/src/errors/factory-token-not-resolved.mts.html +115 -0
  33. package/coverage/src/errors/index.html +221 -0
  34. package/coverage/src/errors/index.mts.html +106 -0
  35. package/coverage/src/errors/instance-destroying.mts.html +109 -0
  36. package/coverage/src/errors/instance-expired.mts.html +109 -0
  37. package/coverage/src/errors/instance-not-found.mts.html +109 -0
  38. package/coverage/src/errors/unknown-error.mts.html +130 -0
  39. package/coverage/src/event-emitter.mts.html +400 -0
  40. package/coverage/src/factory-context.mts.html +109 -0
  41. package/coverage/src/index.html +296 -0
  42. package/coverage/src/index.mts.html +139 -0
  43. package/coverage/src/injection-token.mts.html +571 -0
  44. package/coverage/src/injector.mts.html +133 -0
  45. package/coverage/src/interfaces/factory.interface.mts.html +121 -0
  46. package/coverage/src/interfaces/index.html +161 -0
  47. package/coverage/src/interfaces/index.mts.html +94 -0
  48. package/coverage/src/interfaces/on-service-destroy.interface.mts.html +94 -0
  49. package/coverage/src/interfaces/on-service-init.interface.mts.html +94 -0
  50. package/coverage/src/registry.mts.html +247 -0
  51. package/coverage/src/request-context-holder.mts.html +607 -0
  52. package/coverage/src/service-instantiator.mts.html +559 -0
  53. package/coverage/src/service-locator-event-bus.mts.html +289 -0
  54. package/coverage/src/service-locator-instance-holder.mts.html +307 -0
  55. package/coverage/src/service-locator-manager.mts.html +604 -0
  56. package/coverage/src/service-locator.mts.html +2911 -0
  57. package/coverage/src/symbols/index.html +131 -0
  58. package/coverage/src/symbols/index.mts.html +88 -0
  59. package/coverage/src/symbols/injectable-token.mts.html +88 -0
  60. package/coverage/src/utils/defer.mts.html +304 -0
  61. package/coverage/src/utils/get-injectable-token.mts.html +142 -0
  62. package/coverage/src/utils/get-injectors.mts.html +691 -0
  63. package/coverage/src/utils/index.html +176 -0
  64. package/coverage/src/utils/index.mts.html +97 -0
  65. package/coverage/src/utils/types.mts.html +241 -0
  66. package/docs/README.md +5 -2
  67. package/docs/api-reference.md +38 -0
  68. package/docs/container.md +75 -0
  69. package/docs/getting-started.md +4 -3
  70. package/docs/injectable.md +4 -3
  71. package/docs/migration.md +177 -0
  72. package/docs/request-contexts.md +364 -0
  73. package/lib/_tsup-dts-rollup.d.mts +182 -41
  74. package/lib/_tsup-dts-rollup.d.ts +182 -41
  75. package/lib/index.d.mts +1 -0
  76. package/lib/index.d.ts +1 -0
  77. package/lib/index.js +480 -294
  78. package/lib/index.js.map +1 -1
  79. package/lib/index.mjs +480 -295
  80. package/lib/index.mjs.map +1 -1
  81. package/package.json +1 -1
  82. package/src/__tests__/defer.spec.mts +166 -0
  83. package/src/__tests__/errors.spec.mts +61 -0
  84. package/src/__tests__/event-emitter.spec.mts +163 -0
  85. package/src/__tests__/get-injectors.spec.mts +70 -0
  86. package/src/__tests__/registry.spec.mts +335 -0
  87. package/src/__tests__/request-scope.spec.mts +34 -35
  88. package/src/__tests__/service-instantiator.spec.mts +408 -0
  89. package/src/__tests__/service-locator-event-bus.spec.mts +242 -0
  90. package/src/__tests__/service-locator-manager.spec.mts +370 -0
  91. package/src/__tests__/unified-api.spec.mts +130 -0
  92. package/src/base-instance-holder-manager.mts +175 -0
  93. package/src/event-emitter.mts +5 -5
  94. package/src/index.mts +1 -0
  95. package/src/request-context-holder.mts +73 -44
  96. package/src/service-locator-manager.mts +12 -70
  97. package/src/service-locator.mts +421 -226
@@ -0,0 +1,370 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+
3
+ import { InjectableScope, InjectableType } from '../enums/index.mjs'
4
+ import {
5
+ InstanceDestroying,
6
+ InstanceExpired,
7
+ InstanceNotFound,
8
+ } from '../errors/index.mjs'
9
+ import { ServiceLocatorInstanceHolderStatus } from '../service-locator-instance-holder.mjs'
10
+ import { ServiceLocatorManager } from '../service-locator-manager.mjs'
11
+
12
+ describe('ServiceLocatorManager', () => {
13
+ let manager: ServiceLocatorManager
14
+ let mockLogger: Console
15
+
16
+ beforeEach(() => {
17
+ mockLogger = {
18
+ log: vi.fn(),
19
+ } as any as Console
20
+ manager = new ServiceLocatorManager(mockLogger)
21
+ })
22
+
23
+ describe('constructor', () => {
24
+ it('should create manager without logger', () => {
25
+ const managerWithoutLogger = new ServiceLocatorManager()
26
+ expect(managerWithoutLogger).toBeDefined()
27
+ })
28
+
29
+ it('should create manager with logger', () => {
30
+ expect(manager).toBeDefined()
31
+ })
32
+ })
33
+
34
+ describe('get', () => {
35
+ it('should return InstanceNotFound for non-existent instance', () => {
36
+ const result = manager.get('non-existent')
37
+
38
+ expect(result).toHaveLength(1)
39
+ expect(result[0]).toBeInstanceOf(InstanceNotFound)
40
+ expect(mockLogger.log).toHaveBeenCalledWith(
41
+ '[ServiceLocatorManager]#getInstanceHolder() Instance non-existent not found',
42
+ )
43
+ })
44
+
45
+ it('should return valid holder for existing instance', () => {
46
+ const holder = manager.storeCreatedHolder(
47
+ 'test-instance',
48
+ { value: 'test' },
49
+ InjectableType.Class,
50
+ InjectableScope.Singleton,
51
+ )
52
+
53
+ const result = manager.get('test-instance')
54
+
55
+ expect(result).toHaveLength(2)
56
+ expect(result[0]).toBeUndefined()
57
+ expect(result[1]).toBe(holder)
58
+ })
59
+
60
+ it('should return InstanceExpired for expired instance with TTL', () => {
61
+ const holder = manager.storeCreatedHolder(
62
+ 'expired-instance',
63
+ { value: 'test' },
64
+ InjectableType.Class,
65
+ InjectableScope.Singleton,
66
+ new Set(),
67
+ 100, // 100ms TTL
68
+ )
69
+
70
+ // Manually set creation time to past
71
+ holder.createdAt = Date.now() - 200
72
+
73
+ const result = manager.get('expired-instance')
74
+
75
+ expect(result).toHaveLength(2)
76
+ expect(result[0]).toBeInstanceOf(InstanceExpired)
77
+ expect(result[1]).toBe(holder)
78
+ expect(mockLogger.log).toHaveBeenCalledWith(
79
+ '[ServiceLocatorManager]#getInstanceHolder() TTL expired for expired-instance',
80
+ )
81
+ })
82
+
83
+ it('should return InstanceDestroying for destroying instance', () => {
84
+ const holder = manager.storeCreatedHolder(
85
+ 'destroying-instance',
86
+ { value: 'test' },
87
+ InjectableType.Class,
88
+ InjectableScope.Singleton,
89
+ )
90
+
91
+ // Manually set status to destroying
92
+ holder.status = ServiceLocatorInstanceHolderStatus.Destroying
93
+
94
+ const result = manager.get('destroying-instance')
95
+
96
+ expect(result).toHaveLength(2)
97
+ expect(result[0]).toBeInstanceOf(InstanceDestroying)
98
+ expect(result[1]).toBe(holder)
99
+ expect(mockLogger.log).toHaveBeenCalledWith(
100
+ '[ServiceLocatorManager]#getInstanceHolder() Instance destroying-instance is destroying',
101
+ )
102
+ })
103
+
104
+ it('should return error for instance in error state', () => {
105
+ const holder = manager.storeCreatedHolder(
106
+ 'error-instance',
107
+ { value: 'test' },
108
+ InjectableType.Class,
109
+ InjectableScope.Singleton,
110
+ )
111
+
112
+ // Manually set status to error with an error instance
113
+ holder.status = ServiceLocatorInstanceHolderStatus.Error
114
+ const errorInstance = new InstanceNotFound('error-instance')
115
+ holder.instance = errorInstance
116
+
117
+ const result = manager.get('error-instance')
118
+
119
+ expect(result).toHaveLength(2)
120
+ expect(result[0]).toBeInstanceOf(InstanceNotFound)
121
+ expect(result[1]).toBe(holder)
122
+ expect(mockLogger.log).toHaveBeenCalledWith(
123
+ '[ServiceLocatorManager]#getInstanceHolder() Instance error-instance is in error state',
124
+ )
125
+ })
126
+
127
+ it('should handle instance with infinite TTL correctly', () => {
128
+ const holder = manager.storeCreatedHolder(
129
+ 'infinite-ttl-instance',
130
+ { value: 'test' },
131
+ InjectableType.Class,
132
+ InjectableScope.Singleton,
133
+ new Set(),
134
+ Infinity,
135
+ )
136
+
137
+ const result = manager.get('infinite-ttl-instance')
138
+
139
+ expect(result).toHaveLength(2)
140
+ expect(result[0]).toBeUndefined()
141
+ expect(result[1]).toBe(holder)
142
+ })
143
+ })
144
+
145
+ describe('set', () => {
146
+ it('should store holder with given name', () => {
147
+ const holder = manager.storeCreatedHolder(
148
+ 'test-instance',
149
+ { value: 'test' },
150
+ InjectableType.Class,
151
+ InjectableScope.Singleton,
152
+ )
153
+
154
+ manager.set('new-name', holder)
155
+
156
+ const result = manager.get('new-name')
157
+ expect(result).toHaveLength(2)
158
+ expect(result[0]).toBeUndefined()
159
+ expect(result[1]).toBe(holder)
160
+ })
161
+ })
162
+
163
+ describe('has', () => {
164
+ it('should return false for non-existent instance', () => {
165
+ const result = manager.has('non-existent')
166
+
167
+ expect(result).toHaveLength(2)
168
+ expect(result[0]).toBeUndefined()
169
+ expect(result[1]).toBe(false)
170
+ })
171
+
172
+ it('should return true for existing instance', () => {
173
+ manager.storeCreatedHolder(
174
+ 'test-instance',
175
+ { value: 'test' },
176
+ InjectableType.Class,
177
+ InjectableScope.Singleton,
178
+ )
179
+
180
+ const result = manager.has('test-instance')
181
+
182
+ expect(result).toHaveLength(2)
183
+ expect(result[0]).toBeUndefined()
184
+ expect(result[1]).toBe(true)
185
+ })
186
+
187
+ it('should return error for expired instance', () => {
188
+ const holder = manager.storeCreatedHolder(
189
+ 'expired-instance',
190
+ { value: 'test' },
191
+ InjectableType.Class,
192
+ InjectableScope.Singleton,
193
+ new Set(),
194
+ 100,
195
+ )
196
+
197
+ holder.createdAt = Date.now() - 200
198
+
199
+ const result = manager.has('expired-instance')
200
+
201
+ expect(result).toHaveLength(1)
202
+ expect(result[0]).toBeInstanceOf(InstanceExpired)
203
+ })
204
+
205
+ it('should return error for destroying instance', () => {
206
+ const holder = manager.storeCreatedHolder(
207
+ 'destroying-instance',
208
+ { value: 'test' },
209
+ InjectableType.Class,
210
+ InjectableScope.Singleton,
211
+ )
212
+
213
+ holder.status = ServiceLocatorInstanceHolderStatus.Destroying
214
+
215
+ const result = manager.has('destroying-instance')
216
+
217
+ expect(result).toHaveLength(1)
218
+ expect(result[0]).toBeInstanceOf(InstanceDestroying)
219
+ })
220
+ })
221
+
222
+ describe('delete', () => {
223
+ it('should delete existing instance and return true', () => {
224
+ manager.storeCreatedHolder(
225
+ 'test-instance',
226
+ { value: 'test' },
227
+ InjectableType.Class,
228
+ InjectableScope.Singleton,
229
+ )
230
+
231
+ const result = manager.delete('test-instance')
232
+ expect(result).toBe(true)
233
+
234
+ const getResult = manager.get('test-instance')
235
+ expect(getResult).toHaveLength(1)
236
+ expect(getResult[0]).toBeInstanceOf(InstanceNotFound)
237
+ })
238
+
239
+ it('should return false for non-existent instance', () => {
240
+ const result = manager.delete('non-existent')
241
+ expect(result).toBe(false)
242
+ })
243
+ })
244
+
245
+ describe('filter', () => {
246
+ it('should filter instances by predicate', () => {
247
+ manager.storeCreatedHolder(
248
+ 'instance1',
249
+ { value: 'test1' },
250
+ InjectableType.Class,
251
+ InjectableScope.Singleton,
252
+ )
253
+ manager.storeCreatedHolder(
254
+ 'instance2',
255
+ { value: 'test2' },
256
+ InjectableType.Factory,
257
+ InjectableScope.Transient,
258
+ )
259
+
260
+ const result = manager.filter(
261
+ (holder) => holder.type === InjectableType.Class,
262
+ )
263
+
264
+ expect(result.size).toBe(1)
265
+ expect(result.has('instance1')).toBe(true)
266
+ expect(result.has('instance2')).toBe(false)
267
+ })
268
+
269
+ it('should return empty map when no instances match', () => {
270
+ manager.storeCreatedHolder(
271
+ 'instance1',
272
+ { value: 'test1' },
273
+ InjectableType.Class,
274
+ InjectableScope.Singleton,
275
+ )
276
+
277
+ const result = manager.filter(
278
+ (holder) => holder.type === InjectableType.Factory,
279
+ )
280
+
281
+ expect(result.size).toBe(0)
282
+ })
283
+ })
284
+
285
+ describe('createCreatingHolder', () => {
286
+ it('should create holder with Creating status and deferred promise', () => {
287
+ const [deferred, holder] = manager.createCreatingHolder(
288
+ 'test-instance',
289
+ InjectableType.Class,
290
+ InjectableScope.Singleton,
291
+ )
292
+
293
+ expect(deferred).toBeDefined()
294
+ expect(deferred.promise).toBeInstanceOf(Promise)
295
+ expect(holder.status).toBe(ServiceLocatorInstanceHolderStatus.Creating)
296
+ expect(holder.name).toBe('test-instance')
297
+ expect(holder.instance).toBeNull()
298
+ expect(holder.creationPromise).toBe(deferred.promise)
299
+ expect(holder.type).toBe(InjectableType.Class)
300
+ expect(holder.scope).toBe(InjectableScope.Singleton)
301
+ expect(holder.deps).toEqual(new Set())
302
+ expect(holder.ttl).toBe(Infinity)
303
+ })
304
+
305
+ it('should create holder with custom dependencies and TTL', () => {
306
+ const deps = new Set(['dep1', 'dep2'])
307
+ const ttl = 5000
308
+
309
+ const [deferred, holder] = manager.createCreatingHolder(
310
+ 'test-instance',
311
+ InjectableType.Factory,
312
+ InjectableScope.Request,
313
+ deps,
314
+ ttl,
315
+ )
316
+
317
+ expect(holder.deps).toBe(deps)
318
+ expect(holder.ttl).toBe(ttl)
319
+ expect(holder.type).toBe(InjectableType.Factory)
320
+ expect(holder.scope).toBe(InjectableScope.Request)
321
+ })
322
+ })
323
+
324
+ describe('storeCreatedHolder', () => {
325
+ it('should create and store holder with Created status', () => {
326
+ const instance = { value: 'test' }
327
+ const holder = manager.storeCreatedHolder(
328
+ 'test-instance',
329
+ instance,
330
+ InjectableType.Class,
331
+ InjectableScope.Singleton,
332
+ )
333
+
334
+ expect(holder.status).toBe(ServiceLocatorInstanceHolderStatus.Created)
335
+ expect(holder.name).toBe('test-instance')
336
+ expect(holder.instance).toBe(instance)
337
+ expect(holder.creationPromise).toBeNull()
338
+ expect(holder.type).toBe(InjectableType.Class)
339
+ expect(holder.scope).toBe(InjectableScope.Singleton)
340
+ expect(holder.deps).toEqual(new Set())
341
+ expect(holder.ttl).toBe(Infinity)
342
+
343
+ // Verify it's stored
344
+ const getResult = manager.get('test-instance')
345
+ expect(getResult).toHaveLength(2)
346
+ expect(getResult[0]).toBeUndefined()
347
+ expect(getResult[1]).toBe(holder)
348
+ })
349
+
350
+ it('should create holder with custom dependencies and TTL', () => {
351
+ const deps = new Set(['dep1', 'dep2'])
352
+ const ttl = 5000
353
+ const instance = { value: 'test' }
354
+
355
+ const holder = manager.storeCreatedHolder(
356
+ 'test-instance',
357
+ instance,
358
+ InjectableType.Factory,
359
+ InjectableScope.Request,
360
+ deps,
361
+ ttl,
362
+ )
363
+
364
+ expect(holder.deps).toBe(deps)
365
+ expect(holder.ttl).toBe(ttl)
366
+ expect(holder.type).toBe(InjectableType.Factory)
367
+ expect(holder.scope).toBe(InjectableScope.Request)
368
+ })
369
+ })
370
+ })
@@ -0,0 +1,130 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { InjectableScope, InjectableType } from '../enums/index.mjs'
4
+ import { DefaultRequestContextHolder } from '../request-context-holder.mjs'
5
+ import { ServiceLocatorManager } from '../service-locator-manager.mjs'
6
+
7
+ describe('Unified API', () => {
8
+ describe('Common methods between ServiceLocatorManager and RequestContextHolder', () => {
9
+ it('should have the same basic API surface', () => {
10
+ const serviceManager = new ServiceLocatorManager()
11
+ const requestContext = new DefaultRequestContextHolder(
12
+ 'test-request',
13
+ 100,
14
+ )
15
+
16
+ // Both should have the same common methods
17
+ expect(typeof serviceManager.size).toBe('function')
18
+ expect(typeof requestContext.size).toBe('function')
19
+
20
+ expect(typeof serviceManager.isEmpty).toBe('function')
21
+ expect(typeof requestContext.isEmpty).toBe('function')
22
+
23
+ expect(typeof serviceManager.filter).toBe('function')
24
+ expect(typeof requestContext.filter).toBe('function')
25
+
26
+ expect(typeof serviceManager.clear).toBe('function')
27
+ expect(typeof requestContext.clear).toBe('function')
28
+
29
+ expect(typeof serviceManager.delete).toBe('function')
30
+ expect(typeof requestContext.delete).toBe('function')
31
+
32
+ expect(typeof serviceManager.getAllNames).toBe('function')
33
+ expect(typeof requestContext.getAllNames).toBe('function')
34
+
35
+ expect(typeof serviceManager.getAllHolders).toBe('function')
36
+ expect(typeof requestContext.getAllHolders).toBe('function')
37
+
38
+ expect(typeof serviceManager.createCreatingHolder).toBe('function')
39
+ expect(typeof requestContext.createCreatingHolder).toBe('function')
40
+ })
41
+
42
+ it('should work with the same holder creation patterns', () => {
43
+ const serviceManager = new ServiceLocatorManager()
44
+ const requestContext = new DefaultRequestContextHolder(
45
+ 'test-request',
46
+ 100,
47
+ )
48
+
49
+ // Both should be able to create holders the same way
50
+ const [deferred1, holder1] = serviceManager.createCreatingHolder(
51
+ 'Service1',
52
+ InjectableType.Class,
53
+ InjectableScope.Singleton,
54
+ )
55
+
56
+ const [deferred2, holder2] = requestContext.createCreatingHolder(
57
+ 'Service2',
58
+ InjectableType.Class,
59
+ InjectableScope.Request,
60
+ )
61
+
62
+ expect(holder1.name).toBe('Service1')
63
+ expect(holder1.scope).toBe(InjectableScope.Singleton)
64
+ expect(holder2.name).toBe('Service2')
65
+ expect(holder2.scope).toBe(InjectableScope.Request)
66
+
67
+ // Both should be able to store holders
68
+ serviceManager.set('Service1', holder1)
69
+ requestContext.set('Service2', holder2)
70
+
71
+ expect(serviceManager.size()).toBe(1)
72
+ expect(requestContext.size()).toBe(1)
73
+ })
74
+
75
+ it('should support the same filtering patterns', () => {
76
+ const serviceManager = new ServiceLocatorManager()
77
+ const requestContext = new DefaultRequestContextHolder(
78
+ 'test-request',
79
+ 100,
80
+ )
81
+
82
+ // Create and store different types of holders
83
+ const singletonHolder = serviceManager.storeCreatedHolder(
84
+ 'Singleton',
85
+ {},
86
+ InjectableType.Class,
87
+ InjectableScope.Singleton,
88
+ )
89
+
90
+ // Create a holder first, then add it to the request context
91
+ const [, transientHolderTemp] = requestContext.createCreatingHolder(
92
+ 'Transient',
93
+ InjectableType.Class,
94
+ InjectableScope.Transient,
95
+ )
96
+ requestContext.set('Transient', transientHolderTemp)
97
+ const transientHolder = transientHolderTemp
98
+
99
+ // Both should support the same filtering API
100
+ const singletons = serviceManager.filter(
101
+ (holder) => holder.scope === InjectableScope.Singleton,
102
+ )
103
+ const transients = requestContext.filter(
104
+ (holder) => holder.scope === InjectableScope.Transient,
105
+ )
106
+
107
+ expect(singletons.size).toBe(1)
108
+ expect(transients.size).toBe(1)
109
+ expect(singletons.get('Singleton')).toBe(singletonHolder)
110
+ expect(transients.get('Transient')).toBe(transientHolder)
111
+ })
112
+
113
+ it('should maintain their specific behaviors while sharing common API', () => {
114
+ const serviceManager = new ServiceLocatorManager()
115
+ const requestContext = new DefaultRequestContextHolder(
116
+ 'test-request',
117
+ 100,
118
+ )
119
+
120
+ // ServiceLocatorManager has specific error handling
121
+ const [notFound] = serviceManager.get('NonExistent')
122
+ expect(notFound).toBeDefined()
123
+
124
+ // RequestContextHolder has specific request features
125
+ expect(requestContext.requestId).toBe('test-request')
126
+ expect(requestContext.priority).toBe(100)
127
+ expect(requestContext.metadata).toBeInstanceOf(Map)
128
+ })
129
+ })
130
+ })
@@ -0,0 +1,175 @@
1
+ import type { ServiceLocatorInstanceHolder } from './service-locator-instance-holder.mjs'
2
+
3
+ import { InjectableScope, InjectableType } from './enums/index.mjs'
4
+ import { ServiceLocatorInstanceHolderStatus } from './service-locator-instance-holder.mjs'
5
+ import { createDeferred } from './utils/defer.mjs'
6
+
7
+ /**
8
+ * Abstract base class that provides common functionality for managing ServiceLocatorInstanceHolder objects.
9
+ * This class contains shared patterns used by both RequestContextHolder and ServiceLocatorManager.
10
+ */
11
+ export abstract class BaseInstanceHolderManager {
12
+ protected readonly _holders: Map<string, ServiceLocatorInstanceHolder>
13
+
14
+ constructor(protected readonly logger: Console | null = null) {
15
+ this._holders = new Map()
16
+ }
17
+
18
+ /**
19
+ * Protected getter for accessing the holders map from subclasses.
20
+ */
21
+ protected get holders(): Map<string, ServiceLocatorInstanceHolder> {
22
+ return this._holders
23
+ }
24
+
25
+ /**
26
+ * Abstract method to get a holder by name. Each implementation defines its own return type
27
+ * based on their specific error handling and validation needs.
28
+ */
29
+ abstract get(name: string): any
30
+
31
+ /**
32
+ * Abstract method to set a holder by name. Each implementation may have different validation logic.
33
+ */
34
+ abstract set(name: string, holder: ServiceLocatorInstanceHolder): void
35
+
36
+ /**
37
+ * Abstract method to check if a holder exists. Each implementation may have different validation logic.
38
+ */
39
+ abstract has(name: string): any
40
+
41
+ /**
42
+ * Deletes a holder by name.
43
+ * @param name The name of the holder to delete
44
+ * @returns true if the holder was deleted, false if it didn't exist
45
+ */
46
+ delete(name: string): boolean {
47
+ return this._holders.delete(name)
48
+ }
49
+
50
+ /**
51
+ * Filters holders based on a predicate function.
52
+ * @param predicate Function to test each holder
53
+ * @returns A new Map containing only the holders that match the predicate
54
+ */
55
+ filter(
56
+ predicate: (
57
+ value: ServiceLocatorInstanceHolder<any>,
58
+ key: string,
59
+ ) => boolean,
60
+ ): Map<string, ServiceLocatorInstanceHolder> {
61
+ return new Map(
62
+ [...this._holders].filter(([key, value]) => predicate(value, key)),
63
+ )
64
+ }
65
+
66
+ /**
67
+ * Clears all holders from this manager.
68
+ */
69
+ clear(): void {
70
+ this._holders.clear()
71
+ }
72
+
73
+ /**
74
+ * Gets the number of holders currently managed.
75
+ */
76
+ size(): number {
77
+ return this._holders.size
78
+ }
79
+
80
+ /**
81
+ * Creates a new holder with Creating status and a deferred creation promise.
82
+ * This is useful for creating placeholder holders that can be fulfilled later.
83
+ * @param name The name of the instance
84
+ * @param type The injectable type
85
+ * @param scope The injectable scope
86
+ * @param deps Optional set of dependencies
87
+ * @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
88
+ * @returns A tuple containing the deferred promise and the holder
89
+ */
90
+ createCreatingHolder<Instance>(
91
+ name: string,
92
+ type: InjectableType,
93
+ scope: InjectableScope,
94
+ deps: Set<string> = new Set(),
95
+ ttl: number = Infinity,
96
+ ): [
97
+ ReturnType<typeof createDeferred<[undefined, Instance]>>,
98
+ ServiceLocatorInstanceHolder<Instance>,
99
+ ] {
100
+ const deferred = createDeferred<[undefined, Instance]>()
101
+
102
+ const holder: ServiceLocatorInstanceHolder<Instance> = {
103
+ status: ServiceLocatorInstanceHolderStatus.Creating,
104
+ name,
105
+ instance: null,
106
+ creationPromise: deferred.promise,
107
+ destroyPromise: null,
108
+ type,
109
+ scope,
110
+ deps,
111
+ destroyListeners: [],
112
+ createdAt: Date.now(),
113
+ ttl,
114
+ }
115
+
116
+ return [deferred, holder]
117
+ }
118
+
119
+ /**
120
+ * Creates a new holder with Created status and an actual instance.
121
+ * This is useful for creating holders that already have their instance ready.
122
+ * @param name The name of the instance
123
+ * @param instance The actual instance to store
124
+ * @param type The injectable type
125
+ * @param scope The injectable scope
126
+ * @param deps Optional set of dependencies
127
+ * @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
128
+ * @returns The created holder
129
+ */
130
+ protected createCreatedHolder<Instance>(
131
+ name: string,
132
+ instance: Instance,
133
+ type: InjectableType,
134
+ scope: InjectableScope,
135
+ deps: Set<string> = new Set(),
136
+ ttl: number = Infinity,
137
+ ): ServiceLocatorInstanceHolder<Instance> {
138
+ const holder: ServiceLocatorInstanceHolder<Instance> = {
139
+ status: ServiceLocatorInstanceHolderStatus.Created,
140
+ name,
141
+ instance,
142
+ creationPromise: null,
143
+ destroyPromise: null,
144
+ type,
145
+ scope,
146
+ deps,
147
+ destroyListeners: [],
148
+ createdAt: Date.now(),
149
+ ttl,
150
+ }
151
+
152
+ return holder
153
+ }
154
+
155
+ /**
156
+ * Gets all holder names currently managed.
157
+ */
158
+ getAllNames(): string[] {
159
+ return Array.from(this._holders.keys())
160
+ }
161
+
162
+ /**
163
+ * Gets all holders currently managed.
164
+ */
165
+ getAllHolders(): ServiceLocatorInstanceHolder[] {
166
+ return Array.from(this._holders.values())
167
+ }
168
+
169
+ /**
170
+ * Checks if this manager has any holders.
171
+ */
172
+ isEmpty(): boolean {
173
+ return this._holders.size === 0
174
+ }
175
+ }