@fluojs/di
Advanced tools
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAW3E,OAAO,KAAK,EASV,QAAQ,EAET,MAAM,YAAY,CAAC;AA4IpB;;GAEG;AACH,qBAAa,SAAS;IAsBlB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAtBtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IACtE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA0C;IAC7E,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAoB;IAC1D,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,iBAAiB,CAAwD;IACjF,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAmD;IACvF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA4B;IAC/D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAiB;IACrD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+B;IAC9D,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAsC;IAC3E,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAA0E;IAClH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyE;IAChH,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAAmD;IAChG,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAA0E;IACrH,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAK;gBAGP,MAAM,CAAC,EAAE,SAAS,YAAA,EAClB,mBAAmB,UAAQ,EAC5C,cAAc,CAAC,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAK/C;;;;;;;;;OASG;IACH,QAAQ,CAAC,GAAG,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI;IA4CxC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,GAAG,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI;IA6DxC;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAI1B;;;;;OAKG;IACH,0BAA0B,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAcjD;;;;;OAKG;IACH,kBAAkB,IAAI,SAAS;IAW/B;;;;;;;;;OASG;IACG,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAW7C;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAkBhB,UAAU;IAgCxB,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,4BAA4B;IAsBpC,OAAO,CAAC,6BAA6B;IAIrC,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,4BAA4B;IAIpC,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,qBAAqB;IAsB7B,OAAO,CAAC,iCAAiC;IAyBzC,OAAO,CAAC,qCAAqC;IAc7C,OAAO,CAAC,sCAAsC;IAY9C,OAAO,CAAC,mCAAmC;YAa7B,gBAAgB;YAehB,8BAA8B;IAqC5C,OAAO,CAAC,eAAe;YAgBT,kBAAkB;IAMhC,OAAO,CAAC,mCAAmC;YAoB7B,6BAA6B;YAc7B,4BAA4B;IA4B1C,OAAO,CAAC,6BAA6B;YAQvB,gCAAgC;IAuB9C,OAAO,CAAC,kCAAkC;IAY1C,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,kCAAkC;YAI5B,eAAe;YAwBf,gBAAgB;IAiB9B,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,yBAAyB;IAWjC,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,yBAAyB;IAMjC,OAAO,CAAC,cAAc;IAatB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,oBAAoB;YAkBd,YAAY;YAaZ,0BAA0B;YA0B1B,8BAA8B;IAc5C,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,yBAAyB;YAOnB,yBAAyB;IAMvC,OAAO,CAAC,qBAAqB;IAoB7B,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,YAAY;YAIN,WAAW;IA+BzB,OAAO,CAAC,+BAA+B;IAmBvC,OAAO,CAAC,2BAA2B;IAqBnC,OAAO,CAAC,gCAAgC;IAkCxC,OAAO,CAAC,wBAAwB;IA+ChC,OAAO,CAAC,8BAA8B;IAYtC,OAAO,CAAC,sBAAsB;YAUhB,mBAAmB;IAUjC,OAAO,CAAC,qBAAqB;CAmD9B"} |
+30
-9
@@ -206,16 +206,37 @@ import { InvariantError, formatTokenName } from '@fluojs/core'; | ||
| } | ||
| const normalizedByToken = new Map(); | ||
| for (const provider of providers) { | ||
| const normalized = normalizeProvider(provider); | ||
| const existing = this.lookupProvider(normalized.provide); | ||
| this.registrations.delete(normalized.provide); | ||
| this.multiRegistrations.delete(normalized.provide); | ||
| this.invalidateCachedEntry(normalized.provide, existing?.scope ?? normalized.scope); | ||
| if (normalized.multi) { | ||
| this.multiRegistrations.set(normalized.provide, [normalized]); | ||
| this.multiOverriddenTokens.add(normalized.provide); | ||
| const normalizedProviders = normalizedByToken.get(normalized.provide); | ||
| if (normalizedProviders) { | ||
| normalizedProviders.push(normalized); | ||
| continue; | ||
| } | ||
| normalizedByToken.set(normalized.provide, [normalized]); | ||
| } | ||
| for (const [token, normalizedProviders] of normalizedByToken) { | ||
| const firstProvider = normalizedProviders[0]; | ||
| if (!firstProvider) { | ||
| continue; | ||
| } | ||
| const containsMultiProvider = normalizedProviders.some(normalized => normalized.multi === true); | ||
| if (containsMultiProvider && normalizedProviders.some(normalized => normalized.multi !== true)) { | ||
| throw new DuplicateProviderError(token); | ||
| } | ||
| if (!containsMultiProvider && normalizedProviders.length > 1) { | ||
| throw new DuplicateProviderError(token); | ||
| } | ||
| const existing = this.lookupProvider(token); | ||
| const existingMultiProviders = this.collectMultiProviders(token); | ||
| this.registrations.delete(token); | ||
| this.multiRegistrations.delete(token); | ||
| this.invalidateCachedEntry(token, existing?.scope ?? existingMultiProviders[0]?.scope ?? firstProvider.scope); | ||
| if (containsMultiProvider) { | ||
| this.multiRegistrations.set(token, normalizedProviders); | ||
| this.multiOverriddenTokens.add(token); | ||
| this.advanceGraphRevision(); | ||
| continue; | ||
| } | ||
| this.multiOverriddenTokens.add(normalized.provide); | ||
| this.registrations.set(normalized.provide, normalized); | ||
| this.multiOverriddenTokens.add(token); | ||
| this.registrations.set(token, firstProvider); | ||
| this.advanceGraphRevision(); | ||
@@ -222,0 +243,0 @@ } |
+2
-2
@@ -12,3 +12,3 @@ { | ||
| ], | ||
| "version": "1.0.0-beta.6", | ||
| "version": "1.0.0-beta.7", | ||
| "private": false, | ||
@@ -40,3 +40,3 @@ "license": "MIT", | ||
| "dependencies": { | ||
| "@fluojs/core": "^1.0.0-beta.3" | ||
| "@fluojs/core": "^1.0.0-beta.5" | ||
| }, | ||
@@ -43,0 +43,0 @@ "devDependencies": { |
+26
-2
@@ -81,2 +81,6 @@ # @fluojs/di | ||
| ### provider override | ||
| 테스트나 request-local 경계에서 기존 등록을 의도적으로 교체해야 할 때는 `override(...providers)`를 사용합니다. override는 각 토큰의 현재 provider set을 교체하고 cached instance를 무효화하며, 오래된 instance를 즉시 dispose합니다. multi provider override는 해당 토큰의 전체 multi-provider set을 교체하므로 필요한 replacement provider를 한 번에 모두 전달하세요. 같은 토큰에 single replacement와 multi replacement를 한 override 호출에서 섞으면 모호한 교체로 보고 거부합니다. | ||
| ### request scope 분리 | ||
@@ -115,2 +119,14 @@ | ||
| `forwardRef(...)`와 `optional(...)`은 클래스 수준 `@Inject(...)` 토큰 목록이나 provider 수준 `inject` 배열 안에서 쓰는 토큰 래퍼입니다. 이들은 데코레이터가 아니며 constructor parameter에 붙이지 않습니다. | ||
| ```typescript | ||
| import { optional } from '@fluojs/di'; | ||
| import { Inject } from '@fluojs/core'; | ||
| @Inject(optional(AuditLogger)) | ||
| class ServiceWithOptionalLogger { | ||
| constructor(private readonly auditLogger: AuditLogger | undefined) {} | ||
| } | ||
| ``` | ||
| ## 테스트 및 모킹 | ||
@@ -124,3 +140,3 @@ | ||
| const container = new Container(); | ||
| const mockDb = { query: jest.fn() }; | ||
| const mockDb = { query: vi.fn() }; | ||
@@ -134,3 +150,3 @@ // 실제 Database 클래스를 모의 객체 값으로 교체 | ||
| const service = await container.resolve(DataService); | ||
| // 이제 service는 실제 Database 인스턴스 대신 mockDb를 사용합니다. | ||
| // service는 실제 Database 인스턴스 대신 mockDb를 사용합니다. | ||
| ``` | ||
@@ -152,2 +168,3 @@ | ||
| | `register(...providers)` | 하나 이상의 프로바이더를 등록합니다. | | ||
| | `override(...providers)` | 기존 provider를 교체하고 cached instance를 무효화하며 오래된 instance를 dispose합니다. | | ||
| | `resolve<T>(token)` | 토큰을 인스턴스로 비동기 해석합니다. | | ||
@@ -157,3 +174,10 @@ | `createRequestScope()` | 요청 스코프 의존성을 위한 자식 컨테이너를 생성합니다. | | ||
| | `hasRequestScopedDependency(token)` | 토큰 해석 시 provider 그래프에 request-scoped 의존성이나 순환이 있어 request-scope 컨테이너가 필요할 수 있는지 확인합니다. | | ||
| | `dispose()` | request child와 루트가 소유한 singleton instance를 정리합니다. | | ||
| | `forwardRef(fn)` | 선언 순서 문제를 위해 조회를 지연하는 토큰 래퍼를 반환합니다. 실제 생성자 순환을 해석 가능하게 만들지는 않습니다. | | ||
| | `optional(token)` | 하나의 의존성을 optional로 표시하는 토큰 래퍼를 반환합니다. 누락된 optional dependency는 `undefined`로 해석됩니다. | | ||
| | `Scope` | `DEFAULT`, `REQUEST`, `TRANSIENT` scope 상수를 제공합니다. | | ||
| | 에러 클래스 | `InvalidProviderError`, `ContainerResolutionError`, `RequestScopeResolutionError`, `ScopeMismatchError`, `CircularDependencyError`, `DuplicateProviderError`. | | ||
| multi-provider 토큰을 resolve하면 등록 순서대로 해석된 값의 배열이 반환됩니다. | ||
| ## 관련 패키지 | ||
@@ -160,0 +184,0 @@ |
+27
-3
@@ -67,3 +67,3 @@ # @fluojs/di | ||
| ### Provider Types | ||
| fluo DI supports three main provider shapes: | ||
| fluo DI supports four provider shapes: | ||
| - **Class Providers**: `container.register(MyService)` or `{ provide: MyToken, useClass: MyService }`. | ||
@@ -81,2 +81,6 @@ - **Value Providers**: `{ provide: 'API_URL', useValue: 'https://api.example.com' }`. | ||
| ### Provider Overrides | ||
| Use `override(...providers)` when a test or request-local boundary needs to replace existing registrations deliberately. Overrides replace the current provider set for each token, invalidate cached instances, and dispose stale instances immediately. Multi-provider overrides replace the full multi-provider set for that token, so pass every replacement provider together; mixing single and multi replacements for the same token in one override call is rejected as ambiguous. | ||
| ### Request Scoping | ||
@@ -116,2 +120,14 @@ Isolated containers can be created to handle per-request state without polluting the root container. | ||
| `forwardRef(...)` and `optional(...)` are token wrappers used inside the class-level `@Inject(...)` token list or provider-level `inject` arrays. They are not decorators and do not attach to constructor parameters. | ||
| ```typescript | ||
| import { optional } from '@fluojs/di'; | ||
| import { Inject } from '@fluojs/core'; | ||
| @Inject(optional(AuditLogger)) | ||
| class ServiceWithOptionalLogger { | ||
| constructor(private readonly auditLogger: AuditLogger | undefined) {} | ||
| } | ||
| ``` | ||
| ## Testing and Mocking | ||
@@ -125,3 +141,3 @@ | ||
| const container = new Container(); | ||
| const mockDb = { query: jest.fn() }; | ||
| const mockDb = { query: vi.fn() }; | ||
@@ -135,3 +151,3 @@ // Override the real Database class with a mock value | ||
| const service = await container.resolve(DataService); | ||
| // service will now use mockDb instead of the real Database instance | ||
| // service uses mockDb instead of the real Database instance | ||
| ``` | ||
@@ -153,2 +169,3 @@ | ||
| | `register(...providers)` | Registers one or more providers. | | ||
| | `override(...providers)` | Replaces existing providers, invalidates cached instances, and disposes stale instances. | | ||
| | `resolve<T>(token)` | Asynchronously resolves a token to an instance. | | ||
@@ -158,3 +175,10 @@ | `createRequestScope()` | Creates a child container for request-scoped dependencies. | | ||
| | `hasRequestScopedDependency(token)` | Checks whether resolving a token may require a request-scope container because its provider graph contains request-scoped dependencies or is cyclic. | | ||
| | `dispose()` | Disposes request children and root-owned singleton instances. | | ||
| | `forwardRef(fn)` | Returns a token wrapper that defers lookup for declaration-order issues; it does not make constructor dependency cycles resolvable. | | ||
| | `optional(token)` | Returns a token wrapper that marks one dependency as optional; missing optional dependencies resolve to `undefined`. | | ||
| | `Scope` | Exposes `DEFAULT`, `REQUEST`, and `TRANSIENT` scope constants. | | ||
| | Error classes | `InvalidProviderError`, `ContainerResolutionError`, `RequestScopeResolutionError`, `ScopeMismatchError`, `CircularDependencyError`, `DuplicateProviderError`. | | ||
| Resolving a multi-provider token returns an array of resolved values in registration order. | ||
| ## Related Packages | ||
@@ -161,0 +185,0 @@ |
85795
5.8%1549
1.37%188
14.63%Updated