@navios/di 0.8.0 → 0.9.0
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.
- package/CHANGELOG.md +87 -0
- package/README.md +117 -17
- package/lib/browser/container/abstract-container.d.mts +112 -0
- package/lib/browser/container/abstract-container.d.mts.map +1 -0
- package/lib/browser/container/abstract-container.mjs +100 -0
- package/lib/browser/container/abstract-container.mjs.map +1 -0
- package/lib/browser/container/container.d.mts +100 -0
- package/lib/browser/container/container.d.mts.map +1 -0
- package/lib/browser/container/container.mjs +424 -0
- package/lib/browser/container/container.mjs.map +1 -0
- package/lib/browser/container/scoped-container.d.mts +93 -0
- package/lib/browser/container/scoped-container.d.mts.map +1 -0
- package/lib/browser/container/scoped-container.mjs +119 -0
- package/lib/browser/container/scoped-container.mjs.map +1 -0
- package/lib/browser/decorators/factory.decorator.d.mts +26 -0
- package/lib/browser/decorators/factory.decorator.d.mts.map +1 -0
- package/lib/browser/decorators/factory.decorator.mjs +20 -0
- package/lib/browser/decorators/factory.decorator.mjs.map +1 -0
- package/lib/browser/decorators/injectable.decorator.d.mts +38 -0
- package/lib/browser/decorators/injectable.decorator.d.mts.map +1 -0
- package/lib/browser/decorators/injectable.decorator.mjs +21 -0
- package/lib/browser/decorators/injectable.decorator.mjs.map +1 -0
- package/lib/browser/enums/injectable-scope.enum.d.mts +18 -0
- package/lib/browser/enums/injectable-scope.enum.d.mts.map +1 -0
- package/lib/browser/enums/injectable-scope.enum.mjs +20 -0
- package/lib/browser/enums/injectable-scope.enum.mjs.map +1 -0
- package/lib/browser/enums/injectable-type.enum.d.mts +8 -0
- package/lib/browser/enums/injectable-type.enum.d.mts.map +1 -0
- package/lib/browser/enums/injectable-type.enum.mjs +10 -0
- package/lib/browser/enums/injectable-type.enum.mjs.map +1 -0
- package/lib/browser/errors/di-error.d.mts +43 -0
- package/lib/browser/errors/di-error.d.mts.map +1 -0
- package/lib/browser/errors/di-error.mjs +98 -0
- package/lib/browser/errors/di-error.mjs.map +1 -0
- package/lib/browser/event-emitter.d.mts +16 -0
- package/lib/browser/event-emitter.d.mts.map +1 -0
- package/lib/browser/event-emitter.mjs +320 -0
- package/lib/browser/event-emitter.mjs.map +1 -0
- package/lib/browser/index.d.mts +37 -1558
- package/lib/browser/index.mjs +29 -2749
- package/lib/browser/interfaces/container.interface.d.mts +59 -0
- package/lib/browser/interfaces/container.interface.d.mts.map +1 -0
- package/lib/browser/interfaces/factory.interface.d.mts +14 -0
- package/lib/browser/interfaces/factory.interface.d.mts.map +1 -0
- package/lib/browser/interfaces/on-service-destroy.interface.d.mts +7 -0
- package/lib/browser/interfaces/on-service-destroy.interface.d.mts.map +1 -0
- package/lib/browser/interfaces/on-service-init.interface.d.mts +7 -0
- package/lib/browser/interfaces/on-service-init.interface.d.mts.map +1 -0
- package/lib/browser/internal/context/async-local-storage.browser.mjs +20 -0
- package/lib/browser/internal/context/async-local-storage.browser.mjs.map +1 -0
- package/lib/browser/internal/context/async-local-storage.d.mts +9 -0
- package/lib/browser/internal/context/async-local-storage.d.mts.map +1 -0
- package/lib/browser/internal/context/async-local-storage.types.d.mts +11 -0
- package/lib/browser/internal/context/async-local-storage.types.d.mts.map +1 -0
- package/lib/browser/internal/context/factory-context.d.mts +23 -0
- package/lib/browser/internal/context/factory-context.d.mts.map +1 -0
- package/lib/browser/internal/context/resolution-context.d.mts +43 -0
- package/lib/browser/internal/context/resolution-context.d.mts.map +1 -0
- package/lib/browser/internal/context/resolution-context.mjs +56 -0
- package/lib/browser/internal/context/resolution-context.mjs.map +1 -0
- package/lib/browser/internal/context/service-initialization-context.d.mts +48 -0
- package/lib/browser/internal/context/service-initialization-context.d.mts.map +1 -0
- package/lib/browser/internal/context/sync-local-storage.mjs +53 -0
- package/lib/browser/internal/context/sync-local-storage.mjs.map +1 -0
- package/lib/browser/internal/core/instance-resolver.d.mts +119 -0
- package/lib/browser/internal/core/instance-resolver.d.mts.map +1 -0
- package/lib/browser/internal/core/instance-resolver.mjs +306 -0
- package/lib/browser/internal/core/instance-resolver.mjs.map +1 -0
- package/lib/browser/internal/core/name-resolver.d.mts +52 -0
- package/lib/browser/internal/core/name-resolver.d.mts.map +1 -0
- package/lib/browser/internal/core/name-resolver.mjs +118 -0
- package/lib/browser/internal/core/name-resolver.mjs.map +1 -0
- package/lib/browser/internal/core/scope-tracker.d.mts +65 -0
- package/lib/browser/internal/core/scope-tracker.d.mts.map +1 -0
- package/lib/browser/internal/core/scope-tracker.mjs +120 -0
- package/lib/browser/internal/core/scope-tracker.mjs.map +1 -0
- package/lib/browser/internal/core/service-initializer.d.mts +44 -0
- package/lib/browser/internal/core/service-initializer.d.mts.map +1 -0
- package/lib/browser/internal/core/service-initializer.mjs +109 -0
- package/lib/browser/internal/core/service-initializer.mjs.map +1 -0
- package/lib/browser/internal/core/service-invalidator.d.mts +81 -0
- package/lib/browser/internal/core/service-invalidator.d.mts.map +1 -0
- package/lib/browser/internal/core/service-invalidator.mjs +142 -0
- package/lib/browser/internal/core/service-invalidator.mjs.map +1 -0
- package/lib/browser/internal/core/token-resolver.d.mts +54 -0
- package/lib/browser/internal/core/token-resolver.d.mts.map +1 -0
- package/lib/browser/internal/core/token-resolver.mjs +77 -0
- package/lib/browser/internal/core/token-resolver.mjs.map +1 -0
- package/lib/browser/internal/holder/holder-storage.interface.d.mts +99 -0
- package/lib/browser/internal/holder/holder-storage.interface.d.mts.map +1 -0
- package/lib/browser/internal/holder/instance-holder.d.mts +101 -0
- package/lib/browser/internal/holder/instance-holder.d.mts.map +1 -0
- package/lib/browser/internal/holder/instance-holder.mjs +19 -0
- package/lib/browser/internal/holder/instance-holder.mjs.map +1 -0
- package/lib/browser/internal/holder/unified-storage.d.mts +53 -0
- package/lib/browser/internal/holder/unified-storage.d.mts.map +1 -0
- package/lib/browser/internal/holder/unified-storage.mjs +144 -0
- package/lib/browser/internal/holder/unified-storage.mjs.map +1 -0
- package/lib/browser/internal/lifecycle/circular-detector.d.mts +39 -0
- package/lib/browser/internal/lifecycle/circular-detector.d.mts.map +1 -0
- package/lib/browser/internal/lifecycle/circular-detector.mjs +55 -0
- package/lib/browser/internal/lifecycle/circular-detector.mjs.map +1 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts +18 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.d.mts.map +1 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs +43 -0
- package/lib/browser/internal/lifecycle/lifecycle-event-bus.mjs.map +1 -0
- package/lib/browser/internal/stub-factory-class.d.mts +14 -0
- package/lib/browser/internal/stub-factory-class.d.mts.map +1 -0
- package/lib/browser/internal/stub-factory-class.mjs +18 -0
- package/lib/browser/internal/stub-factory-class.mjs.map +1 -0
- package/lib/browser/symbols/injectable-token.d.mts +5 -0
- package/lib/browser/symbols/injectable-token.d.mts.map +1 -0
- package/lib/browser/symbols/injectable-token.mjs +6 -0
- package/lib/browser/symbols/injectable-token.mjs.map +1 -0
- package/lib/browser/token/injection-token.d.mts +55 -0
- package/lib/browser/token/injection-token.d.mts.map +1 -0
- package/lib/browser/token/injection-token.mjs +100 -0
- package/lib/browser/token/injection-token.mjs.map +1 -0
- package/lib/browser/token/registry.d.mts +37 -0
- package/lib/browser/token/registry.d.mts.map +1 -0
- package/lib/browser/token/registry.mjs +86 -0
- package/lib/browser/token/registry.mjs.map +1 -0
- package/lib/browser/utils/default-injectors.d.mts +12 -0
- package/lib/browser/utils/default-injectors.d.mts.map +1 -0
- package/lib/browser/utils/default-injectors.mjs +13 -0
- package/lib/browser/utils/default-injectors.mjs.map +1 -0
- package/lib/browser/utils/get-injectable-token.d.mts +9 -0
- package/lib/browser/utils/get-injectable-token.d.mts.map +1 -0
- package/lib/browser/utils/get-injectable-token.mjs +13 -0
- package/lib/browser/utils/get-injectable-token.mjs.map +1 -0
- package/lib/browser/utils/get-injectors.d.mts +55 -0
- package/lib/browser/utils/get-injectors.d.mts.map +1 -0
- package/lib/browser/utils/get-injectors.mjs +121 -0
- package/lib/browser/utils/get-injectors.mjs.map +1 -0
- package/lib/browser/utils/types.d.mts +23 -0
- package/lib/browser/utils/types.d.mts.map +1 -0
- package/lib/{container-DAKOvAgr.mjs → container-8-z89TyQ.mjs} +1325 -1462
- package/lib/container-8-z89TyQ.mjs.map +1 -0
- package/lib/{container-Bp1W-pWJ.d.mts → container-CNiqesCL.d.mts} +598 -617
- package/lib/container-CNiqesCL.d.mts.map +1 -0
- package/lib/{container-DENMeJ87.cjs → container-CaY2fDuk.cjs} +1369 -1512
- package/lib/container-CaY2fDuk.cjs.map +1 -0
- package/lib/{container-YPwvmlK2.d.cts → container-D-0Ho3qL.d.cts} +598 -612
- package/lib/container-D-0Ho3qL.d.cts.map +1 -0
- package/lib/index.cjs +13 -15
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +58 -223
- package/lib/index.d.cts.map +1 -1
- package/lib/index.d.mts +62 -222
- package/lib/index.d.mts.map +1 -1
- package/lib/index.mjs +5 -6
- package/lib/index.mjs.map +1 -1
- package/lib/testing/index.cjs +569 -311
- package/lib/testing/index.cjs.map +1 -1
- package/lib/testing/index.d.cts +370 -41
- package/lib/testing/index.d.cts.map +1 -1
- package/lib/testing/index.d.mts +370 -41
- package/lib/testing/index.d.mts.map +1 -1
- package/lib/testing/index.mjs +568 -305
- package/lib/testing/index.mjs.map +1 -1
- package/package.json +2 -1
- package/src/__tests__/circular-detector.spec.mts +193 -0
- package/src/__tests__/concurrent.spec.mts +368 -0
- package/src/__tests__/container.spec.mts +32 -30
- package/src/__tests__/di-error.spec.mts +351 -0
- package/src/__tests__/e2e.browser.spec.mts +0 -4
- package/src/__tests__/e2e.spec.mts +10 -19
- package/src/__tests__/event-emitter.spec.mts +232 -109
- package/src/__tests__/get-injectors.spec.mts +250 -39
- package/src/__tests__/injection-token.spec.mts +293 -349
- package/src/__tests__/library-findings.spec.mts +8 -8
- package/src/__tests__/registry.spec.mts +358 -210
- package/src/__tests__/resolution-context.spec.mts +255 -0
- package/src/__tests__/scope-tracker.spec.mts +598 -0
- package/src/__tests__/scope-upgrade.spec.mts +808 -0
- package/src/__tests__/scoped-container.spec.mts +595 -0
- package/src/__tests__/test-container.spec.mts +293 -0
- package/src/__tests__/token-resolver.spec.mts +207 -0
- package/src/__tests__/unified-storage.spec.mts +535 -0
- package/src/__tests__/unit-test-container.spec.mts +405 -0
- package/src/__type-tests__/container.spec-d.mts +180 -0
- package/src/__type-tests__/factory.spec-d.mts +15 -3
- package/src/__type-tests__/inject.spec-d.mts +115 -20
- package/src/__type-tests__/injectable.spec-d.mts +69 -52
- package/src/__type-tests__/injection-token.spec-d.mts +176 -0
- package/src/__type-tests__/scoped-container.spec-d.mts +212 -0
- package/src/container/abstract-container.mts +327 -0
- package/src/container/container.mts +142 -170
- package/src/container/scoped-container.mts +126 -208
- package/src/decorators/factory.decorator.mts +16 -11
- package/src/decorators/injectable.decorator.mts +20 -16
- package/src/enums/index.mts +2 -2
- package/src/enums/injectable-scope.enum.mts +1 -0
- package/src/enums/injectable-type.enum.mts +1 -0
- package/src/errors/di-error.mts +96 -0
- package/src/event-emitter.mts +3 -27
- package/src/index.mts +6 -153
- package/src/interfaces/container.interface.mts +13 -0
- package/src/interfaces/factory.interface.mts +1 -1
- package/src/interfaces/index.mts +1 -1
- package/src/internal/context/async-local-storage.mts +3 -2
- package/src/internal/context/async-local-storage.types.mts +1 -0
- package/src/internal/context/factory-context.mts +1 -0
- package/src/internal/context/index.mts +3 -1
- package/src/internal/context/resolution-context.mts +1 -0
- package/src/internal/context/service-initialization-context.mts +43 -0
- package/src/internal/core/index.mts +5 -4
- package/src/internal/core/instance-resolver.mts +460 -302
- package/src/internal/core/name-resolver.mts +196 -0
- package/src/internal/core/scope-tracker.mts +242 -0
- package/src/internal/core/{instantiator.mts → service-initializer.mts} +51 -29
- package/src/internal/core/service-invalidator.mts +290 -0
- package/src/internal/core/token-resolver.mts +122 -0
- package/src/internal/holder/holder-storage.interface.mts +11 -5
- package/src/internal/holder/index.mts +2 -5
- package/src/internal/holder/instance-holder.mts +1 -3
- package/src/internal/holder/unified-storage.mts +245 -0
- package/src/internal/index.mts +2 -1
- package/src/internal/lifecycle/circular-detector.mts +1 -0
- package/src/internal/lifecycle/index.mts +1 -1
- package/src/internal/lifecycle/lifecycle-event-bus.mts +1 -0
- package/src/internal/stub-factory-class.mts +16 -0
- package/src/symbols/injectable-token.mts +3 -1
- package/src/testing/index.mts +2 -0
- package/src/testing/test-container.mts +546 -85
- package/src/testing/types.mts +117 -0
- package/src/testing/unit-test-container.mts +509 -0
- package/src/token/injection-token.mts +41 -4
- package/src/token/registry.mts +75 -9
- package/src/utils/default-injectors.mts +16 -0
- package/src/utils/get-injectable-token.mts +2 -3
- package/src/utils/get-injectors.mts +26 -15
- package/src/utils/index.mts +3 -1
- package/src/utils/types.mts +1 -0
- package/tsdown.config.mts +11 -1
- package/lib/browser/index.d.mts.map +0 -1
- package/lib/browser/index.mjs.map +0 -1
- package/lib/container-Bp1W-pWJ.d.mts.map +0 -1
- package/lib/container-DAKOvAgr.mjs.map +0 -1
- package/lib/container-DENMeJ87.cjs.map +0 -1
- package/lib/container-YPwvmlK2.d.cts.map +0 -1
- package/src/__tests__/async-local-storage.browser.spec.mts +0 -166
- package/src/__tests__/async-local-storage.spec.mts +0 -333
- package/src/__tests__/errors.spec.mts +0 -87
- package/src/__tests__/factory.spec.mts +0 -137
- package/src/__tests__/injectable.spec.mts +0 -246
- package/src/__tests__/request-scope.spec.mts +0 -416
- package/src/__tests__/service-instantiator.spec.mts +0 -410
- package/src/__tests__/service-locator-event-bus.spec.mts +0 -242
- package/src/__tests__/service-locator-manager.spec.mts +0 -300
- package/src/__tests__/service-locator.spec.mts +0 -966
- package/src/__tests__/unified-api.spec.mts +0 -130
- package/src/browser.mts +0 -11
- package/src/injectors.mts +0 -18
- package/src/internal/context/request-context.mts +0 -225
- package/src/internal/core/invalidator.mts +0 -437
- package/src/internal/core/service-locator.mts +0 -202
- package/src/internal/core/token-processor.mts +0 -252
- package/src/internal/holder/base-holder-manager.mts +0 -334
- package/src/internal/holder/holder-manager.mts +0 -85
- package/src/internal/holder/request-storage.mts +0 -127
- package/src/internal/holder/singleton-storage.mts +0 -92
- package/src/testing/README.md +0 -80
- package/src/testing/__tests__/test-container.spec.mts +0 -173
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,93 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.9.0] - 2025-12-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Priority System for Multiple Registrations**: Services can now be registered with priority levels. Higher priority wins when multiple registrations exist for the same token
|
|
13
|
+
- New `priority` option in `@Injectable()` decorator
|
|
14
|
+
- `Registry.getAll(token)` method to retrieve all registrations sorted by priority
|
|
15
|
+
- `FactoryRecord` now includes `priority` field
|
|
16
|
+
- **Unified Storage Architecture**: Replaced multiple storage classes with a single `UnifiedStorage` class
|
|
17
|
+
- Replaces `HolderManager`, `SingletonStorage`, `RequestStorage`, and `RequestContext`
|
|
18
|
+
- Simpler mental model: one storage class regardless of scope
|
|
19
|
+
- Reverse dependency index for O(1) dependent lookups (vs O(n) iteration)
|
|
20
|
+
- Consistent API across all scopes
|
|
21
|
+
- **Enhanced Testing Utilities**: Significantly extended `TestContainer` with new assertion helpers
|
|
22
|
+
- Fluent binding API extended with `toFactory()` method
|
|
23
|
+
- Assertion helpers: `expectResolved()`, `expectNotResolved()`, `expectSingleton()`, `expectTransient()`, `expectRequestScoped()`
|
|
24
|
+
- Lifecycle assertions: `expectInitialized()`, `expectDestroyed()`, `expectNotDestroyed()`
|
|
25
|
+
- Method call tracking: `recordMethodCall()`, `expectCalled()`, `expectCalledWith()`, `expectCallCount()`, `getMethodCalls()`, `getServiceStats()`
|
|
26
|
+
- Dependency graph utilities: `getDependencyGraph()`, `getSimplifiedDependencyGraph()`
|
|
27
|
+
- **New UnitTestContainer**: Strict isolated unit testing container with auto-tracking
|
|
28
|
+
- Automatic method call tracking via Proxy
|
|
29
|
+
- Strict mode (default): unregistered dependencies throw errors
|
|
30
|
+
- Auto-mocking mode: unregistered dependencies return mock proxies
|
|
31
|
+
- Simplified provider-based configuration
|
|
32
|
+
- **Enhanced Error Messages**: Extended `DIErrorCode` enum with 8 new error types
|
|
33
|
+
- `TokenValidationError`: Zod schema validation failed
|
|
34
|
+
- `TokenSchemaRequiredError`: Schema args required but not provided
|
|
35
|
+
- `ClassNotInjectable`: Missing `@Injectable` decorator
|
|
36
|
+
- `ScopeMismatchError`: Wrong container for scope
|
|
37
|
+
- `PriorityConflictError`: Multiple same-priority registrations
|
|
38
|
+
- `StorageError`: Storage operation failed
|
|
39
|
+
- `InitializationError`: Service init failed
|
|
40
|
+
- `DependencyResolutionError`: Dependency chain error
|
|
41
|
+
- **New Internal Components**:
|
|
42
|
+
- `NameResolver`: Generates deterministic instance names
|
|
43
|
+
- `ScopeTracker`: Tracks and validates scope relationships
|
|
44
|
+
- `AbstractContainer`: Base class for containers
|
|
45
|
+
- **New Container Methods**: Direct access to internal components
|
|
46
|
+
- `getStorage()`: Returns `UnifiedStorage`
|
|
47
|
+
- `getServiceInitializer()`: Returns `ServiceInitializer`
|
|
48
|
+
- `getServiceInvalidator()`: Returns `ServiceInvalidator`
|
|
49
|
+
- `getTokenResolver()`: Returns `TokenResolver`
|
|
50
|
+
- `getNameResolver()`: Returns `NameResolver`
|
|
51
|
+
- `getScopeTracker()`: Returns `ScopeTracker`
|
|
52
|
+
- `getEventBus()`: Returns `LifecycleEventBus`
|
|
53
|
+
- `getInstanceResolver()`: Returns `InstanceResolver`
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- **BREAKING**: `ServiceLocator` wrapper class removed
|
|
58
|
+
- Container now uses components directly (no ServiceLocator wrapper)
|
|
59
|
+
- `container.getServiceLocator()` method removed
|
|
60
|
+
- Use direct component access methods instead (e.g., `container.getStorage()`)
|
|
61
|
+
- **BREAKING**: Internal component renames
|
|
62
|
+
- `Instantiator` → `ServiceInitializer`
|
|
63
|
+
- `Invalidator` → `ServiceInvalidator`
|
|
64
|
+
- `TokenProcessor` → `TokenResolver`
|
|
65
|
+
- `HolderManager` → `UnifiedStorage`
|
|
66
|
+
- `SingletonStorage` → `UnifiedStorage` (same class, different instance)
|
|
67
|
+
- `RequestStorage` → `UnifiedStorage` (same class, different instance)
|
|
68
|
+
- `RequestContext` → merged into `UnifiedStorage`
|
|
69
|
+
- **BREAKING**: `Container.removeActiveRequest()` renamed to `Container.removeRequestId()`
|
|
70
|
+
- **Registry Changes**:
|
|
71
|
+
- `Registry.set()` now accepts optional `priority` parameter (5 params instead of 4)
|
|
72
|
+
- `Registry` stores `FactoryRecord[]` per token instead of single `FactoryRecord`
|
|
73
|
+
- `FactoryRecord` interface extended with `priority: number` field
|
|
74
|
+
- **Scope Error Handling**: Request scope violations now throw dedicated `ScopeMismatchError` with structured context instead of generic errors
|
|
75
|
+
- **Build Configuration**: Updated tsdown config with external dependencies
|
|
76
|
+
- Node build: external `node:async_hooks` and `zod`
|
|
77
|
+
- Browser build: external `zod`
|
|
78
|
+
|
|
79
|
+
### Performance
|
|
80
|
+
|
|
81
|
+
- **Reverse Dependency Index**: O(1) dependent lookup during invalidation (vs O(n) iteration)
|
|
82
|
+
- **Priority Cache**: Registry maintains `highestPriority` Map for fast access to winning registration
|
|
83
|
+
- **Unified Storage**: Single storage class eliminates wrapper overhead
|
|
84
|
+
- **No ServiceLocator Wrapper**: Container directly uses components, reducing indirection
|
|
85
|
+
|
|
86
|
+
### Migration
|
|
87
|
+
|
|
88
|
+
- Replace `container.getServiceLocator()` with direct component access methods
|
|
89
|
+
- Replace `container.removeActiveRequest()` with `container.removeRequestId()`
|
|
90
|
+
- Update internal class references if you were using them directly
|
|
91
|
+
- Consider using new testing utilities (`TestContainer` assertions, `UnitTestContainer`)
|
|
92
|
+
- Use `priority` option for service registration if needed
|
|
93
|
+
- Use `registry.getAll(token)` if you need all registrations, not just highest priority
|
|
94
|
+
|
|
8
95
|
## [0.8.0] - 2025-12-21
|
|
9
96
|
|
|
10
97
|
### Added
|
package/README.md
CHANGED
|
@@ -10,10 +10,12 @@ A powerful, type-safe dependency injection library for TypeScript applications.
|
|
|
10
10
|
- **Factory Pattern**: Create instances using factory classes
|
|
11
11
|
- **Injection Tokens**: Flexible token-based dependency resolution
|
|
12
12
|
- **Scoped Instances**: Singleton, transient, and request scopes
|
|
13
|
+
- **Priority System**: Register multiple services per token with priority levels
|
|
13
14
|
- **Async/Sync Injection**: Both synchronous and asynchronous dependency resolution
|
|
14
15
|
- **Container API**: Simple container-based API for dependency management
|
|
15
16
|
- **Request Context**: Manage request-scoped services with automatic cleanup via ScopedContainer
|
|
16
17
|
- **Circular Dependency Detection**: Automatic detection and helpful error messages for circular dependencies
|
|
18
|
+
- **Enhanced Testing**: Comprehensive test utilities with assertion helpers and auto-tracking
|
|
17
19
|
|
|
18
20
|
## Installation
|
|
19
21
|
|
|
@@ -132,6 +134,13 @@ class RequestService {}
|
|
|
132
134
|
@Injectable({ token: MyToken })
|
|
133
135
|
class TokenizedService {}
|
|
134
136
|
|
|
137
|
+
// With priority (higher priority wins when multiple registrations exist)
|
|
138
|
+
@Injectable({ priority: 100 })
|
|
139
|
+
class DefaultService {}
|
|
140
|
+
|
|
141
|
+
@Injectable({ priority: 200 }) // This wins
|
|
142
|
+
class OverrideService {}
|
|
143
|
+
|
|
135
144
|
// With schema for constructor arguments
|
|
136
145
|
const configSchema = z.object({
|
|
137
146
|
host: z.string(),
|
|
@@ -426,6 +435,9 @@ import { Container, Registry } from '@navios/di'
|
|
|
426
435
|
|
|
427
436
|
const customRegistry = new Registry()
|
|
428
437
|
const container = new Container(customRegistry)
|
|
438
|
+
|
|
439
|
+
// Get all registrations for a token (sorted by priority, highest first)
|
|
440
|
+
const allRegistrations = customRegistry.getAll(MyToken)
|
|
429
441
|
```
|
|
430
442
|
|
|
431
443
|
### Error Handling
|
|
@@ -444,6 +456,13 @@ try {
|
|
|
444
456
|
case DIErrorCode.InstanceDestroying:
|
|
445
457
|
console.error('Service is being destroyed')
|
|
446
458
|
break
|
|
459
|
+
case DIErrorCode.ScopeMismatchError:
|
|
460
|
+
console.error('Wrong container for scope')
|
|
461
|
+
break
|
|
462
|
+
case DIErrorCode.TokenValidationError:
|
|
463
|
+
console.error('Token validation failed')
|
|
464
|
+
break
|
|
465
|
+
// ... and more error codes
|
|
447
466
|
}
|
|
448
467
|
}
|
|
449
468
|
}
|
|
@@ -469,11 +488,21 @@ const newService = await container.get(MyService)
|
|
|
469
488
|
- `dispose(): Promise<void>` - Clean up all resources
|
|
470
489
|
- `clear(): Promise<void>` - Clear all instances and bindings
|
|
471
490
|
- `isRegistered(token: any): boolean` - Check if service is registered
|
|
472
|
-
- `
|
|
491
|
+
- `calculateInstanceName(token, args?): string | null` - Calculate the instance name for a token (returns null for unresolved factory tokens or validation errors)
|
|
473
492
|
- `getRegistry(): Registry` - Get the registry
|
|
474
493
|
- `beginRequest(requestId: string, metadata?, priority?): ScopedContainer` - Begin request context
|
|
475
494
|
- `getActiveRequestIds(): ReadonlySet<string>` - Get active request IDs
|
|
476
495
|
- `hasActiveRequest(requestId: string): boolean` - Check if request is active
|
|
496
|
+
- `removeRequestId(requestId: string): void` - Remove a request ID from tracking
|
|
497
|
+
- **Component Access Methods** (for advanced usage):
|
|
498
|
+
- `getStorage(): UnifiedStorage` - Get storage instance
|
|
499
|
+
- `getServiceInitializer(): ServiceInitializer` - Get service initializer
|
|
500
|
+
- `getServiceInvalidator(): ServiceInvalidator` - Get service invalidator
|
|
501
|
+
- `getTokenResolver(): TokenResolver` - Get token resolver
|
|
502
|
+
- `getNameResolver(): NameResolver` - Get name resolver
|
|
503
|
+
- `getScopeTracker(): ScopeTracker` - Get scope tracker
|
|
504
|
+
- `getEventBus(): LifecycleEventBus` - Get event bus
|
|
505
|
+
- `getInstanceResolver(): InstanceResolver` - Get instance resolver
|
|
477
506
|
|
|
478
507
|
### ScopedContainer
|
|
479
508
|
|
|
@@ -483,12 +512,12 @@ const newService = await container.get(MyService)
|
|
|
483
512
|
- `dispose(): Promise<void>` - Alias for endRequest()
|
|
484
513
|
- `ready(): Promise<void>` - Wait for pending operations
|
|
485
514
|
- `isRegistered(token: any): boolean` - Check if service is registered
|
|
515
|
+
- `calculateInstanceName(token, args?): string | null` - Calculate the instance name for a token (returns null for unresolved factory tokens or validation errors)
|
|
486
516
|
- `getMetadata(key: string): any` - Get request metadata
|
|
487
517
|
- `setMetadata(key: string, value: any): void` - Set request metadata
|
|
488
|
-
- `addInstance(token: InjectionToken, instance: any): void` - Add pre-prepared instance
|
|
489
518
|
- `getRequestId(): string` - Get the request ID
|
|
490
519
|
- `getParent(): Container` - Get the parent container
|
|
491
|
-
- `
|
|
520
|
+
- `getStorage(): UnifiedStorage` - Get the underlying storage instance
|
|
492
521
|
|
|
493
522
|
### Injectable Decorator
|
|
494
523
|
|
|
@@ -498,6 +527,7 @@ const newService = await container.get(MyService)
|
|
|
498
527
|
- `token?: InjectionToken` - Custom injection token
|
|
499
528
|
- `schema?: ZodSchema` - Zod schema for constructor arguments
|
|
500
529
|
- `registry?: Registry` - Custom registry
|
|
530
|
+
- `priority?: number` - Priority level (higher wins when multiple registrations exist, default: 0)
|
|
501
531
|
- Note: Cannot use both `token` and `schema` options together
|
|
502
532
|
|
|
503
533
|
### Factory Decorator
|
|
@@ -528,24 +558,21 @@ const newService = await container.get(MyService)
|
|
|
528
558
|
- `OnServiceInit` - Implement `onServiceInit(): Promise<void> | void`
|
|
529
559
|
- `OnServiceDestroy` - Implement `onServiceDestroy(): Promise<void> | void`
|
|
530
560
|
|
|
531
|
-
###
|
|
561
|
+
### Registry
|
|
532
562
|
|
|
533
|
-
- `
|
|
534
|
-
- `
|
|
535
|
-
- `
|
|
536
|
-
- `
|
|
537
|
-
- `
|
|
538
|
-
- `
|
|
539
|
-
- `set(instanceName, holder): void` - Set instance holder
|
|
540
|
-
- `has(instanceName): boolean` - Check if instance exists
|
|
541
|
-
- `clear(): void` - Clear all instances
|
|
542
|
-
- `getMetadata(key): any` - Get metadata value
|
|
543
|
-
- `setMetadata(key, value): void` - Set metadata value
|
|
563
|
+
- `set(token, scope, target, type, priority?): void` - Register a service factory
|
|
564
|
+
- `get(token): FactoryRecord` - Get the highest priority factory record for a token
|
|
565
|
+
- `getAll(token): FactoryRecord[]` - Get all factory records for a token (sorted by priority, highest first)
|
|
566
|
+
- `has(token): boolean` - Check if a token is registered
|
|
567
|
+
- `delete(token): void` - Remove all registrations for a token
|
|
568
|
+
- `updateScope(token, scope): boolean` - Update the scope of an already registered factory
|
|
544
569
|
|
|
545
570
|
## Testing
|
|
546
571
|
|
|
547
572
|
### TestContainer
|
|
548
573
|
|
|
574
|
+
`TestContainer` extends `Container` with enhanced testing utilities, including fluent binding API, assertion helpers, method call tracking, and dependency graph inspection.
|
|
575
|
+
|
|
549
576
|
```typescript
|
|
550
577
|
import { TestContainer } from '@navios/di/testing'
|
|
551
578
|
|
|
@@ -557,19 +584,89 @@ describe('UserService', () => {
|
|
|
557
584
|
})
|
|
558
585
|
|
|
559
586
|
afterEach(async () => {
|
|
560
|
-
await container.
|
|
587
|
+
await container.clear()
|
|
561
588
|
})
|
|
562
589
|
|
|
563
590
|
it('should create user', async () => {
|
|
564
|
-
//
|
|
591
|
+
// Fluent binding API
|
|
565
592
|
container.bind(DatabaseService).toValue({
|
|
566
593
|
save: vi.fn().mockResolvedValue({ id: '1' }),
|
|
567
594
|
})
|
|
595
|
+
|
|
596
|
+
// Or bind to class
|
|
597
|
+
container.bind(UserService).toClass(MockUserService)
|
|
598
|
+
|
|
599
|
+
// Or bind to factory
|
|
600
|
+
container.bind(ConfigToken).toFactory(() => ({ apiKey: 'test' }))
|
|
568
601
|
|
|
569
602
|
const userService = await container.get(UserService)
|
|
570
603
|
const user = await userService.create({ name: 'John' })
|
|
571
604
|
|
|
572
605
|
expect(user.id).toBe('1')
|
|
606
|
+
|
|
607
|
+
// Assertion helpers
|
|
608
|
+
container.expectResolved(UserService)
|
|
609
|
+
container.expectSingleton(UserService)
|
|
610
|
+
container.expectInitialized(UserService)
|
|
611
|
+
|
|
612
|
+
// Method call tracking
|
|
613
|
+
container.recordMethodCall(UserService, 'create', [{ name: 'John' }], user)
|
|
614
|
+
container.expectCalled(UserService, 'create')
|
|
615
|
+
container.expectCalledWith(UserService, 'create', [{ name: 'John' }])
|
|
616
|
+
container.expectCallCount(UserService, 'create', 1)
|
|
617
|
+
|
|
618
|
+
// Dependency graph inspection
|
|
619
|
+
const graph = container.getDependencyGraph()
|
|
620
|
+
console.log(graph)
|
|
621
|
+
})
|
|
622
|
+
})
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### UnitTestContainer
|
|
626
|
+
|
|
627
|
+
`UnitTestContainer` provides strict isolated unit testing with automatic method call tracking via Proxy. Only services explicitly provided can be resolved.
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
import { UnitTestContainer } from '@navios/di/testing'
|
|
631
|
+
|
|
632
|
+
describe('UserService Unit Tests', () => {
|
|
633
|
+
let container: UnitTestContainer
|
|
634
|
+
|
|
635
|
+
beforeEach(() => {
|
|
636
|
+
container = new UnitTestContainer({
|
|
637
|
+
providers: [
|
|
638
|
+
{ token: UserService, useClass: MockUserService },
|
|
639
|
+
{ token: ConfigToken, useValue: { apiUrl: 'test' } },
|
|
640
|
+
{ token: ApiClient, useFactory: () => new MockApiClient() },
|
|
641
|
+
],
|
|
642
|
+
})
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
afterEach(async () => {
|
|
646
|
+
await container.dispose()
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
it('should track method calls automatically', async () => {
|
|
650
|
+
const service = await container.get(UserService)
|
|
651
|
+
await service.findUser('123')
|
|
652
|
+
|
|
653
|
+
// Auto-tracked assertions (no manual recording needed)
|
|
654
|
+
container.expectCalled(UserService, 'findUser')
|
|
655
|
+
container.expectCalledWith(UserService, 'findUser', ['123'])
|
|
656
|
+
container.expectNotCalled(UserService, 'deleteUser')
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
it('should throw on unregistered dependencies (strict mode)', async () => {
|
|
660
|
+
// Strict mode (default): unregistered dependencies throw
|
|
661
|
+
await expect(container.get(UnregisteredService)).rejects.toThrow(DIError)
|
|
662
|
+
})
|
|
663
|
+
|
|
664
|
+
it('should auto-mock unregistered dependencies', async () => {
|
|
665
|
+
// Enable auto-mocking mode
|
|
666
|
+
container.enableAutoMocking()
|
|
667
|
+
const mock = await container.get(UnregisteredService)
|
|
668
|
+
container.expectAutoMocked(UnregisteredService)
|
|
669
|
+
container.disableAutoMocking()
|
|
573
670
|
})
|
|
574
671
|
})
|
|
575
672
|
```
|
|
@@ -584,6 +681,9 @@ describe('UserService', () => {
|
|
|
584
681
|
6. **Prefer singletons** - Unless you specifically need new instances each time
|
|
585
682
|
7. **Use factories** - For complex object creation logic
|
|
586
683
|
8. **Leverage ScopedContainer** - For request-scoped data and cleanup
|
|
684
|
+
9. **Use priority system** - When you need multiple implementations of the same token, use priority to control which one wins
|
|
685
|
+
10. **Use TestContainer for integration tests** - Provides comprehensive assertion helpers and dependency graph inspection
|
|
686
|
+
11. **Use UnitTestContainer for unit tests** - Provides strict isolation and automatic method call tracking
|
|
587
687
|
|
|
588
688
|
## License
|
|
589
689
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { BoundInjectionToken, ClassType, ClassTypeWithArgument, FactoryInjectionToken, InjectionToken, InjectionTokenSchemaType } from "../token/injection-token.mjs";
|
|
2
|
+
import { Join, UnionToArray } from "../utils/types.mjs";
|
|
3
|
+
import { IContainer } from "../interfaces/container.interface.mjs";
|
|
4
|
+
import { InjectableScope } from "../enums/injectable-scope.enum.mjs";
|
|
5
|
+
import { Factorable } from "../interfaces/factory.interface.mjs";
|
|
6
|
+
import { Registry } from "../token/registry.mjs";
|
|
7
|
+
import { NameResolver } from "../internal/core/name-resolver.mjs";
|
|
8
|
+
import { ServiceInvalidator } from "../internal/core/service-invalidator.mjs";
|
|
9
|
+
import { TokenResolver } from "../internal/core/token-resolver.mjs";
|
|
10
|
+
import { UnifiedStorage } from "../internal/holder/unified-storage.mjs";
|
|
11
|
+
import { ZodType, z } from "zod/v4";
|
|
12
|
+
|
|
13
|
+
//#region src/container/abstract-container.d.mts
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Abstract base class for dependency injection containers.
|
|
17
|
+
*
|
|
18
|
+
* Provides shared implementation for common container operations.
|
|
19
|
+
* Both Container and ScopedContainer extend this class.
|
|
20
|
+
*/
|
|
21
|
+
declare abstract class AbstractContainer implements IContainer {
|
|
22
|
+
/**
|
|
23
|
+
* The default scope used when adding instances without explicit registration.
|
|
24
|
+
*/
|
|
25
|
+
protected abstract readonly defaultScope: InjectableScope;
|
|
26
|
+
/**
|
|
27
|
+
* The request ID for scoped containers, undefined for root container.
|
|
28
|
+
*/
|
|
29
|
+
protected abstract readonly requestId: string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Gets the storage for this container.
|
|
32
|
+
*/
|
|
33
|
+
abstract getStorage(): UnifiedStorage;
|
|
34
|
+
/**
|
|
35
|
+
* Gets the registry for this container.
|
|
36
|
+
*/
|
|
37
|
+
protected abstract getRegistry(): Registry;
|
|
38
|
+
/**
|
|
39
|
+
* Gets the token resolver.
|
|
40
|
+
*/
|
|
41
|
+
protected abstract getTokenResolver(): TokenResolver;
|
|
42
|
+
/**
|
|
43
|
+
* Gets the name resolver.
|
|
44
|
+
*/
|
|
45
|
+
protected abstract getNameResolver(): NameResolver;
|
|
46
|
+
/**
|
|
47
|
+
* Gets the service invalidator.
|
|
48
|
+
*/
|
|
49
|
+
protected abstract getServiceInvalidator(): ServiceInvalidator;
|
|
50
|
+
/**
|
|
51
|
+
* Gets an instance from the container.
|
|
52
|
+
*/
|
|
53
|
+
abstract get<T extends ClassType>(token: T): InstanceType<T> extends Factorable<infer R> ? Promise<R> : Promise<InstanceType<T>>;
|
|
54
|
+
abstract get<T extends ClassTypeWithArgument<R>, R>(token: T, args: R): Promise<InstanceType<T>>;
|
|
55
|
+
abstract get<T, S extends InjectionTokenSchemaType>(token: InjectionToken<T, S>, args: z.input<S>): Promise<T>;
|
|
56
|
+
abstract get<T, S extends InjectionTokenSchemaType, R extends boolean>(token: InjectionToken<T, S, R>): R extends false ? Promise<T> : S extends ZodType<infer Type> ? `Error: Your token requires args: ${Join<UnionToArray<keyof Type>, ', '>}` : 'Error: Your token requires args';
|
|
57
|
+
abstract get<T>(token: InjectionToken<T, undefined>): Promise<T>;
|
|
58
|
+
abstract get<T>(token: BoundInjectionToken<T, any>): Promise<T>;
|
|
59
|
+
abstract get<T>(token: FactoryInjectionToken<T, any>): Promise<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Invalidates a service and its dependencies.
|
|
62
|
+
*/
|
|
63
|
+
abstract invalidate(service: unknown): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Disposes the container and cleans up all resources.
|
|
66
|
+
*/
|
|
67
|
+
abstract dispose(): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Calculates the instance name for a given token and optional arguments.
|
|
70
|
+
*
|
|
71
|
+
* @internal
|
|
72
|
+
* @param token The class type, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
|
|
73
|
+
* @param args Optional arguments (ignored for BoundInjectionToken which uses its bound value)
|
|
74
|
+
* @returns The calculated instance name string, or null if the token is a FactoryInjectionToken that is not yet resolved
|
|
75
|
+
*/
|
|
76
|
+
calculateInstanceName(token: ClassType | InjectionToken<any, any> | BoundInjectionToken<any, any> | FactoryInjectionToken<any, any>, args?: unknown): string | null;
|
|
77
|
+
/**
|
|
78
|
+
* Checks if a service is registered in the container.
|
|
79
|
+
*/
|
|
80
|
+
isRegistered(token: any): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Waits for all pending operations to complete.
|
|
83
|
+
*/
|
|
84
|
+
ready(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* @internal
|
|
87
|
+
* Attempts to get an instance synchronously if it already exists.
|
|
88
|
+
*/
|
|
89
|
+
tryGetSync<T>(token: any, args?: any): T | null;
|
|
90
|
+
/**
|
|
91
|
+
* @internal
|
|
92
|
+
* Internal method for getting instances synchronously with configurable storage.
|
|
93
|
+
*/
|
|
94
|
+
protected tryGetSyncFromStorage<T>(token: any, args: any, storage: UnifiedStorage, requestId?: string): T | null;
|
|
95
|
+
/**
|
|
96
|
+
* Adds an instance to the container.
|
|
97
|
+
* Accepts class types, InjectionTokens, and BoundInjectionTokens.
|
|
98
|
+
* Rejects InjectionTokens with required schemas (use BoundInjectionToken instead).
|
|
99
|
+
*
|
|
100
|
+
* @param token The class type, InjectionToken, or BoundInjectionToken to register the instance for
|
|
101
|
+
* @param instance The instance to store
|
|
102
|
+
*/
|
|
103
|
+
addInstance<T>(token: ClassType | InjectionToken<T, any> | BoundInjectionToken<T, any>, instance: T): void;
|
|
104
|
+
/**
|
|
105
|
+
* @internal
|
|
106
|
+
* Internal method for adding instances with configurable scope and storage.
|
|
107
|
+
*/
|
|
108
|
+
protected addInstanceToStorage<T>(token: ClassType | InjectionToken<T, any> | BoundInjectionToken<T, any>, instance: T, storage: UnifiedStorage, scope: InjectableScope, requestId?: string): void;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { AbstractContainer };
|
|
112
|
+
//# sourceMappingURL=abstract-container.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abstract-container.d.mts","names":[],"sources":["../../../src/container/abstract-container.mts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { InjectableScope } from "../enums/injectable-scope.enum.mjs";
|
|
2
|
+
import { InjectableType } from "../enums/injectable-type.enum.mjs";
|
|
3
|
+
import { BoundInjectionToken, InjectionToken } from "../token/injection-token.mjs";
|
|
4
|
+
import { DIError, DIErrorCode } from "../errors/di-error.mjs";
|
|
5
|
+
import { InstanceStatus } from "../internal/holder/instance-holder.mjs";
|
|
6
|
+
import { StubFactoryClass } from "../internal/stub-factory-class.mjs";
|
|
7
|
+
|
|
8
|
+
//#region src/container/abstract-container.mts
|
|
9
|
+
/**
|
|
10
|
+
* Abstract base class for dependency injection containers.
|
|
11
|
+
*
|
|
12
|
+
* Provides shared implementation for common container operations.
|
|
13
|
+
* Both Container and ScopedContainer extend this class.
|
|
14
|
+
*/ var AbstractContainer = class {
|
|
15
|
+
/**
|
|
16
|
+
* Calculates the instance name for a given token and optional arguments.
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
* @param token The class type, InjectionToken, BoundInjectionToken, or FactoryInjectionToken
|
|
20
|
+
* @param args Optional arguments (ignored for BoundInjectionToken which uses its bound value)
|
|
21
|
+
* @returns The calculated instance name string, or null if the token is a FactoryInjectionToken that is not yet resolved
|
|
22
|
+
*/ calculateInstanceName(token, args) {
|
|
23
|
+
const [err, { actualToken, validatedArgs }] = this.getTokenResolver().validateAndResolveTokenArgs(token, args);
|
|
24
|
+
if (err) {
|
|
25
|
+
if (err instanceof DIError && err.code === DIErrorCode.FactoryTokenNotResolved) return null;
|
|
26
|
+
if (err instanceof DIError && err.code === DIErrorCode.TokenValidationError) return null;
|
|
27
|
+
}
|
|
28
|
+
const realToken = this.getTokenResolver().getRealToken(actualToken);
|
|
29
|
+
const registry = this.getRegistry();
|
|
30
|
+
const scope = registry.has(realToken) ? registry.get(realToken).scope : this.defaultScope;
|
|
31
|
+
return this.getNameResolver().generateInstanceName(actualToken, validatedArgs, scope === InjectableScope.Request ? this.requestId : void 0, scope);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Checks if a service is registered in the container.
|
|
35
|
+
*/ isRegistered(token) {
|
|
36
|
+
const realToken = this.getTokenResolver().getRegistryToken(token);
|
|
37
|
+
return this.getRegistry().has(realToken);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Waits for all pending operations to complete.
|
|
41
|
+
*/ async ready() {
|
|
42
|
+
await this.getServiceInvalidator().readyWithStorage(this.getStorage());
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @internal
|
|
46
|
+
* Attempts to get an instance synchronously if it already exists.
|
|
47
|
+
*/ tryGetSync(token, args) {
|
|
48
|
+
return this.tryGetSyncFromStorage(token, args, this.getStorage(), this.requestId);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @internal
|
|
52
|
+
* Internal method for getting instances synchronously with configurable storage.
|
|
53
|
+
*/ tryGetSyncFromStorage(token, args, storage, requestId) {
|
|
54
|
+
const tokenResolver = this.getTokenResolver();
|
|
55
|
+
const realToken = tokenResolver.getRegistryToken(token);
|
|
56
|
+
const registry = this.getRegistry();
|
|
57
|
+
const scope = registry.has(realToken) ? registry.get(realToken).scope : InjectableScope.Singleton;
|
|
58
|
+
try {
|
|
59
|
+
const instanceName = this.getNameResolver().generateInstanceName(tokenResolver.normalizeToken(token), args, requestId, scope);
|
|
60
|
+
const result = storage.get(instanceName);
|
|
61
|
+
if (result && result[0] === void 0 && result[1]) {
|
|
62
|
+
const holder = result[1];
|
|
63
|
+
if (holder.status === InstanceStatus.Created) return holder.instance;
|
|
64
|
+
}
|
|
65
|
+
} catch {}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Adds an instance to the container.
|
|
70
|
+
* Accepts class types, InjectionTokens, and BoundInjectionTokens.
|
|
71
|
+
* Rejects InjectionTokens with required schemas (use BoundInjectionToken instead).
|
|
72
|
+
*
|
|
73
|
+
* @param token The class type, InjectionToken, or BoundInjectionToken to register the instance for
|
|
74
|
+
* @param instance The instance to store
|
|
75
|
+
*/ addInstance(token, instance) {
|
|
76
|
+
this.addInstanceToStorage(token, instance, this.getStorage(), this.defaultScope, this.requestId);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* @internal
|
|
80
|
+
* Internal method for adding instances with configurable scope and storage.
|
|
81
|
+
*/ addInstanceToStorage(token, instance, storage, scope, requestId) {
|
|
82
|
+
if (token instanceof InjectionToken) {
|
|
83
|
+
if (token.schema) {
|
|
84
|
+
if (token.schema?.def?.type !== "optional") throw DIError.tokenSchemaRequiredError(token.name);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const tokenResolver = this.getTokenResolver();
|
|
88
|
+
const registry = this.getRegistry();
|
|
89
|
+
const normalizedToken = tokenResolver.normalizeToken(token);
|
|
90
|
+
const realToken = tokenResolver.getRegistryToken(token);
|
|
91
|
+
if (typeof token === "function" && !registry.has(realToken)) registry.set(realToken, scope, token, InjectableType.Class);
|
|
92
|
+
else if (!registry.has(realToken)) registry.set(realToken, scope, StubFactoryClass, InjectableType.Class, -1);
|
|
93
|
+
const instanceName = this.getNameResolver().generateInstanceName(normalizedToken, normalizedToken instanceof BoundInjectionToken ? normalizedToken.value : void 0, requestId, scope);
|
|
94
|
+
storage.storeInstance(instanceName, instance);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
//#endregion
|
|
99
|
+
export { AbstractContainer };
|
|
100
|
+
//# sourceMappingURL=abstract-container.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abstract-container.mjs","names":["InjectableScope","InjectableType","DIError","DIErrorCode","InstanceStatus","StubFactoryClass","BoundInjectionToken","InjectionToken","AbstractContainer","calculateInstanceName","token","args","tokenResolver","getTokenResolver","err","actualToken","validatedArgs","validateAndResolveTokenArgs","code","FactoryTokenNotResolved","TokenValidationError","realToken","getRealToken","registry","getRegistry","scope","has","get","defaultScope","getNameResolver","generateInstanceName","Request","requestId","undefined","isRegistered","getRegistryToken","ready","getServiceInvalidator","readyWithStorage","getStorage","tryGetSync","tryGetSyncFromStorage","storage","Singleton","instanceName","normalizeToken","result","holder","status","Created","instance","addInstance","addInstanceToStorage","schema","schemaType","def","type","tokenSchemaRequiredError","name","normalizedToken","set","Class","value","storeInstance"],"sources":["../../../src/container/abstract-container.mts"],"sourcesContent":["import type { z, ZodType } from 'zod/v4'\n\nimport type { IContainer } from '../interfaces/container.interface.mjs'\nimport type { Factorable } from '../interfaces/factory.interface.mjs'\nimport type { NameResolver } from '../internal/core/name-resolver.mjs'\nimport type { ServiceInvalidator } from '../internal/core/service-invalidator.mjs'\nimport type { TokenResolver } from '../internal/core/token-resolver.mjs'\nimport type {\n ClassType,\n ClassTypeWithArgument,\n InjectionTokenSchemaType,\n} from '../token/injection-token.mjs'\nimport type { Registry } from '../token/registry.mjs'\nimport type { Join, UnionToArray } from '../utils/types.mjs'\n\nimport { InjectableScope, InjectableType } from '../enums/index.mjs'\nimport { DIError, DIErrorCode } from '../errors/index.mjs'\nimport { InstanceStatus } from '../internal/holder/instance-holder.mjs'\nimport { UnifiedStorage } from '../internal/holder/unified-storage.mjs'\nimport { StubFactoryClass } from '../internal/index.mjs'\nimport {\n BoundInjectionToken,\n FactoryInjectionToken,\n InjectionToken,\n} from '../token/injection-token.mjs'\n\n/**\n * Abstract base class for dependency injection containers.\n *\n * Provides shared implementation for common container operations.\n * Both Container and ScopedContainer extend this class.\n */\nexport abstract class AbstractContainer implements IContainer {\n /**\n * The default scope used when adding instances without explicit registration.\n */\n protected abstract readonly defaultScope: InjectableScope\n\n /**\n * The request ID for scoped containers, undefined for root container.\n */\n protected abstract readonly requestId: string | undefined\n\n // ============================================================================\n // ABSTRACT METHODS - Must be implemented by subclasses\n // ============================================================================\n\n /**\n * Gets the storage for this container.\n */\n abstract getStorage(): UnifiedStorage\n\n /**\n * Gets the registry for this container.\n */\n protected abstract getRegistry(): Registry\n\n /**\n * Gets the token resolver.\n */\n protected abstract getTokenResolver(): TokenResolver\n\n /**\n * Gets the name resolver.\n */\n protected abstract getNameResolver(): NameResolver\n\n /**\n * Gets the service invalidator.\n */\n protected abstract getServiceInvalidator(): ServiceInvalidator\n\n /**\n * Gets an instance from the container.\n */\n // #1 Simple class\n abstract get<T extends ClassType>(\n token: T,\n ): InstanceType<T> extends Factorable<infer R>\n ? Promise<R>\n : Promise<InstanceType<T>>\n // #1.1 Simple class with args\n abstract get<T extends ClassTypeWithArgument<R>, R>(\n token: T,\n args: R,\n ): Promise<InstanceType<T>>\n // #2 Token with required Schema\n abstract get<T, S extends InjectionTokenSchemaType>(\n token: InjectionToken<T, S>,\n args: z.input<S>,\n ): Promise<T>\n // #3 Token with optional Schema\n abstract get<T, S extends InjectionTokenSchemaType, R extends boolean>(\n token: InjectionToken<T, S, R>,\n ): R extends false\n ? Promise<T>\n : S extends ZodType<infer Type>\n ? `Error: Your token requires args: ${Join<\n UnionToArray<keyof Type>,\n ', '\n >}`\n : 'Error: Your token requires args'\n // #4 Token with no Schema\n abstract get<T>(token: InjectionToken<T, undefined>): Promise<T>\n abstract get<T>(token: BoundInjectionToken<T, any>): Promise<T>\n abstract get<T>(token: FactoryInjectionToken<T, any>): Promise<T>\n\n /**\n * Invalidates a service and its dependencies.\n */\n abstract invalidate(service: unknown): Promise<void>\n\n /**\n * Disposes the container and cleans up all resources.\n */\n abstract dispose(): Promise<void>\n\n // ============================================================================\n // SHARED IMPLEMENTATIONS\n // ============================================================================\n\n /**\n * Calculates the instance name for a given token and optional arguments.\n *\n * @internal\n * @param token The class type, InjectionToken, BoundInjectionToken, or FactoryInjectionToken\n * @param args Optional arguments (ignored for BoundInjectionToken which uses its bound value)\n * @returns The calculated instance name string, or null if the token is a FactoryInjectionToken that is not yet resolved\n */\n calculateInstanceName(\n token:\n | ClassType\n | InjectionToken<any, any>\n | BoundInjectionToken<any, any>\n | FactoryInjectionToken<any, any>,\n args?: unknown,\n ): string | null {\n const tokenResolver = this.getTokenResolver()\n\n // Use validateAndResolveTokenArgs to handle token normalization and arg resolution\n const [err, { actualToken, validatedArgs }] =\n tokenResolver.validateAndResolveTokenArgs(token, args)\n\n if (err) {\n // Return null if factory token is not resolved\n if (\n err instanceof DIError &&\n err.code === DIErrorCode.FactoryTokenNotResolved\n ) {\n return null\n }\n\n // Return null if validation fails (can't calculate name with invalid args)\n if (\n err instanceof DIError &&\n err.code === DIErrorCode.TokenValidationError\n ) {\n return null\n }\n }\n\n // Get the real token for registry lookup to determine scope\n const realToken = this.getTokenResolver().getRealToken(actualToken)\n\n const registry = this.getRegistry()\n\n // Get scope from registry, or use default scope if not registered\n const scope = registry.has(realToken)\n ? registry.get(realToken).scope\n : this.defaultScope\n\n // Generate instance name using the name resolver with actual token and validated args\n return this.getNameResolver().generateInstanceName(\n actualToken,\n validatedArgs,\n scope === InjectableScope.Request ? this.requestId : undefined,\n scope,\n )\n }\n\n /**\n * Checks if a service is registered in the container.\n */\n isRegistered(token: any): boolean {\n const realToken = this.getTokenResolver().getRegistryToken(token)\n return this.getRegistry().has(realToken)\n }\n\n /**\n * Waits for all pending operations to complete.\n */\n async ready(): Promise<void> {\n await this.getServiceInvalidator().readyWithStorage(this.getStorage())\n }\n\n /**\n * @internal\n * Attempts to get an instance synchronously if it already exists.\n */\n tryGetSync<T>(token: any, args?: any): T | null {\n return this.tryGetSyncFromStorage(\n token,\n args,\n this.getStorage(),\n this.requestId,\n )\n }\n\n /**\n * @internal\n * Internal method for getting instances synchronously with configurable storage.\n */\n protected tryGetSyncFromStorage<T>(\n token: any,\n args: any,\n storage: UnifiedStorage,\n requestId?: string,\n ): T | null {\n const tokenResolver = this.getTokenResolver()\n const realToken = tokenResolver.getRegistryToken(token)\n const registry = this.getRegistry()\n const scope = registry.has(realToken)\n ? registry.get(realToken).scope\n : InjectableScope.Singleton\n\n try {\n const instanceName = this.getNameResolver().generateInstanceName(\n tokenResolver.normalizeToken(token),\n args,\n requestId,\n scope,\n )\n\n const result = storage.get(instanceName)\n if (result && result[0] === undefined && result[1]) {\n const holder = result[1]\n if (holder.status === InstanceStatus.Created) {\n return holder.instance as T\n }\n }\n } catch {\n // Ignore error\n }\n\n return null\n }\n\n /**\n * Adds an instance to the container.\n * Accepts class types, InjectionTokens, and BoundInjectionTokens.\n * Rejects InjectionTokens with required schemas (use BoundInjectionToken instead).\n *\n * @param token The class type, InjectionToken, or BoundInjectionToken to register the instance for\n * @param instance The instance to store\n */\n addInstance<T>(\n token: ClassType | InjectionToken<T, any> | BoundInjectionToken<T, any>,\n instance: T,\n ): void {\n this.addInstanceToStorage(\n token,\n instance,\n this.getStorage(),\n this.defaultScope,\n this.requestId,\n )\n }\n\n /**\n * @internal\n * Internal method for adding instances with configurable scope and storage.\n */\n protected addInstanceToStorage<T>(\n token: ClassType | InjectionToken<T, any> | BoundInjectionToken<T, any>,\n instance: T,\n storage: UnifiedStorage,\n scope: InjectableScope,\n requestId?: string,\n ): void {\n // Check if token is an InjectionToken with required schema\n // BoundInjectionToken is allowed (it already has a value bound)\n if (token instanceof InjectionToken) {\n // Check if schema exists and is required (not optional)\n if (token.schema) {\n const schemaType = (token.schema as ZodType)?.def?.type\n if (schemaType !== 'optional') {\n throw DIError.tokenSchemaRequiredError(token.name)\n }\n }\n }\n\n const tokenResolver = this.getTokenResolver()\n const registry = this.getRegistry()\n\n // Normalize the token\n const normalizedToken = tokenResolver.normalizeToken(token)\n const realToken = tokenResolver.getRegistryToken(token)\n\n // If it's a class type and not registered, register it with the given scope\n if (typeof token === 'function' && !registry.has(realToken)) {\n registry.set(realToken, scope, token, InjectableType.Class)\n } else if (!registry.has(realToken)) {\n // Set a stub factory class to avoid errors when getting instances of unregistered factory tokens\n registry.set(\n realToken,\n scope,\n StubFactoryClass,\n InjectableType.Class,\n // Lowest priority to avoid conflicts with other registered tokens\n -1,\n )\n }\n\n // Generate instance name with the given scope\n const instanceName = this.getNameResolver().generateInstanceName(\n normalizedToken,\n normalizedToken instanceof BoundInjectionToken\n ? normalizedToken.value\n : undefined,\n requestId,\n scope,\n )\n\n // Store the instance\n storage.storeInstance(instanceName, instance)\n }\n}\n"],"mappings":"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"}
|