@navios/di 0.2.1 → 0.3.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 (62) hide show
  1. package/README.md +299 -38
  2. package/docs/README.md +121 -48
  3. package/docs/api-reference.md +763 -0
  4. package/docs/container.md +274 -0
  5. package/docs/examples/basic-usage.mts +97 -0
  6. package/docs/examples/factory-pattern.mts +318 -0
  7. package/docs/examples/injection-tokens.mts +225 -0
  8. package/docs/examples/request-scope-example.mts +254 -0
  9. package/docs/examples/service-lifecycle.mts +359 -0
  10. package/docs/factory.md +584 -0
  11. package/docs/getting-started.md +308 -0
  12. package/docs/injectable.md +496 -0
  13. package/docs/injection-tokens.md +400 -0
  14. package/docs/lifecycle.md +539 -0
  15. package/docs/scopes.md +749 -0
  16. package/lib/_tsup-dts-rollup.d.mts +494 -145
  17. package/lib/_tsup-dts-rollup.d.ts +494 -145
  18. package/lib/index.d.mts +26 -12
  19. package/lib/index.d.ts +26 -12
  20. package/lib/index.js +1021 -470
  21. package/lib/index.js.map +1 -1
  22. package/lib/index.mjs +1011 -461
  23. package/lib/index.mjs.map +1 -1
  24. package/package.json +2 -2
  25. package/project.json +10 -2
  26. package/src/__tests__/container.spec.mts +1301 -0
  27. package/src/__tests__/factory.spec.mts +137 -0
  28. package/src/__tests__/injectable.spec.mts +32 -88
  29. package/src/__tests__/injection-token.spec.mts +333 -17
  30. package/src/__tests__/request-scope.spec.mts +427 -0
  31. package/src/__type-tests__/factory.spec-d.mts +65 -0
  32. package/src/__type-tests__/inject.spec-d.mts +27 -28
  33. package/src/__type-tests__/injectable.spec-d.mts +42 -206
  34. package/src/container.mts +167 -0
  35. package/src/decorators/factory.decorator.mts +79 -0
  36. package/src/decorators/index.mts +1 -0
  37. package/src/decorators/injectable.decorator.mts +6 -56
  38. package/src/enums/injectable-scope.enum.mts +5 -1
  39. package/src/event-emitter.mts +18 -20
  40. package/src/factory-context.mts +2 -10
  41. package/src/index.mts +3 -2
  42. package/src/injection-token.mts +19 -4
  43. package/src/injector.mts +8 -20
  44. package/src/interfaces/factory.interface.mts +3 -3
  45. package/src/interfaces/index.mts +2 -0
  46. package/src/interfaces/on-service-destroy.interface.mts +3 -0
  47. package/src/interfaces/on-service-init.interface.mts +3 -0
  48. package/src/registry.mts +7 -16
  49. package/src/request-context-holder.mts +174 -0
  50. package/src/service-instantiator.mts +158 -0
  51. package/src/service-locator-event-bus.mts +0 -28
  52. package/src/service-locator-instance-holder.mts +27 -16
  53. package/src/service-locator-manager.mts +84 -0
  54. package/src/service-locator.mts +548 -393
  55. package/src/utils/defer.mts +73 -0
  56. package/src/utils/get-injectors.mts +91 -78
  57. package/src/utils/index.mts +2 -0
  58. package/src/utils/types.mts +52 -0
  59. package/docs/concepts/injectable.md +0 -182
  60. package/docs/concepts/injection-token.md +0 -145
  61. package/src/proxy-service-locator.mts +0 -83
  62. package/src/resolve-service.mts +0 -41
@@ -2,7 +2,6 @@ import { expectTypeOf, test } from 'vitest'
2
2
  import { z } from 'zod/v4'
3
3
 
4
4
  import { Injectable } from '../decorators/index.mjs'
5
- import { InjectableType } from '../enums/index.mjs'
6
5
  import { InjectionToken } from '../injection-token.mjs'
7
6
 
8
7
  interface FooService {
@@ -17,8 +16,8 @@ const simpleOptionalObjectSchema = z
17
16
  foo: z.string(),
18
17
  })
19
18
  .optional()
20
- const simpleRecordSchema = z.record(z.string(), z.string())
21
- const simpleOptionalRecordSchema = z.record(z.string(), z.string()).optional()
19
+ // const simpleRecordSchema = z.record(z.string(), z.string())
20
+ // const simpleOptionalRecordSchema = z.record(z.string(), z.string()).optional()
22
21
 
23
22
  const typelessObjectToken = InjectionToken.create(
24
23
  Symbol.for('Typeless object token'),
@@ -28,14 +27,14 @@ const typelessOptionalObjectToken = InjectionToken.create(
28
27
  Symbol.for('Typeless optional object token'),
29
28
  simpleOptionalObjectSchema,
30
29
  )
31
- const typelessRecordToken = InjectionToken.create(
32
- Symbol.for('Typeless record token'),
33
- simpleRecordSchema,
34
- )
35
- const typelessOptionalRecordToken = InjectionToken.create(
36
- Symbol.for('Typeless optional record token'),
37
- simpleOptionalRecordSchema,
38
- )
30
+ // const typelessRecordToken = InjectionToken.create(
31
+ // Symbol.for('Typeless record token'),
32
+ // simpleRecordSchema,
33
+ // )
34
+ // const typelessOptionalRecordToken = InjectionToken.create(
35
+ // Symbol.for('Typeless optional record token'),
36
+ // simpleOptionalRecordSchema,
37
+ // )
39
38
 
40
39
  const typedObjectToken = InjectionToken.create<
41
40
  FooService,
@@ -45,14 +44,14 @@ const typedOptionalObjectToken = InjectionToken.create<
45
44
  FooService,
46
45
  typeof simpleOptionalObjectSchema
47
46
  >(Symbol.for('Typed optional object token'), simpleOptionalObjectSchema)
48
- const typedRecordToken = InjectionToken.create<
49
- FooService,
50
- typeof simpleRecordSchema
51
- >(Symbol.for('Typed record token'), simpleRecordSchema)
52
- const typedOptionalRecordToken = InjectionToken.create<
53
- FooService,
54
- typeof simpleOptionalRecordSchema
55
- >(Symbol.for('Typed optional record token'), simpleOptionalRecordSchema)
47
+ // const typedRecordToken = InjectionToken.create<
48
+ // FooService,
49
+ // typeof simpleRecordSchema
50
+ // >(Symbol.for('Typed record token'), simpleRecordSchema)
51
+ // const typedOptionalRecordToken = InjectionToken.create<
52
+ // FooService,
53
+ // typeof simpleOptionalRecordSchema
54
+ // >(Symbol.for('Typed optional record token'), simpleOptionalRecordSchema)
56
55
 
57
56
  const typedToken = InjectionToken.create<FooService>(Symbol.for('Typed token'))
58
57
 
@@ -62,24 +61,8 @@ test('Injectable types', () => {
62
61
  @Injectable()
63
62
  class {},
64
63
  ).toBeConstructibleWith()
65
- // #2
66
- expectTypeOf(
67
- @Injectable({
68
- type: InjectableType.Factory,
69
- })
70
- class {
71
- create() {}
72
- },
73
- ).toBeConstructibleWith()
74
- expectTypeOf(
75
- // @ts-expect-error should check that the class implements the factory
76
- @Injectable({
77
- type: InjectableType.Factory,
78
- })
79
- class {},
80
- ).toBeConstructibleWith()
81
64
 
82
- // #3 required argument
65
+ // #2 required argument
83
66
  expectTypeOf(
84
67
  @Injectable({
85
68
  token: typelessObjectToken,
@@ -90,7 +73,7 @@ test('Injectable types', () => {
90
73
  ).toBeConstructibleWith({
91
74
  foo: 'something',
92
75
  })
93
- // #3 it's required in token but optional in class allowed
76
+ // #2 it's required in token but optional in class allowed
94
77
  expectTypeOf(
95
78
  @Injectable({
96
79
  token: typelessObjectToken,
@@ -101,7 +84,7 @@ test('Injectable types', () => {
101
84
  ).toBeConstructibleWith({
102
85
  foo: 'something',
103
86
  })
104
- // #3 optional value but class accepts it
87
+ // #2 optional value but class accepts it
105
88
  expectTypeOf(
106
89
  @Injectable({
107
90
  token: typelessOptionalObjectToken,
@@ -112,7 +95,7 @@ test('Injectable types', () => {
112
95
  ).toBeConstructibleWith({
113
96
  foo: 'something',
114
97
  })
115
- // #3 optional value and class accepts it
98
+ // #2 optional value and class accepts it
116
99
  expectTypeOf(
117
100
  @Injectable({
118
101
  token: typelessOptionalObjectToken,
@@ -121,7 +104,7 @@ test('Injectable types', () => {
121
104
  constructor(public arg: z.infer<typeof simpleOptionalObjectSchema>) {}
122
105
  },
123
106
  ).toBeConstructibleWith(undefined)
124
- // #3 compatible schemas
107
+ // #2 compatible schemas
125
108
  expectTypeOf(
126
109
  @Injectable({
127
110
  token: typelessOptionalObjectToken,
@@ -130,7 +113,7 @@ test('Injectable types', () => {
130
113
  constructor(public arg?: z.infer<typeof simpleObjectSchema>) {}
131
114
  },
132
115
  ).toBeConstructibleWith(undefined)
133
- // #3 compatible schemas
116
+ // #2 compatible schemas
134
117
  expectTypeOf(
135
118
  // @ts-expect-error token has optional schema, but Class has required, should fail
136
119
  @Injectable({
@@ -143,7 +126,7 @@ test('Injectable types', () => {
143
126
  foo: 'something',
144
127
  })
145
128
 
146
- // #3 typed token and required argument
129
+ // #2 typed token and required argument
147
130
  expectTypeOf(
148
131
  @Injectable({
149
132
  token: typedObjectToken,
@@ -158,7 +141,7 @@ test('Injectable types', () => {
158
141
  ).toBeConstructibleWith({
159
142
  foo: 'something',
160
143
  })
161
- // #3 typed token and required argument
144
+ // #2 typed token and required argument
162
145
  expectTypeOf(
163
146
  @Injectable({
164
147
  token: typedOptionalObjectToken,
@@ -173,7 +156,7 @@ test('Injectable types', () => {
173
156
  ).toBeConstructibleWith({
174
157
  foo: 'something',
175
158
  })
176
- // #3 should fail if not compatible
159
+ // #2 should fail if not compatible
177
160
  expectTypeOf(
178
161
  // @ts-expect-error class doesn't implement the token type
179
162
  @Injectable({
@@ -185,7 +168,7 @@ test('Injectable types', () => {
185
168
  ).toBeConstructibleWith({
186
169
  foo: 'something',
187
170
  })
188
- // #3 should fail if not compatible
171
+ // #2 should fail if not compatible
189
172
  expectTypeOf(
190
173
  // @ts-expect-error class doesn't implement the token type
191
174
  @Injectable({
@@ -201,7 +184,7 @@ test('Injectable types', () => {
201
184
  ).toBeConstructibleWith({
202
185
  foo: 'something',
203
186
  })
204
- // #3 typed token without schema
187
+ // #2 typed token without schema
205
188
  expectTypeOf(
206
189
  @Injectable({
207
190
  token: typedToken,
@@ -213,7 +196,7 @@ test('Injectable types', () => {
213
196
  }
214
197
  },
215
198
  ).toBeConstructibleWith()
216
- // #3 typed token without schema fail if not compatible
199
+ // #2 typed token without schema fail if not compatible
217
200
  expectTypeOf(
218
201
  // @ts-expect-error class doesn't implement the token type
219
202
  @Injectable({
@@ -224,69 +207,6 @@ test('Injectable types', () => {
224
207
  },
225
208
  ).toBeConstructibleWith()
226
209
 
227
- // #4 factory with typed token
228
- expectTypeOf(
229
- @Injectable({
230
- type: InjectableType.Factory,
231
- token: typedToken,
232
- })
233
- class {
234
- constructor() {}
235
- create() {
236
- return {
237
- makeFoo: () => 'foo',
238
- }
239
- }
240
- },
241
- ).toBeConstructibleWith()
242
- // #4 factory with typed token without schema should fail if not compatible
243
- expectTypeOf(
244
- // @ts-expect-error factory doesn't implement the token type
245
- @Injectable({
246
- type: InjectableType.Factory,
247
- token: typedToken,
248
- })
249
- class {
250
- constructor() {}
251
- create(ctx: any, arg: z.infer<typeof simpleObjectSchema>) {
252
- return {
253
- makeFoo: () => 'foo',
254
- }
255
- }
256
- },
257
- ).toBeConstructibleWith()
258
- // #4 factory with typed token fail if not compatible
259
- expectTypeOf(
260
- // @ts-expect-error class doesn't implement the token type
261
- @Injectable({
262
- type: InjectableType.Factory,
263
- token: typedToken,
264
- })
265
- class {
266
- constructor() {}
267
- create() {
268
- return {
269
- // makeFoo: () => 'foo',
270
- }
271
- }
272
- },
273
- ).toBeConstructibleWith()
274
- // #4 factory with typed token and schema
275
- expectTypeOf(
276
- @Injectable({
277
- type: InjectableType.Factory,
278
- token: typedObjectToken,
279
- })
280
- class {
281
- constructor() {}
282
- create(ctx: any, arg: z.infer<typeof simpleObjectSchema>) {
283
- return {
284
- makeFoo: () => 'foo',
285
- }
286
- }
287
- },
288
- )
289
-
290
210
  // #1 Injectable w/o decorators enabled in project
291
211
  expectTypeOf(
292
212
  Injectable({
@@ -300,25 +220,8 @@ test('Injectable types', () => {
300
220
  },
301
221
  ),
302
222
  ).toBeConstructibleWith()
303
- expectTypeOf(
304
- Injectable({
305
- type: InjectableType.Factory,
306
- })(
307
- class {
308
- create() {}
309
- },
310
- ),
311
- ).toBeConstructibleWith()
312
- expectTypeOf(
313
- Injectable({
314
- type: InjectableType.Factory,
315
- })(
316
- // @ts-expect-error should check that the class implements the factory
317
- class {},
318
- ),
319
- ).toBeConstructibleWith()
320
223
 
321
- // #3 required argument
224
+ // #2 required argument
322
225
  expectTypeOf(
323
226
  Injectable({
324
227
  token: typelessObjectToken,
@@ -330,7 +233,7 @@ test('Injectable types', () => {
330
233
  ).toBeConstructibleWith({
331
234
  foo: 'something',
332
235
  })
333
- // #3 it's required in token but optional in class allowed
236
+ // #2 it's required in token but optional in class allowed
334
237
  expectTypeOf(
335
238
  Injectable({
336
239
  token: typelessObjectToken,
@@ -342,7 +245,7 @@ test('Injectable types', () => {
342
245
  ).toBeConstructibleWith({
343
246
  foo: 'something',
344
247
  })
345
- // #3 optional value but class accepts it
248
+ // #2 optional value but class accepts it
346
249
  expectTypeOf(
347
250
  Injectable({
348
251
  token: typelessOptionalObjectToken,
@@ -354,7 +257,7 @@ test('Injectable types', () => {
354
257
  ).toBeConstructibleWith({
355
258
  foo: 'something',
356
259
  })
357
- // #3 optional value and class accepts it
260
+ // #2 optional value and class accepts it
358
261
  expectTypeOf(
359
262
  Injectable({
360
263
  token: typelessOptionalObjectToken,
@@ -364,7 +267,7 @@ test('Injectable types', () => {
364
267
  },
365
268
  ),
366
269
  ).toBeConstructibleWith(undefined)
367
- // #3 compatible schemas
270
+ // #2 compatible schemas
368
271
  expectTypeOf(
369
272
  Injectable({
370
273
  token: typelessOptionalObjectToken,
@@ -374,7 +277,7 @@ test('Injectable types', () => {
374
277
  },
375
278
  ),
376
279
  ).toBeConstructibleWith(undefined)
377
- // #3 compatible schemas
280
+ // #2 compatible schemas
378
281
  expectTypeOf(
379
282
  Injectable({
380
283
  token: typelessOptionalObjectToken,
@@ -388,7 +291,7 @@ test('Injectable types', () => {
388
291
  foo: 'something',
389
292
  })
390
293
 
391
- // #3 typed token and required argument
294
+ // #2 typed token and required argument
392
295
  expectTypeOf(
393
296
  Injectable({
394
297
  token: typedObjectToken,
@@ -404,7 +307,7 @@ test('Injectable types', () => {
404
307
  ).toBeConstructibleWith({
405
308
  foo: 'something',
406
309
  })
407
- // #3 typed token and required argument
310
+ // #2 typed token and required argument
408
311
  expectTypeOf(
409
312
  Injectable({
410
313
  token: typedOptionalObjectToken,
@@ -420,7 +323,7 @@ test('Injectable types', () => {
420
323
  ).toBeConstructibleWith({
421
324
  foo: 'something',
422
325
  })
423
- // #3 should fail if not compatible
326
+ // #2 should fail if not compatible
424
327
  expectTypeOf(
425
328
  Injectable({
426
329
  token: typedOptionalObjectToken,
@@ -433,7 +336,7 @@ test('Injectable types', () => {
433
336
  ).toBeConstructibleWith({
434
337
  foo: 'something',
435
338
  })
436
- // #3 should fail if not compatible
339
+ // #2 should fail if not compatible
437
340
  expectTypeOf(
438
341
  Injectable({
439
342
  token: typedOptionalObjectToken,
@@ -450,7 +353,7 @@ test('Injectable types', () => {
450
353
  ).toBeConstructibleWith({
451
354
  foo: 'something',
452
355
  })
453
- // #3 typed token without schema
356
+ // #2 typed token without schema
454
357
  expectTypeOf(
455
358
  Injectable({
456
359
  token: typedToken,
@@ -463,82 +366,15 @@ test('Injectable types', () => {
463
366
  },
464
367
  ),
465
368
  ).toBeConstructibleWith()
466
- // #3 typed token without schema fail if not compatible
467
- expectTypeOf(
468
- Injectable({
469
- token: typedToken,
470
- })(
471
- // @ts-expect-error class doesn't implement the token type
472
- class {
473
- constructor() {}
474
- },
475
- ),
476
- ).toBeConstructibleWith()
477
-
478
- // #4 factory with typed token
479
- expectTypeOf(
480
- Injectable({
481
- type: InjectableType.Factory,
482
- token: typedToken,
483
- })(
484
- class {
485
- constructor() {}
486
- create() {
487
- return {
488
- makeFoo: () => 'foo',
489
- }
490
- }
491
- },
492
- ),
493
- ).toBeConstructibleWith()
494
- // #4 factory with typed token without schema should fail if not compatible
369
+ // #2 typed token without schema fail if not compatible
495
370
  expectTypeOf(
496
371
  Injectable({
497
- type: InjectableType.Factory,
498
- token: typedToken,
499
- })(
500
- // @ts-expect-error factory doesn't implement the token type
501
- class {
502
- constructor() {}
503
- create(ctx: any, arg: z.infer<typeof simpleObjectSchema>) {
504
- return {
505
- makeFoo: () => 'foo',
506
- }
507
- }
508
- },
509
- ),
510
- ).toBeConstructibleWith()
511
- // #4 factory with typed token fail if not compatible
512
- expectTypeOf(
513
- Injectable({
514
- type: InjectableType.Factory,
515
372
  token: typedToken,
516
373
  })(
517
374
  // @ts-expect-error class doesn't implement the token type
518
375
  class {
519
376
  constructor() {}
520
- create() {
521
- return {
522
- // makeFoo: () => 'foo',
523
- }
524
- }
525
377
  },
526
378
  ),
527
379
  ).toBeConstructibleWith()
528
- // #4 factory with typed token and schema
529
- expectTypeOf(
530
- Injectable({
531
- type: InjectableType.Factory,
532
- token: typedObjectToken,
533
- })(
534
- class {
535
- constructor() {}
536
- create(ctx: any, arg: z.infer<typeof simpleObjectSchema>) {
537
- return {
538
- makeFoo: () => 'foo',
539
- }
540
- }
541
- },
542
- ),
543
- )
544
380
  })
@@ -0,0 +1,167 @@
1
+ import type { z, ZodType } from 'zod/v4'
2
+
3
+ import type {
4
+ BoundInjectionToken,
5
+ ClassType,
6
+ FactoryInjectionToken,
7
+ InjectionToken,
8
+ InjectionTokenSchemaType,
9
+ } from './injection-token.mjs'
10
+ import type { Factorable } from './interfaces/factory.interface.mjs'
11
+ import type { Registry } from './registry.mjs'
12
+ import type { RequestContextHolder } from './request-context-holder.mjs'
13
+ import type { Injectors } from './utils/index.mjs'
14
+ import type { Join, UnionToArray } from './utils/types.mjs'
15
+
16
+ import { Injectable } from './decorators/injectable.decorator.mjs'
17
+ import { InjectableScope, InjectableType } from './enums/index.mjs'
18
+ import { defaultInjectors } from './injector.mjs'
19
+ import { globalRegistry } from './registry.mjs'
20
+ import { ServiceLocator } from './service-locator.mjs'
21
+ import { getInjectableToken } from './utils/get-injectable-token.mjs'
22
+
23
+ /**
24
+ * Container class that provides a simplified public API for dependency injection.
25
+ * It wraps a ServiceLocator instance and provides convenient methods for getting instances.
26
+ */
27
+ @Injectable()
28
+ export class Container {
29
+ private readonly serviceLocator: ServiceLocator
30
+
31
+ constructor(
32
+ registry: Registry = globalRegistry,
33
+ logger: Console | null = null,
34
+ injectors: Injectors = defaultInjectors,
35
+ ) {
36
+ this.serviceLocator = new ServiceLocator(registry, logger, injectors)
37
+ this.registerSelf()
38
+ }
39
+
40
+ private registerSelf() {
41
+ const token = getInjectableToken(Container)
42
+ const instanceName = this.serviceLocator.getInstanceIdentifier(token)
43
+ this.serviceLocator
44
+ .getManager()
45
+ .storeCreatedHolder(
46
+ instanceName,
47
+ this,
48
+ InjectableType.Class,
49
+ InjectableScope.Singleton,
50
+ )
51
+ }
52
+
53
+ /**
54
+ * Gets an instance from the container.
55
+ * This method has the same type signature as the inject method from get-injectors.mts
56
+ */
57
+ // #1 Simple class
58
+ get<T extends ClassType>(
59
+ token: T,
60
+ ): InstanceType<T> extends Factorable<infer R>
61
+ ? Promise<R>
62
+ : Promise<InstanceType<T>>
63
+ // #2 Token with required Schema
64
+ get<T, S extends InjectionTokenSchemaType>(
65
+ token: InjectionToken<T, S>,
66
+ args: z.input<S>,
67
+ ): Promise<T>
68
+ // #3 Token with optional Schema
69
+ get<T, S extends InjectionTokenSchemaType, R extends boolean>(
70
+ token: InjectionToken<T, S, R>,
71
+ ): R extends false
72
+ ? Promise<T>
73
+ : S extends ZodType<infer Type>
74
+ ? `Error: Your token requires args: ${Join<
75
+ UnionToArray<keyof Type>,
76
+ ', '
77
+ >}`
78
+ : 'Error: Your token requires args'
79
+ // #4 Token with no Schema
80
+ get<T>(token: InjectionToken<T, undefined>): Promise<T>
81
+ get<T>(token: BoundInjectionToken<T, any>): Promise<T>
82
+ get<T>(token: FactoryInjectionToken<T, any>): Promise<T>
83
+
84
+ async get(
85
+ token:
86
+ | ClassType
87
+ | InjectionToken<any>
88
+ | BoundInjectionToken<any, any>
89
+ | FactoryInjectionToken<any, any>,
90
+ args?: unknown,
91
+ ) {
92
+ return this.serviceLocator.getOrThrowInstance(token, args as any)
93
+ }
94
+
95
+ /**
96
+ * Gets the underlying ServiceLocator instance for advanced usage
97
+ */
98
+ getServiceLocator(): ServiceLocator {
99
+ return this.serviceLocator
100
+ }
101
+
102
+ /**
103
+ * Invalidates a service and its dependencies
104
+ */
105
+ async invalidate(service: unknown): Promise<void> {
106
+ const holderMap = this.serviceLocator
107
+ .getManager()
108
+ .filter((holder) => holder.instance === service)
109
+ if (holderMap.size === 0) {
110
+ return
111
+ }
112
+ const holder = holderMap.values().next().value
113
+ if (holder) {
114
+ await this.serviceLocator.invalidate(holder.name)
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Waits for all pending operations to complete
120
+ */
121
+ async ready(): Promise<void> {
122
+ await this.serviceLocator.ready()
123
+ }
124
+
125
+ // ============================================================================
126
+ // REQUEST CONTEXT MANAGEMENT
127
+ // ============================================================================
128
+
129
+ /**
130
+ * Begins a new request context with the given parameters.
131
+ * @param requestId Unique identifier for this request
132
+ * @param metadata Optional metadata for the request
133
+ * @param priority Priority for resolution (higher = more priority)
134
+ * @returns The created request context holder
135
+ */
136
+ beginRequest(
137
+ requestId: string,
138
+ metadata?: Record<string, any>,
139
+ priority: number = 100,
140
+ ): RequestContextHolder {
141
+ return this.serviceLocator.beginRequest(requestId, metadata, priority)
142
+ }
143
+
144
+ /**
145
+ * Ends a request context and cleans up all associated instances.
146
+ * @param requestId The request ID to end
147
+ */
148
+ async endRequest(requestId: string): Promise<void> {
149
+ await this.serviceLocator.endRequest(requestId)
150
+ }
151
+
152
+ /**
153
+ * Gets the current request context.
154
+ * @returns The current request context holder or null
155
+ */
156
+ getCurrentRequestContext(): RequestContextHolder | null {
157
+ return this.serviceLocator.getCurrentRequestContext()
158
+ }
159
+
160
+ /**
161
+ * Sets the current request context.
162
+ * @param requestId The request ID to set as current
163
+ */
164
+ setCurrentRequestContext(requestId: string): void {
165
+ this.serviceLocator.setCurrentRequestContext(requestId)
166
+ }
167
+ }
@@ -0,0 +1,79 @@
1
+ import type {
2
+ ClassTypeWithInstance,
3
+ InjectionTokenSchemaType,
4
+ } from '../injection-token.mjs'
5
+ import type { Factorable, FactorableWithArgs } from '../interfaces/index.mjs'
6
+ import type { Registry } from '../registry.mjs'
7
+
8
+ import { InjectableScope, InjectableType } from '../enums/index.mjs'
9
+ import { InjectionToken } from '../injection-token.mjs'
10
+ import { globalRegistry } from '../registry.mjs'
11
+ import { InjectableTokenMeta } from '../symbols/index.mjs'
12
+
13
+ export interface FactoryOptions {
14
+ scope?: InjectableScope
15
+ token?: InjectionToken<any, any>
16
+ registry?: Registry
17
+ }
18
+
19
+ // #1 Factory without arguments
20
+ export function Factory<R>(options?: {
21
+ scope?: InjectableScope
22
+ registry?: Registry
23
+ }): <T extends ClassTypeWithInstance<Factorable<R>>>(
24
+ target: T,
25
+ context?: ClassDecoratorContext,
26
+ ) => T
27
+
28
+ // #2 Factory with typed token
29
+ export function Factory<R, S>(options: {
30
+ scope?: InjectableScope
31
+ token: InjectionToken<R, S>
32
+ registry?: Registry
33
+ }): R extends undefined // #2.1 Check that token has a type
34
+ ? never // #2.1.1 Token must have a type
35
+ : S extends InjectionTokenSchemaType // #2.2 Check that schema is an object or a record
36
+ ? <T extends ClassTypeWithInstance<FactorableWithArgs<R, S>>>( // #2.2.1 Token have a schema
37
+ target: T,
38
+ context?: ClassDecoratorContext,
39
+ ) => T
40
+ : S extends undefined // #2.3 For a factory without schema
41
+ ? <T extends ClassTypeWithInstance<Factorable<R>>>( // #2.3.1 Token without a schema
42
+ target: T,
43
+ context?: ClassDecoratorContext,
44
+ ) => T
45
+ : never // #2.4 Cannot use a token without a type and schema
46
+
47
+ export function Factory({
48
+ scope = InjectableScope.Singleton,
49
+ token,
50
+ registry = globalRegistry,
51
+ }: FactoryOptions = {}) {
52
+ return <
53
+ T extends ClassTypeWithInstance<
54
+ Factorable<any> | FactorableWithArgs<any, any>
55
+ >,
56
+ >(
57
+ target: T,
58
+ context?: ClassDecoratorContext,
59
+ ): T => {
60
+ if (
61
+ (context && context.kind !== 'class') ||
62
+ (target instanceof Function && !context)
63
+ ) {
64
+ throw new Error(
65
+ '[ServiceLocator] @Factory decorator can only be used on classes.',
66
+ )
67
+ }
68
+
69
+ let injectableToken: InjectionToken<any, any> =
70
+ token ?? InjectionToken.create(target)
71
+
72
+ registry.set(injectableToken, scope, target, InjectableType.Factory)
73
+
74
+ // @ts-expect-error
75
+ target[InjectableTokenMeta] = injectableToken
76
+
77
+ return target
78
+ }
79
+ }
@@ -1 +1,2 @@
1
+ export * from './factory.decorator.mjs'
1
2
  export * from './injectable.decorator.mjs'