@navios/di 0.9.0 → 0.9.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.
@@ -1,4 +1,7 @@
1
- import type { InjectionToken } from '../token/injection-token.mjs'
1
+ import type {
2
+ BoundInjectionToken,
3
+ InjectionToken,
4
+ } from '../token/injection-token.mjs'
2
5
  import type { Registry } from '../token/registry.mjs'
3
6
 
4
7
  /**
@@ -73,7 +76,10 @@ export interface BindingBuilder<T> {
73
76
  * Provider configuration for UnitTestContainer.
74
77
  */
75
78
  export interface ProviderConfig<T = any> {
76
- token: InjectionToken<T, any> | (new (...args: any[]) => T)
79
+ token:
80
+ | InjectionToken<T, any>
81
+ | BoundInjectionToken<T, any>
82
+ | (new (...args: any[]) => T)
77
83
  useValue?: T
78
84
  useClass?: new (...args: any[]) => T
79
85
  useFactory?: () => T | Promise<T>
@@ -1,14 +1,26 @@
1
- import type { LifecycleRecord, MethodCallRecord, MockServiceStats, ProviderConfig, UnitTestContainerOptions } from './types.mjs'
1
+ import type {
2
+ LifecycleRecord,
3
+ MethodCallRecord,
4
+ MockServiceStats,
5
+ ProviderConfig,
6
+ UnitTestContainerOptions,
7
+ } from './types.mjs'
2
8
 
3
9
  import { Container } from '../container/container.mjs'
4
10
  import { InjectableScope, InjectableType } from '../enums/index.mjs'
5
11
  import { DIError } from '../errors/index.mjs'
6
- import { InjectionToken } from '../token/injection-token.mjs'
12
+ import {
13
+ BoundInjectionToken,
14
+ InjectionToken,
15
+ } from '../token/injection-token.mjs'
7
16
  import { Registry } from '../token/registry.mjs'
8
17
  import { getInjectableToken } from '../utils/get-injectable-token.mjs'
9
18
  import { defaultInjectors } from '../utils/index.mjs'
10
19
 
11
- type AnyToken = InjectionToken<any, any> | (new (...args: any[]) => any)
20
+ type AnyToken =
21
+ | InjectionToken<any, any>
22
+ | BoundInjectionToken<any, any>
23
+ | (new (...args: any[]) => any)
12
24
 
13
25
  /**
14
26
  * Creates a tracking proxy that records method calls.
@@ -160,10 +172,17 @@ export class UnitTestContainer extends Container {
160
172
  * Override get to wrap instances in tracking proxies.
161
173
  */
162
174
  override async get(token: any, args?: unknown): Promise<any> {
175
+ // Check if token is a BoundInjectionToken and if it's registered
176
+ const isBoundToken = token instanceof BoundInjectionToken
177
+ const tokenId = isBoundToken ? token.id : undefined
163
178
  const realToken = this.resolveToken(token)
164
179
 
165
- // Check if this is a registered provider
166
- if (!this.registeredTokenIds.has(realToken.id)) {
180
+ // Check if this is a registered provider (check both bound token ID and real token ID)
181
+ const isRegistered =
182
+ (tokenId && this.registeredTokenIds.has(tokenId)) ||
183
+ this.registeredTokenIds.has(realToken.id)
184
+
185
+ if (!isRegistered) {
167
186
  if (!this.allowUnregistered) {
168
187
  throw DIError.factoryNotFound(
169
188
  `${realToken.toString()} is not in the providers list. ` +
@@ -172,18 +191,20 @@ export class UnitTestContainer extends Container {
172
191
  }
173
192
 
174
193
  // Auto-mock unregistered dependency
175
- if (!this.autoMockedTokenIds.has(realToken.id)) {
176
- this.autoMockedTokenIds.add(realToken.id)
194
+ const idToCheck = tokenId || realToken.id
195
+ if (!this.autoMockedTokenIds.has(idToCheck)) {
196
+ this.autoMockedTokenIds.add(idToCheck)
177
197
  }
178
198
 
179
- return createAutoMockProxy(realToken.id)
199
+ return createAutoMockProxy(idToCheck)
180
200
  }
181
201
 
182
202
  const instance = await super.get(token, args)
183
203
 
184
204
  // Wrap in tracking proxy if it's an object
205
+ const trackingId = tokenId || realToken.id
185
206
  if (instance && typeof instance === 'object') {
186
- return createTrackingProxy(instance, realToken.id, this.methodCalls)
207
+ return createTrackingProxy(instance, trackingId, this.methodCalls)
187
208
  }
188
209
 
189
210
  return instance
@@ -214,7 +235,9 @@ export class UnitTestContainer extends Container {
214
235
  const found = names.some((name) => name.includes(realToken.id))
215
236
 
216
237
  if (!found) {
217
- throw new Error(`Expected ${realToken.toString()} to be resolved, but it was not`)
238
+ throw new Error(
239
+ `Expected ${realToken.toString()} to be resolved, but it was not`,
240
+ )
218
241
  }
219
242
  }
220
243
 
@@ -228,7 +251,9 @@ export class UnitTestContainer extends Container {
228
251
  const found = names.some((name) => name.includes(realToken.id))
229
252
 
230
253
  if (found) {
231
- throw new Error(`Expected ${realToken.toString()} to NOT be resolved, but it was`)
254
+ throw new Error(
255
+ `Expected ${realToken.toString()} to NOT be resolved, but it was`,
256
+ )
232
257
  }
233
258
  }
234
259
 
@@ -266,7 +291,11 @@ export class UnitTestContainer extends Container {
266
291
  /**
267
292
  * Records a lifecycle event for tracking.
268
293
  */
269
- recordLifecycleEvent(token: AnyToken, event: 'created' | 'initialized' | 'destroyed', instanceName: string): void {
294
+ recordLifecycleEvent(
295
+ token: AnyToken,
296
+ event: 'created' | 'initialized' | 'destroyed',
297
+ instanceName: string,
298
+ ): void {
270
299
  const realToken = this.resolveToken(token)
271
300
  const events = this.lifecycleEvents.get(realToken.id) || []
272
301
  events.push({
@@ -291,7 +320,9 @@ export class UnitTestContainer extends Container {
291
320
  const initialized = events.some((e) => e.event === 'initialized')
292
321
 
293
322
  if (!initialized) {
294
- throw new Error(`Expected ${realToken.toString()} to be initialized, but onServiceInit was not called`)
323
+ throw new Error(
324
+ `Expected ${realToken.toString()} to be initialized, but onServiceInit was not called`,
325
+ )
295
326
  }
296
327
  }
297
328
 
@@ -304,7 +335,9 @@ export class UnitTestContainer extends Container {
304
335
  const destroyed = events.some((e) => e.event === 'destroyed')
305
336
 
306
337
  if (!destroyed) {
307
- throw new Error(`Expected ${realToken.toString()} to be destroyed, but onServiceDestroy was not called`)
338
+ throw new Error(
339
+ `Expected ${realToken.toString()} to be destroyed, but onServiceDestroy was not called`,
340
+ )
308
341
  }
309
342
  }
310
343
 
@@ -317,7 +350,9 @@ export class UnitTestContainer extends Container {
317
350
  const destroyed = events.some((e) => e.event === 'destroyed')
318
351
 
319
352
  if (destroyed) {
320
- throw new Error(`Expected ${realToken.toString()} to NOT be destroyed, but onServiceDestroy was called`)
353
+ throw new Error(
354
+ `Expected ${realToken.toString()} to NOT be destroyed, but onServiceDestroy was called`,
355
+ )
321
356
  }
322
357
  }
323
358
 
@@ -334,7 +369,9 @@ export class UnitTestContainer extends Container {
334
369
  const found = calls.some((c) => c.method === method)
335
370
 
336
371
  if (!found) {
337
- throw new Error(`Expected ${realToken.toString()}.${method}() to be called, but it was not`)
372
+ throw new Error(
373
+ `Expected ${realToken.toString()}.${method}() to be called, but it was not`,
374
+ )
338
375
  }
339
376
  }
340
377
 
@@ -347,14 +384,20 @@ export class UnitTestContainer extends Container {
347
384
  const found = calls.some((c) => c.method === method)
348
385
 
349
386
  if (found) {
350
- throw new Error(`Expected ${realToken.toString()}.${method}() to NOT be called, but it was`)
387
+ throw new Error(
388
+ `Expected ${realToken.toString()}.${method}() to NOT be called, but it was`,
389
+ )
351
390
  }
352
391
  }
353
392
 
354
393
  /**
355
394
  * Asserts that a method was called with specific arguments.
356
395
  */
357
- expectCalledWith(token: AnyToken, method: string, expectedArgs: unknown[]): void {
396
+ expectCalledWith(
397
+ token: AnyToken,
398
+ method: string,
399
+ expectedArgs: unknown[],
400
+ ): void {
358
401
  const realToken = this.resolveToken(token)
359
402
  const calls = this.methodCalls.get(realToken.id) || []
360
403
  const found = calls.some(
@@ -363,7 +406,9 @@ export class UnitTestContainer extends Container {
363
406
 
364
407
  if (!found) {
365
408
  const methodCalls = calls.filter((c) => c.method === method)
366
- const actualArgs = methodCalls.map((c) => JSON.stringify(c.args)).join(', ')
409
+ const actualArgs = methodCalls
410
+ .map((c) => JSON.stringify(c.args))
411
+ .join(', ')
367
412
  throw new Error(
368
413
  `Expected ${realToken.toString()}.${method}() to be called with ${JSON.stringify(expectedArgs)}. ` +
369
414
  `Actual calls: [${actualArgs}]`,
@@ -440,12 +485,21 @@ export class UnitTestContainer extends Container {
440
485
  return InjectionToken.create(token)
441
486
  }
442
487
  }
488
+ if (token instanceof BoundInjectionToken) {
489
+ return token.token
490
+ }
443
491
  return token
444
492
  }
445
493
 
446
494
  private registerProvider<T>(provider: ProviderConfig<T>): void {
447
- const realToken = this.resolveToken(provider.token as AnyToken)
495
+ const providerToken = provider.token as AnyToken
496
+ const realToken = this.resolveToken(providerToken)
497
+
498
+ // Track both the real token ID and the bound token ID if it's a bound token
448
499
  this.registeredTokenIds.add(realToken.id)
500
+ if (providerToken instanceof BoundInjectionToken) {
501
+ this.registeredTokenIds.add(providerToken.id)
502
+ }
449
503
 
450
504
  if (provider.useValue !== undefined) {
451
505
  this.registerValueBinding(realToken, provider.useValue)
@@ -456,17 +510,37 @@ export class UnitTestContainer extends Container {
456
510
  } else {
457
511
  // Just the token - register as itself
458
512
  if (typeof provider.token === 'function') {
459
- this.testRegistry.set(realToken, InjectableScope.Singleton, provider.token, InjectableType.Class)
513
+ this.testRegistry.set(
514
+ realToken,
515
+ InjectableScope.Singleton,
516
+ provider.token,
517
+ InjectableType.Class,
518
+ 1000, // Higher priority for test overrides
519
+ )
520
+ } else if (providerToken instanceof BoundInjectionToken) {
521
+ // If it's a bound token without override, register the bound value
522
+ this.registerValueBinding(realToken, providerToken.value)
460
523
  }
461
524
  }
462
525
  }
463
526
 
464
- private registerValueBinding<T>(token: InjectionToken<T, any>, value: T): void {
527
+ private registerValueBinding<T>(
528
+ token: InjectionToken<T, any>,
529
+ value: T,
530
+ ): void {
465
531
  const ValueHolder = class {
466
- static instance = value
532
+ create(): T {
533
+ return value
534
+ }
467
535
  }
468
536
 
469
- this.testRegistry.set(token, InjectableScope.Singleton, ValueHolder, InjectableType.Class)
537
+ this.testRegistry.set(
538
+ token,
539
+ InjectableScope.Singleton,
540
+ ValueHolder,
541
+ InjectableType.Factory,
542
+ 1000, // Higher priority for test overrides
543
+ )
470
544
 
471
545
  const nameResolver = this.getNameResolver()
472
546
  const instanceName = nameResolver.generateInstanceName(
@@ -479,11 +553,23 @@ export class UnitTestContainer extends Container {
479
553
  this.recordLifecycleEvent(token, 'created', instanceName)
480
554
  }
481
555
 
482
- private registerClassBinding<T>(token: InjectionToken<T, any>, cls: new (...args: any[]) => T): void {
483
- this.testRegistry.set(token, InjectableScope.Singleton, cls, InjectableType.Class)
556
+ private registerClassBinding<T>(
557
+ token: InjectionToken<T, any>,
558
+ cls: new (...args: any[]) => T,
559
+ ): void {
560
+ this.testRegistry.set(
561
+ token,
562
+ InjectableScope.Singleton,
563
+ cls,
564
+ InjectableType.Class,
565
+ 1000, // Higher priority for test overrides
566
+ )
484
567
  }
485
568
 
486
- private registerFactoryBinding<T>(token: InjectionToken<T, any>, factory: () => T | Promise<T>): void {
569
+ private registerFactoryBinding<T>(
570
+ token: InjectionToken<T, any>,
571
+ factory: () => T | Promise<T>,
572
+ ): void {
487
573
  const FactoryWrapper = class {
488
574
  static factory = factory
489
575
  async create(): Promise<T> {
@@ -491,7 +577,13 @@ export class UnitTestContainer extends Container {
491
577
  }
492
578
  }
493
579
 
494
- this.testRegistry.set(token, InjectableScope.Singleton, FactoryWrapper, InjectableType.Factory)
580
+ this.testRegistry.set(
581
+ token,
582
+ InjectableScope.Singleton,
583
+ FactoryWrapper,
584
+ InjectableType.Factory,
585
+ 1000, // Higher priority for test overrides
586
+ )
495
587
  }
496
588
 
497
589
  private argsMatch(actual: unknown[], expected: unknown[]): boolean {