@fluojs/di 2.0.0 → 3.0.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/README.ko.md +89 -7
- package/README.md +89 -7
- package/dist/container.d.ts +39 -5
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +382 -48
- package/dist/errors.d.ts +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -2
- package/dist/internal.d.ts +15 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +25 -0
- package/dist/multi-contribution-registry.d.ts +26 -0
- package/dist/multi-contribution-registry.d.ts.map +1 -0
- package/dist/multi-contribution-registry.js +29 -0
- package/dist/provider-normalization.d.ts.map +1 -1
- package/dist/provider-normalization.js +9 -4
- package/package.json +5 -5
package/README.ko.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
<p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
|
|
4
4
|
|
|
5
|
+
Node.js 지원 범위는 `>=24.0.0 <27`입니다. 업그레이드 절차는 [Node.js 지원 및 마이그레이션](../../docs/reference/node-support.ko.md)을 참조하세요.
|
|
6
|
+
|
|
5
7
|
모든 fluo 애플리케이션을 구동하는 최소 토큰 기반 의존성 주입 컨테이너입니다.
|
|
6
8
|
|
|
7
9
|
## 목차
|
|
@@ -10,8 +12,10 @@
|
|
|
10
12
|
- [사용 시점](#사용-시점)
|
|
11
13
|
- [빠른 시작](#빠른-시작)
|
|
12
14
|
- [주요 기능](#주요-기능)
|
|
15
|
+
- [NestJS scope 및 optional 의존성 마이그레이션](#nestjs-scope-및-optional-의존성-마이그레이션)
|
|
13
16
|
- [순환 의존성 처리](#순환-의존성-처리)
|
|
14
17
|
- [테스트 및 모킹](#테스트-및-모킹)
|
|
18
|
+
- [내부 패키지 통합](#내부-패키지-통합)
|
|
15
19
|
- [문제 해결](#문제-해결)
|
|
16
20
|
- [공개 API](#공개-api)
|
|
17
21
|
- [관련 패키지](#관련-패키지)
|
|
@@ -76,11 +80,59 @@ const service = await container.resolve(UserService);
|
|
|
76
80
|
- **request**: `createRequestScope()`마다 새로 생성됩니다.
|
|
77
81
|
- **transient**: resolve할 때마다 새 인스턴스를 만듭니다.
|
|
78
82
|
|
|
79
|
-
|
|
83
|
+
singleton provider는 request-scoped provider에 의존할 수 없습니다. 이 mismatch는 그래프의 어떤 provider factory나 constructor도 실행되기 전에 `ScopeMismatchError`를 던지며, 이 검사는 single, alias(`useExisting`), multi-provider 등록을 모두 포함합니다. singleton이 multi token을 주입받을 때도 해당 token의 contribution 중 하나라도 request scope이면 같은 방식으로 실패하므로, contribution 일부만 materialize되는 일이 없습니다.
|
|
84
|
+
|
|
85
|
+
dispose 중에는 각 컨테이너가 single-provider cache와 multi-provider cache 전체에서 성공적으로 materialize된 cached instance를 실제 생성 순서의 역순으로 정리하므로, dependency보다 dependent를 먼저 종료합니다. 각 컨테이너는 자신이 소유한 살아 있는 request scope 자식을 먼저 재귀적으로 정리하므로, 루트가 아닌 request scope를 dispose해도 중첩 request scope를 닫은 뒤 자신의 request cache를 정리합니다. 이후 루트 dispose는 자식 dispose 중 하나 이상이 실패하더라도 루트가 소유한 singleton 정리를 계속 수행합니다. 자식/루트 dispose 실패가 여러 개 발생하면 `dispose()`는 모든 shutdown 실패를 확인할 수 있도록 `AggregateError`로 보고합니다.
|
|
86
|
+
|
|
87
|
+
`dispose()` 시작은 `resolve()`, `register()`, `override()`, `createRequestScope()`에 대해 terminal입니다. 동시 caller는 active disposal 시도를 공유합니다. `onDestroy()` hook이 실패하면 컨테이너는 실패한 hook만 이후 명시적 `dispose()` 재시도를 위해 유지하면서 child-before-parent/root 순서와 생성 역순을 보존합니다. 성공적으로 완료된 hook은 다시 실행하지 않으며, 유지된 hook이 모두 성공한 뒤 disposal은 멱등입니다.
|
|
88
|
+
|
|
89
|
+
#### disposal 재시도 ownership
|
|
90
|
+
|
|
91
|
+
Disposal 재시도는 다음 다섯 ownership 규칙을 따릅니다.
|
|
92
|
+
|
|
93
|
+
1. public `child.dispose()`를 직접 호출하면 request child는 active attempt가 settle된 뒤 parent graph에서 분리됩니다. 유지된 `onDestroy()` hook이 실패해도 분리됩니다.
|
|
94
|
+
2. 분리된 child 참조를 유지한 caller는 `dispose()`를 다시 호출할 수 있습니다. 이 호출은 해당 child의 실패한 hook만 재시도하며 성공한 sibling hook은 반복하지 않습니다.
|
|
95
|
+
3. parent 또는 root disposal이 먼저 진입한 child는 실패 후에도 parent가 계속 추적합니다. 이후 parent 또는 root `dispose()`는 parent나 root가 유지한 hook보다 그 child를 먼저 재시도합니다.
|
|
96
|
+
4. 동시 direct caller와 parent caller는 하나의 active attempt를 공유합니다. shared attempt를 시작한 caller가 direct 또는 parent ownership을 결정합니다. 나중에 참여한 caller는 이를 바꿀 수 없습니다.
|
|
97
|
+
5. parent가 유지한 child를 나중에 `child.dispose()`로 직접 재시도하면 해당 direct attempt가 settle된 뒤 child를 분리합니다. direct 재시도가 다시 실패해도 분리됩니다.
|
|
98
|
+
|
|
99
|
+
실행 가능한 근거는 graph ownership을 검증하는 `packages/di/src/container-disposal-ownership.test.ts`와 failed-hook ordering 및 idempotency를 검증하는 `packages/di/src/container-disposal-retry.test.ts`에 있습니다.
|
|
100
|
+
|
|
101
|
+
### 2.x에서 3.x로 disposal 마이그레이션
|
|
102
|
+
|
|
103
|
+
`@fluojs/di` 2.x에서는 실패한 container-managed `onDestroy()` hook을 한 번만 시도했습니다. 3.x에서는 이후 명시적 `Container.dispose()` 호출이나 동일한 컨테이너에 도달하는 application/application-context `close()`가 실패한 hook만 재시도합니다. 이미 성공적으로 완료된 hook은 exactly-once를 유지합니다. 업그레이드하기 전에 실패할 수 있는 cleanup hook이 다시 시도되어도 안전하도록 만드세요. 부분 cleanup을 끝내는 데 필요한 상태를 보존하고, 이미 해제된 resource를 허용하며, 반복된 실패를 shutdown caller에게 전달해야 합니다.
|
|
104
|
+
|
|
105
|
+
direct `child.dispose()`는 이제 실패한 attempt를 포함해 attempt가 settle된 뒤 request child를 parent에서 분리합니다. direct caller가 해당 실패를 확인하거나 재시도해야 한다면 child 참조를 유지하세요. parent 또는 root가 시작한 disposal의 실패는 cleanup이 성공하거나 이후 direct child attempt가 settle될 때까지 parent hierarchy가 소유합니다. direct caller와 parent caller가 겹치면 shared attempt를 시작한 caller가 detach와 retry semantics를 소유합니다.
|
|
80
106
|
|
|
81
107
|
### provider override
|
|
82
108
|
|
|
83
|
-
테스트나 request-local 경계에서 기존 등록을 의도적으로 교체해야 할 때는 `override(...providers)`를 사용합니다. override는 각 토큰의 현재 provider set을 교체하고 현재 컨테이너와 이미 materialize된 request-scope 자식의 cached instance를 무효화하며, 다음 replacement resolution이 계속되기 전에 오래된 instance의 dispose가 끝나도록 보장합니다. multi provider override는 해당 토큰의 전체 multi-provider set을 교체하므로 필요한 replacement provider를 한 번에 모두 전달하세요. 같은 토큰에 single replacement와 multi replacement를 한 override 호출에서 섞으면 모호한 교체로 보고 거부합니다.
|
|
109
|
+
테스트나 request-local 경계에서 기존 등록을 의도적으로 교체해야 할 때는 `override(...providers)`를 사용합니다. override는 각 토큰의 현재 provider set을 교체하고 현재 컨테이너와 이미 materialize된 request-scope 자식의 cached instance를 무효화하며, 다음 replacement resolution이 계속되기 전에 오래된 instance의 dispose가 끝나도록 보장합니다. multi provider override는 해당 토큰의 전체 multi-provider set을 교체하므로 필요한 replacement provider를 한 번에 모두 전달하세요. 같은 토큰에 single replacement와 multi replacement를 한 override 호출에서 섞으면 모호한 교체로 보고 거부합니다. override 호출은 원자적입니다. 배치 전체를 검증한 뒤에야 등록과 캐시를 변경하므로, 거부된 호출은 모든 provider와 cached instance, disposal 소유권을 호출 이전 상태 그대로 남깁니다.
|
|
110
|
+
|
|
111
|
+
### 컨테이너 생성 경계
|
|
112
|
+
|
|
113
|
+
공개된 생성 형태는 `new Container()` 하나뿐이며, 항상 자신의 singleton cache를 소유하는 루트 컨테이너를 만듭니다. child request scope는 package가 소유합니다. parent 연결, request-scope flag, singleton cache 공유는 `createRequestScope()`로만 도달할 수 있는 private construction path입니다.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const root = new Container();
|
|
117
|
+
const requestScope = root.createRequestScope();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
constructor 인자 전달은 거부됩니다. emitted declaration은 할당 가능한 인자 타입을 받지 않으며, 런타임에서도 caller가 인자를 넘기면 cache ownership을 빌린 컨테이너를 만드는 대신 `ContainerResolutionError`를 던집니다.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// 거부됨: child-scope wiring은 package가 소유합니다.
|
|
124
|
+
Reflect.construct(Container, [root]);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 2.x에서 3.x로 컨테이너 생성 마이그레이션
|
|
128
|
+
|
|
129
|
+
`@fluojs/di` 2.x에서는 caller가 child wiring을 직접 넘기는 것이 지원되는 workflow가 아니었음에도, emitted `Container` declaration이 `parent`, `requestScopeEnabled`, `singletonCache` constructor parameter를 노출했습니다. 3.x에서는 이 surface를 봉쇄합니다. constructor는 인자를 받지 않으며, 인자를 전달하면 `ContainerResolutionError`를 던집니다.
|
|
130
|
+
|
|
131
|
+
인자 없는 `new Container()`와 `createRequestScope()`는 그대로이므로 지원되는 코드는 마이그레이션이 필요 없습니다. child 컨테이너를 직접 생성했다면 해당 호출을 `parent.createRequestScope()`로 바꾸세요. 동일한 parent 연결, request-scope flag, 공유 root singleton cache를 제공하면서 disposal ownership도 그대로 유지합니다.
|
|
132
|
+
|
|
133
|
+
실행 가능한 근거는 `packages/di/src/container-construction-boundary.test.ts`에 있습니다.
|
|
134
|
+
|
|
135
|
+
실패한 stale `onDestroy()` hook도 일반 disposal과 동일한 retained-retry 계약을 따릅니다. observing container의 다음 resolution이 그 실패를 한 번 노출해 replacement가 계속될 수 있게 하며, 실패한 instance는 해당 cleanup을 예약한 container가 이후 명시적 `dispose()`로 hook을 다시 호출할 때까지 retain됩니다. 이미 성공한 stale hook은 다시 실행하지 않습니다.
|
|
84
136
|
|
|
85
137
|
### request scope 분리
|
|
86
138
|
|
|
@@ -91,7 +143,28 @@ const scopedService = await requestContainer.resolve(RequestScopedService);
|
|
|
91
143
|
|
|
92
144
|
request scope 컨테이너는 부모 체인의 provider를 해석할 수 있지만, request가 소유하는 등록은 새 singleton provider를 만들 수 없습니다. singleton provider는 request scope를 만들기 전에 루트 컨테이너에 등록하세요. request scope에 로컬 provider를 추가해야 한다면 `scope: 'request'`/`Scope.REQUEST`를 명시하거나 `override()`로 의도적인 request-local 교체를 표현하세요. multi provider에도 같은 규칙이 적용됩니다. 기본 scope의 multi provider는 루트 컨테이너에 등록하고, request-local multi provider는 request scope를 명시하거나 `override()`로 교체해야 합니다.
|
|
93
145
|
|
|
94
|
-
provider 객체는 등록 시점에 검증됩니다. 모든 객체 provider는
|
|
146
|
+
provider 객체는 등록 시점에 검증됩니다. 모든 객체 provider는 string, symbol 또는 constructable class `provide` 토큰과 정확히 하나의 전략(`useClass`, `useValue`, `useFactory`, `useExisting`)을 포함해야 합니다. alias provider의 `useExisting`에도 동일한 유효 토큰 형태가 필요합니다. class provider에서 `inject`를 생략하거나 `undefined`로 지정하면 `useClass`의 `@Inject(...)` 메타데이터로 fallback하며, 그 밖의 명시적 `inject` 값은 유효한 token 또는 올바른 `forwardRef(...)` / `optional(...)` wrapper로 구성된 배열이어야 합니다. value provider는 `inject`를 생략해야 하며, 값이 `undefined`인 경우에도 자체 속성으로 선언하면 거부됩니다. 명시적인 `scope` 값은 `singleton`, `request`, `transient` 중 하나여야 합니다. 잘못된 provider 형태는 컨테이너 그래프에 영향을 주기 전에 `InvalidProviderError`를 발생시킵니다.
|
|
147
|
+
|
|
148
|
+
## NestJS scope 및 optional 의존성 마이그레이션
|
|
149
|
+
|
|
150
|
+
NestJS `@Injectable({ scope: Scope.REQUEST })`와 `@Injectable({ scope: Scope.TRANSIENT })`는 `@Scope('request')` / `@Scope('transient')` 또는 명시적 provider `scope: 'request'` / `scope: 'transient'`를 가진 fluo provider로 매핑합니다. Singleton은 기본값으로 유지됩니다.
|
|
151
|
+
|
|
152
|
+
fluo는 NestJS scope bubbling을 구현하지 않습니다. Request-scoped provider는 `createRequestScope()` child container에서 resolve하세요. Root에서 resolve하면 `RequestScopeResolutionError`가 발생하고, request-scoped provider에 의존하는 singleton은 `ScopeMismatchError`를 발생시킵니다.
|
|
153
|
+
|
|
154
|
+
NestJS `@Optional()`은 클래스 수준 `@Inject(...)` 목록 또는 provider `inject` 배열의 `optional(Token)`으로 매핑합니다. `optional(...)`은 decorator가 아닌 token wrapper이고, 등록이 없으면 `undefined`로 resolve됩니다.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { Inject, Scope } from '@fluojs/core';
|
|
158
|
+
import { optional } from '@fluojs/di';
|
|
159
|
+
|
|
160
|
+
class AuditLogger {}
|
|
161
|
+
|
|
162
|
+
@Scope('request')
|
|
163
|
+
@Inject(optional(AuditLogger))
|
|
164
|
+
class RequestAuditService {
|
|
165
|
+
constructor(private readonly auditLogger: AuditLogger | undefined) {}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
95
168
|
|
|
96
169
|
## 순환 의존성 처리
|
|
97
170
|
|
|
@@ -167,6 +240,14 @@ it('uses a mock database', async () => {
|
|
|
167
240
|
});
|
|
168
241
|
```
|
|
169
242
|
|
|
243
|
+
## 내부 패키지 통합
|
|
244
|
+
|
|
245
|
+
`@fluojs/di/internal`은 first-party framework package를 위한 typed integration
|
|
246
|
+
seam입니다. 이 경로는 owning container를 통해 순서가 있는 `multi: true`
|
|
247
|
+
contribution 하나를 해석하며, container의 scope, cache, cycle, ordering, disposal
|
|
248
|
+
semantics를 보존합니다. 애플리케이션 코드는 `Container.resolve(...)`를 사용해야 하며,
|
|
249
|
+
contribution index는 root `Container` API에 속하지 않습니다.
|
|
250
|
+
|
|
170
251
|
## 문제 해결
|
|
171
252
|
|
|
172
253
|
### CircularDependencyError
|
|
@@ -179,15 +260,15 @@ it('uses a mock database', async () => {
|
|
|
179
260
|
|
|
180
261
|
| Surface | 종류 | 설명 |
|
|
181
262
|
|---|---|---|
|
|
182
|
-
| `Container` | Root export | 메인 DI 컨테이너 클래스입니다. |
|
|
263
|
+
| `Container` | Root export | 메인 DI 컨테이너 클래스입니다. `new Container()`는 인자를 받지 않고 루트 컨테이너를 만들며, child request scope는 package가 소유하고 `createRequestScope()`로 생성합니다. constructor 인자를 전달하면 `ContainerResolutionError`를 던집니다. |
|
|
183
264
|
| `container.register(...providers)` | `Container` instance method | 하나 이상의 프로바이더를 등록합니다. |
|
|
184
|
-
| `container.override(...providers)` | `Container` instance method | 기존 provider를 교체하고 cached instance를 무효화하며 다음 replacement resolution이 계속되기 전에 오래된 instance dispose가 settle되도록 보장합니다. |
|
|
265
|
+
| `container.override(...providers)` | `Container` instance method | 호출 단위로 원자적으로 기존 provider를 교체하고 cached instance를 무효화하며 다음 replacement resolution이 계속되기 전에 오래된 instance dispose가 settle되도록 보장합니다. |
|
|
185
266
|
| `container.resolve<T>(token)` | `Container` instance method | 토큰을 인스턴스로 비동기 해석합니다. |
|
|
186
267
|
| `container.inspectResolutionState()` | `Container` instance method | snapshot read-only map view, frozen provider record, controlled cache adoption을 통해 cache ownership을 보존해야 하는 testing/tooling helper를 위한 지원 대상 framework-owned container introspection seam을 노출합니다. 애플리케이션 코드는 `has(...)`와 `resolve(...)`를 우선 사용하세요. |
|
|
187
|
-
| `container.createRequestScope()` | `Container` instance method | 요청 스코프 의존성을 위한 자식 컨테이너를 생성합니다. |
|
|
268
|
+
| `container.createRequestScope()` | `Container` instance method | 요청 스코프 의존성을 위한 자식 컨테이너를 생성합니다. parent에 연결되고 request scope가 활성화되며 root singleton cache를 공유하는 컨테이너를 얻는 유일한 지원 경로입니다. |
|
|
188
269
|
| `container.has(token)` | `Container` instance method | 컨테이너나 부모에 토큰이 등록되어 있는지 확인합니다. |
|
|
189
270
|
| `container.hasRequestScopedDependency(token)` | `Container` instance method | 토큰 해석 시 provider 그래프에 request-scoped 의존성이나 순환이 있어 request-scope 컨테이너가 필요할 수 있는지 확인합니다. |
|
|
190
|
-
| `container.dispose()` | `Container` instance method | request child
|
|
271
|
+
| `container.dispose()` | `Container` instance method | parent/root cache보다 request child를 먼저 정리하고 active 시도를 공유하며, 이후 명시적 호출에서 실패한 `onDestroy()` hook만 재시도합니다. |
|
|
191
272
|
| `forwardRef(fn)` | 선언 순서 문제를 위해 조회를 지연하는 토큰 래퍼를 반환합니다. 실제 생성자 순환을 해석 가능하게 만들지는 않습니다. |
|
|
192
273
|
| `isForwardRef(value)` | `forwardRef(...)`가 만든 값인지 확인하는 type guard입니다. 커스텀 provider tooling이 DI token wrapper와 통합될 때 사용할 수 있습니다. |
|
|
193
274
|
| `optional(token)` | 하나의 의존성을 optional로 표시하는 토큰 래퍼를 반환합니다. 누락된 optional dependency는 `undefined`로 해석됩니다. |
|
|
@@ -197,6 +278,7 @@ it('uses a mock database', async () => {
|
|
|
197
278
|
| Token wrapper types | `ForwardRefFn`과 `OptionalToken`은 `forwardRef(...)`와 `optional(...)`이 반환하는 wrapper 값을 설명합니다. |
|
|
198
279
|
| Container helper types | `ClassType`, `Disposable`, `RequestScopeContainer`는 typed provider 선언, teardown hook, request-scope helper 경계를 지원합니다. |
|
|
199
280
|
| Container introspection helper types | `ContainerResolutionState`, `ContainerResolutionCacheOwner`, `ContainerFactoryResolutionState`는 `inspectResolutionState()`가 반환하는 read-only graph/cache view와 controlled cache adoption helper를 설명합니다. |
|
|
281
|
+
| `FactoryResolutionKind` | Root export | container 진단과 introspection을 위해 factory provider가 동기적으로 반환했는지(`sync`) 또는 promise를 통해 반환했는지(`async`)를 분류합니다. |
|
|
200
282
|
| `NormalizedProvider` | 컨테이너가 검증한 provider record shape를 위한 compatibility-only 공개 타입입니다. provider를 작성할 때는 `Provider`나 구체 provider interface를 우선 사용하세요. normalized record 생성은 컨테이너가 소유합니다. |
|
|
201
283
|
| `@fluojs/di/internal` | sibling fluo package가 자체 순회 전에 컨테이너의 canonical provider validation을 적용할 수 있도록 `validateProviderInputs(...)`를 노출하는 package-integration seam입니다. 애플리케이션 코드는 계속 `Container`를 통해 provider를 등록해야 합니다. |
|
|
202
284
|
| `DiErrorContext` | DI error에 붙는 구조화된 context입니다. 로그와 테스트가 token, scope, module, dependency chain, hint를 검사할 수 있게 합니다. |
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
<p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
|
|
4
4
|
|
|
5
|
+
Node.js support is `>=24.0.0 <27`. See [Node.js support and migration](../../docs/reference/node-support.md) before upgrading.
|
|
6
|
+
|
|
5
7
|
Minimal token-based dependency injection container powering every fluo application.
|
|
6
8
|
|
|
7
9
|
## Table of Contents
|
|
@@ -10,8 +12,10 @@ Minimal token-based dependency injection container powering every fluo applicati
|
|
|
10
12
|
- [When to Use](#when-to-use)
|
|
11
13
|
- [Quick Start](#quick-start)
|
|
12
14
|
- [Key Capabilities](#key-capabilities)
|
|
15
|
+
- [NestJS Scope and Optional Dependency Migration](#nestjs-scope-and-optional-dependency-migration)
|
|
13
16
|
- [Circular Dependency Handling](#circular-dependency-handling)
|
|
14
17
|
- [Testing and Mocking](#testing-and-mocking)
|
|
18
|
+
- [Internal Package Integrations](#internal-package-integrations)
|
|
15
19
|
- [Troubleshooting](#troubleshooting)
|
|
16
20
|
- [Public API](#public-api)
|
|
17
21
|
- [Related Packages](#related-packages)
|
|
@@ -75,11 +79,59 @@ fluo DI supports four provider shapes:
|
|
|
75
79
|
- **Request**: Instance is created once per `createRequestScope()` call.
|
|
76
80
|
- **Transient**: A new instance is created every time it is resolved.
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
A singleton provider must not depend on a request-scoped provider. That mismatch throws `ScopeMismatchError` before any provider factory or constructor in the graph runs, and the check covers single, alias (`useExisting`), and multi-provider registrations. A singleton that injects a multi token fails the same way when any contribution under that token is request-scoped, so no partial contribution set is materialized first.
|
|
83
|
+
|
|
84
|
+
During disposal, each container tears down successfully materialized cached instances in reverse creation order across single-provider and multi-provider caches, so dependents are destroyed before their dependencies. Each container first recursively tears down live request-scope children it owns, so disposing a non-root request scope also closes nested request scopes before its own request cache. Root disposal then continues with root-owned singleton cleanup even if one or more child disposals fail. When multiple child/root disposals fail, `dispose()` reports an `AggregateError` so callers can inspect every shutdown failure without losing cleanup progress.
|
|
85
|
+
|
|
86
|
+
Starting `dispose()` is terminal for `resolve()`, `register()`, `override()`, and `createRequestScope()`. Concurrent callers share the active disposal attempt. If an `onDestroy()` hook fails, the container retains only that failed hook for a later explicit `dispose()` retry, preserving child-before-parent/root and reverse-creation ordering. Hooks that completed successfully are never run again, and disposal becomes idempotent after every retained hook succeeds.
|
|
87
|
+
|
|
88
|
+
#### Disposal retry ownership
|
|
89
|
+
|
|
90
|
+
Disposal retries follow five ownership rules:
|
|
91
|
+
|
|
92
|
+
1. Calling public `child.dispose()` directly detaches the request child from its parent graph after the active attempt settles, even when retained `onDestroy()` hooks failed.
|
|
93
|
+
2. A retained child reference can call `dispose()` again to retry only that child's failed hooks. Successful sibling hooks are not repeated.
|
|
94
|
+
3. A child first reached through parent or root disposal remains parent-tracked after failure, so a later parent or root `dispose()` retries it before retained hooks in the parent or root.
|
|
95
|
+
4. Concurrent direct and parent callers share one active attempt. The caller that starts the shared attempt sets its direct or parent ownership, and later callers cannot change it.
|
|
96
|
+
5. A later direct retry detaches a parent-retained child after settlement, even when that retry fails.
|
|
97
|
+
|
|
98
|
+
Executable evidence lives in `packages/di/src/container-disposal-ownership.test.ts` for graph ownership and `packages/di/src/container-disposal-retry.test.ts` for failed-hook ordering and idempotency.
|
|
99
|
+
|
|
100
|
+
### Migrating disposal from 2.x to 3.x
|
|
101
|
+
|
|
102
|
+
In `@fluojs/di` 2.x, a failed container-managed `onDestroy()` hook was attempted once. In 3.x, a later explicit `Container.dispose()` call or application/application-context `close()` that reaches the same container retries only hooks that failed. Hooks that already completed successfully remain exactly-once. Before upgrading, make cleanup hooks that can fail safe to attempt again: preserve enough state to finish partial cleanup, tolerate resources that were already released, and surface a repeated failure to the shutdown caller.
|
|
103
|
+
|
|
104
|
+
Direct `child.dispose()` now detaches the request child from its parent after the attempt settles, including a failed attempt. Retain the child reference when the direct caller must inspect or retry that failure. A failure from parent- or root-started disposal remains owned by the parent hierarchy until cleanup succeeds or a later direct child attempt settles. When direct and parent callers overlap, the caller that starts the shared attempt owns those detach and retry semantics.
|
|
79
105
|
|
|
80
106
|
### Provider Overrides
|
|
81
107
|
|
|
82
|
-
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 in the current container and already-materialized request-scope descendants, and dispose stale instances before the next replacement resolution continues. 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.
|
|
108
|
+
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 in the current container and already-materialized request-scope descendants, and dispose stale instances before the next replacement resolution continues. 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. An override call is atomic: the whole batch is validated before any registration or cache changes, so a rejected call leaves every provider, cached instance, and disposal ownership exactly as it was.
|
|
109
|
+
|
|
110
|
+
### Container Construction Boundary
|
|
111
|
+
|
|
112
|
+
`new Container()` is the only supported public construction form, and it always creates a root container that owns its own singleton cache. Child request scopes are package-owned: parent linkage, the request-scope flag, and singleton-cache sharing use a private construction path reachable only through `createRequestScope()`.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const root = new Container();
|
|
116
|
+
const requestScope = root.createRequestScope();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Supplying constructor arguments is rejected. The emitted declaration accepts no assignable argument type, and at runtime a caller-supplied argument throws `ContainerResolutionError` rather than producing a container with borrowed cache ownership.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Rejected: child-scope wiring is package-owned.
|
|
123
|
+
Reflect.construct(Container, [root]);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Migrating container construction from 2.x to 3.x
|
|
127
|
+
|
|
128
|
+
In `@fluojs/di` 2.x, the emitted `Container` declaration exposed `parent`, `requestScopeEnabled`, and `singletonCache` constructor parameters even though caller-supplied child wiring was never a supported application workflow. In 3.x that surface is sealed: the constructor accepts no arguments, and passing any argument throws `ContainerResolutionError`.
|
|
129
|
+
|
|
130
|
+
Zero-argument `new Container()` and `createRequestScope()` are unchanged, so supported code needs no migration. If you constructed child containers directly, replace that call with `parent.createRequestScope()`, which supplies the same parent linkage, request-scope flag, and shared root singleton cache while keeping disposal ownership intact.
|
|
131
|
+
|
|
132
|
+
Executable evidence lives in `packages/di/src/container-construction-boundary.test.ts`.
|
|
133
|
+
|
|
134
|
+
A failed stale `onDestroy()` hook follows the same retained-retry contract as ordinary disposal. The next resolution on an observing container surfaces that failure once so the replacement can continue, and the failed instance stays retained by the container that scheduled its cleanup until a later explicit `dispose()` on that container invokes the hook again. Stale hooks that already completed successfully are never repeated.
|
|
83
135
|
|
|
84
136
|
### Request Scoping
|
|
85
137
|
Isolated containers can be created to handle per-request state without polluting the root container.
|
|
@@ -91,7 +143,28 @@ const scopedService = await requestContainer.resolve(RequestScopedService);
|
|
|
91
143
|
|
|
92
144
|
Request-scope containers may resolve providers from their parent chain, but request-owned registrations must not introduce new singleton providers. Register singleton providers on the root container before creating request scopes. If a request scope needs local additions, declare them with `scope: 'request'`/`Scope.REQUEST` or use `override()` for an explicit request-local replacement. The same rule applies to multi providers: default-scope multi providers belong on the root container, while request-local multi providers must opt into request scope or be replaced through `override()`.
|
|
93
145
|
|
|
94
|
-
Provider objects are validated at registration time: every object provider must include a
|
|
146
|
+
Provider objects are validated at registration time: every object provider must include a string, symbol, or constructable class `provide` token and exactly one strategy (`useClass`, `useValue`, `useFactory`, or `useExisting`). Alias providers require the same valid token forms for `useExisting`. For class providers, an omitted or `undefined` `inject` value falls back to the `useClass` `@Inject(...)` metadata; any other explicit `inject` value must be an array containing valid tokens or well-formed `forwardRef(...)` / `optional(...)` wrappers. Value providers must omit `inject`; declaring it as an own property is rejected even when its value is `undefined`. Explicit `scope` values must be `singleton`, `request`, or `transient`. Invalid provider shapes throw `InvalidProviderError` before they can affect the container graph.
|
|
147
|
+
|
|
148
|
+
## NestJS Scope and Optional Dependency Migration
|
|
149
|
+
|
|
150
|
+
NestJS `@Injectable({ scope: Scope.REQUEST })` and `@Injectable({ scope: Scope.TRANSIENT })` map to a fluo provider with `@Scope('request')` / `@Scope('transient')`, or an explicit provider `scope: 'request'` / `scope: 'transient'`. Singleton remains the default.
|
|
151
|
+
|
|
152
|
+
fluo does not implement NestJS scope bubbling. Resolve request-scoped providers from a `createRequestScope()` child container: root resolution throws `RequestScopeResolutionError`, and a singleton that depends on a request-scoped provider throws `ScopeMismatchError`.
|
|
153
|
+
|
|
154
|
+
NestJS `@Optional()` maps to `optional(Token)` in a class-level `@Inject(...)` list or a provider `inject` array. `optional(...)` is a token wrapper, not a decorator, and a missing registration resolves to `undefined`.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { Inject, Scope } from '@fluojs/core';
|
|
158
|
+
import { optional } from '@fluojs/di';
|
|
159
|
+
|
|
160
|
+
class AuditLogger {}
|
|
161
|
+
|
|
162
|
+
@Scope('request')
|
|
163
|
+
@Inject(optional(AuditLogger))
|
|
164
|
+
class RequestAuditService {
|
|
165
|
+
constructor(private readonly auditLogger: AuditLogger | undefined) {}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
95
168
|
|
|
96
169
|
## Circular Dependency Handling
|
|
97
170
|
|
|
@@ -167,6 +240,14 @@ it('uses a mock database', async () => {
|
|
|
167
240
|
});
|
|
168
241
|
```
|
|
169
242
|
|
|
243
|
+
## Internal Package Integrations
|
|
244
|
+
|
|
245
|
+
`@fluojs/di/internal` is a typed integration seam for first-party framework
|
|
246
|
+
packages. It resolves one ordered `multi: true` contribution through the
|
|
247
|
+
owning container, preserving the container's scope, cache, cycle, ordering,
|
|
248
|
+
and disposal semantics. Application code must use `Container.resolve(...)`;
|
|
249
|
+
contribution indexes are not part of the root `Container` API.
|
|
250
|
+
|
|
170
251
|
## Troubleshooting
|
|
171
252
|
|
|
172
253
|
### CircularDependencyError
|
|
@@ -179,15 +260,15 @@ Ensure all required providers are registered in the container. If you use `creat
|
|
|
179
260
|
|
|
180
261
|
| Surface | Kind | Description |
|
|
181
262
|
|---|---|---|
|
|
182
|
-
| `Container` | Root export | The main DI container class. |
|
|
263
|
+
| `Container` | Root export | The main DI container class. `new Container()` takes no arguments and creates a root container; child request scopes are package-owned and created with `createRequestScope()`. Supplying constructor arguments throws `ContainerResolutionError`. |
|
|
183
264
|
| `container.register(...providers)` | `Container` instance method | Registers one or more providers. |
|
|
184
|
-
| `container.override(...providers)` | `Container` instance method | Replaces existing providers, invalidates cached instances, and ensures stale instance disposal settles before the next replacement resolution continues. |
|
|
265
|
+
| `container.override(...providers)` | `Container` instance method | Replaces existing providers atomically per call, invalidates cached instances, and ensures stale instance disposal settles before the next replacement resolution continues. |
|
|
185
266
|
| `container.resolve<T>(token)` | `Container` instance method | Asynchronously resolves a token to an instance. |
|
|
186
267
|
| `container.inspectResolutionState()` | `Container` instance method | Exposes the supported framework-owned container introspection seam for testing/tooling helpers that must preserve cache ownership through snapshot read-only map views, frozen provider records, and controlled cache adoption. Prefer `has(...)` and `resolve(...)` for application code. |
|
|
187
|
-
| `container.createRequestScope()` | `Container` instance method | Creates a child container for request-scoped dependencies. |
|
|
268
|
+
| `container.createRequestScope()` | `Container` instance method | Creates a child container for request-scoped dependencies. This is the only supported path to parent-linked, request-scope-enabled containers that share the root singleton cache. |
|
|
188
269
|
| `container.has(token)` | `Container` instance method | Checks if a token is registered in the container or its parents. |
|
|
189
270
|
| `container.hasRequestScopedDependency(token)` | `Container` instance method | Checks whether resolving a token may require a request-scope container because its provider graph contains request-scoped dependencies or is cyclic. |
|
|
190
|
-
| `container.dispose()` | `Container` instance method | Disposes request children
|
|
271
|
+
| `container.dispose()` | `Container` instance method | Disposes request children before parent/root caches, shares an active attempt, and retries only failed `onDestroy()` hooks on a later explicit call. |
|
|
191
272
|
| `forwardRef(fn)` | Returns a token wrapper that defers lookup for declaration-order issues; it does not make constructor dependency cycles resolvable. |
|
|
192
273
|
| `isForwardRef(value)` | Type guard for values produced by `forwardRef(...)`; useful when integrating custom provider tooling with DI token wrappers. |
|
|
193
274
|
| `optional(token)` | Returns a token wrapper that marks one dependency as optional; missing optional dependencies resolve to `undefined`. |
|
|
@@ -197,6 +278,7 @@ Ensure all required providers are registered in the container. If you use `creat
|
|
|
197
278
|
| Token wrapper types | `ForwardRefFn` and `OptionalToken` describe the wrapper values returned by `forwardRef(...)` and `optional(...)`. |
|
|
198
279
|
| Container helper types | `ClassType`, `Disposable`, and `RequestScopeContainer` support typed provider declarations, teardown hooks, and request-scope helper boundaries. |
|
|
199
280
|
| Container introspection helper types | `ContainerResolutionState`, `ContainerResolutionCacheOwner`, and `ContainerFactoryResolutionState` describe the read-only graph/cache views and controlled cache adoption helpers returned by `inspectResolutionState()`. |
|
|
281
|
+
| `FactoryResolutionKind` | Root export | Classifies whether a factory provider returned synchronously (`sync`) or through a promise (`async`) for container diagnostics and introspection. |
|
|
200
282
|
| `NormalizedProvider` | Compatibility-only public type for the container's validated provider record shape. Prefer authoring providers with `Provider` or the specific provider interfaces; the container owns normalized record construction. |
|
|
201
283
|
| `@fluojs/di/internal` | Package-integration seam exposing `validateProviderInputs(...)` so sibling fluo packages can apply the container's canonical provider validation before their own traversal. Application code should continue to register providers through `Container`. |
|
|
202
284
|
| `DiErrorContext` | Structured context attached to DI errors so logs and tests can inspect tokens, scopes, modules, dependency chains, and hints. |
|
package/dist/container.d.ts
CHANGED
|
@@ -42,14 +42,15 @@ export interface ContainerResolutionState {
|
|
|
42
42
|
* Scope-aware dependency injection container for Fluo providers.
|
|
43
43
|
*/
|
|
44
44
|
export declare class Container {
|
|
45
|
-
private
|
|
46
|
-
private readonly requestScopeEnabled;
|
|
45
|
+
#private;
|
|
47
46
|
private readonly registrations;
|
|
48
47
|
private readonly multiRegistrations;
|
|
49
48
|
private readonly multiOverriddenTokens;
|
|
50
49
|
private requestCache;
|
|
51
50
|
private multiRequestCache;
|
|
52
51
|
private readonly multiSingletonCache;
|
|
52
|
+
private readonly materializedCachePromises;
|
|
53
|
+
private readonly pendingDisposables;
|
|
53
54
|
private readonly staleDisposalTasks;
|
|
54
55
|
private readonly singletonCache;
|
|
55
56
|
private readonly forwardRefTokenCache;
|
|
@@ -63,7 +64,18 @@ export declare class Container {
|
|
|
63
64
|
private disposed;
|
|
64
65
|
private trackedByParent;
|
|
65
66
|
private graphRevision;
|
|
66
|
-
|
|
67
|
+
private readonly parent;
|
|
68
|
+
private readonly requestScopeEnabled;
|
|
69
|
+
/**
|
|
70
|
+
* Creates a root container that owns its own singleton cache.
|
|
71
|
+
*
|
|
72
|
+
* Child request scopes are package-owned and must be created with
|
|
73
|
+
* {@link Container.createRequestScope}; caller-supplied parent, request-scope,
|
|
74
|
+
* or singleton-cache wiring is rejected.
|
|
75
|
+
*
|
|
76
|
+
* @throws {ContainerResolutionError} When any constructor argument is supplied.
|
|
77
|
+
*/
|
|
78
|
+
constructor(...construction: never[]);
|
|
67
79
|
/**
|
|
68
80
|
* Registers providers in the current container scope.
|
|
69
81
|
*
|
|
@@ -84,10 +96,16 @@ export declare class Container {
|
|
|
84
96
|
* set — the whole set is replaced. If you need to preserve other entries, re-register them
|
|
85
97
|
* together with the replacement in one `override()` call.
|
|
86
98
|
*
|
|
99
|
+
* **Batch atomicity**: the whole batch is validated before any registration or cache is touched,
|
|
100
|
+
* so a rejected `override()` call leaves every provider, cached instance, and disposal ownership
|
|
101
|
+
* exactly as it was before the call.
|
|
102
|
+
*
|
|
87
103
|
* @param providers Provider definitions that should replace existing registrations for each token.
|
|
88
104
|
* @returns The same container instance for fluent override chains.
|
|
89
105
|
* @throws {ContainerResolutionError} When called after the container was disposed.
|
|
106
|
+
* @throws {ScopeMismatchError} When a request-scope override would introduce a new singleton token.
|
|
90
107
|
* @throws {InvalidProviderError} When a provider definition is structurally invalid.
|
|
108
|
+
* @throws {DuplicateProviderError} When one token mixes single and multi replacements or repeats a single replacement.
|
|
91
109
|
*/
|
|
92
110
|
override(...providers: Provider[]): this;
|
|
93
111
|
/**
|
|
@@ -137,13 +155,23 @@ export declare class Container {
|
|
|
137
155
|
* @throws {CircularDependencyError} When provider dependency resolution detects a cycle.
|
|
138
156
|
*/
|
|
139
157
|
resolve<T>(token: Token<T>): Promise<T>;
|
|
158
|
+
private resolveMultiContribution;
|
|
140
159
|
/**
|
|
141
160
|
* Disposes cached instances and nested request scopes.
|
|
142
161
|
*
|
|
162
|
+
* Concurrent callers share the active disposal attempt. After a failed attempt,
|
|
163
|
+
* a later explicit call retries only `onDestroy()` hooks that did not complete;
|
|
164
|
+
* successfully completed hooks are never repeated. Disposal remains terminal
|
|
165
|
+
* for registration, resolution, overrides, and child-scope creation. A directly
|
|
166
|
+
* disposed child owns its remaining retries after that attempt settles, while a
|
|
167
|
+
* parent-started failed attempt remains owned by the parent hierarchy.
|
|
168
|
+
*
|
|
143
169
|
* @returns A promise that settles after all cached disposable instances are torn down.
|
|
144
170
|
* @throws {Error} Propagates one or more disposal errors (`AggregateError` when multiple failures occur).
|
|
145
171
|
*/
|
|
146
172
|
dispose(): Promise<void>;
|
|
173
|
+
private disposeFromParent;
|
|
174
|
+
private disposeWithOrigin;
|
|
147
175
|
private disposeAll;
|
|
148
176
|
private isDisposedInHierarchy;
|
|
149
177
|
private hasMulti;
|
|
@@ -167,8 +195,7 @@ export declare class Container {
|
|
|
167
195
|
private resolveExistingProviderTarget;
|
|
168
196
|
private resolveScopedOrSingletonInstance;
|
|
169
197
|
private getCachedScopedOrSingletonInstance;
|
|
170
|
-
private
|
|
171
|
-
private shouldResolveMultiProviderFromRoot;
|
|
198
|
+
private cacheOwnerFor;
|
|
172
199
|
private resolveDepToken;
|
|
173
200
|
private withTokenInChain;
|
|
174
201
|
private root;
|
|
@@ -195,12 +222,18 @@ export declare class Container {
|
|
|
195
222
|
private collectDisposableInstances;
|
|
196
223
|
private disposeInstancesInReverseOrder;
|
|
197
224
|
private clearDisposalCaches;
|
|
225
|
+
private trackCacheMaterialization;
|
|
198
226
|
private currentLineageRevision;
|
|
199
227
|
private readCachedPlan;
|
|
200
228
|
private writePlanCache;
|
|
201
229
|
private advanceGraphRevision;
|
|
202
230
|
private clearResolutionPlanCaches;
|
|
203
231
|
private assertStaleDisposalsSettled;
|
|
232
|
+
private retainedStaleDisposalTasks;
|
|
233
|
+
private hasRetainedStaleDisposalTasksInSubtree;
|
|
234
|
+
private releaseNonOwnerStaleTaskObservers;
|
|
235
|
+
private releaseNonOwnerStaleTaskObserversInSubtree;
|
|
236
|
+
private retryFailedStaleDisposals;
|
|
204
237
|
private scheduleStaleDisposal;
|
|
205
238
|
private throwDisposalErrors;
|
|
206
239
|
private collectDisposalError;
|
|
@@ -209,6 +242,7 @@ export declare class Container {
|
|
|
209
242
|
private assertSingletonDependencyScopes;
|
|
210
243
|
private findRequestScopedDependency;
|
|
211
244
|
private findRequestScopedDependencyToken;
|
|
245
|
+
private findRequestScopedMultiContribution;
|
|
212
246
|
private resolveEffectiveProvider;
|
|
213
247
|
private resolveProviderDependencyToken;
|
|
214
248
|
private resolveForwardRefToken;
|
package/dist/container.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
|