@fnioc/di 2.0.0 → 4.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.md +76 -28
- package/dist/index.d.ts +297 -242
- package/dist/index.js +203 -131
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,10 @@
|
|
|
1
|
+
type Func$1<in Args extends readonly any[] = any[], out Return = any> = (...args: Args) => Return;
|
|
2
|
+
|
|
1
3
|
interface Ctor$1<in Args extends readonly any[] = any[], out Instance = any> {
|
|
2
4
|
new(...args: Args): Instance;
|
|
3
5
|
prototype: Instance;
|
|
4
6
|
}
|
|
5
7
|
|
|
6
|
-
/**
|
|
7
|
-
* The hole sentinel: marks a constructor parameter as caller-supplied rather
|
|
8
|
-
* than container-resolved.
|
|
9
|
-
*
|
|
10
|
-
* Used in signatures to communicate "this position is a hole — the factory
|
|
11
|
-
* caller supplies this argument, not the DI container." Detected by identity
|
|
12
|
-
* (`slot === hole`), and `null` is the sentinel value: it is exactly what the
|
|
13
|
-
* transformer emits for a hole slot, so the authoring surface and the lowered
|
|
14
|
-
* output agree on one representation.
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* @signature("pkg:ILogger", hole, "pkg:IDb")
|
|
19
|
-
* class SqlRepo { constructor(log: ILogger, tableName: string, db: IDb) { ... } }
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
declare const hole: null;
|
|
23
|
-
|
|
24
8
|
/**
|
|
25
9
|
* Anything dependency metadata can be attached to: a class constructor (its
|
|
26
10
|
* deps are the ctor parameters) or a factory function (its deps are the call
|
|
@@ -28,7 +12,7 @@ declare const hole: null;
|
|
|
28
12
|
* `never[]` rest keeps any concrete function assignable here regardless of its
|
|
29
13
|
* own parameter list.
|
|
30
14
|
*/
|
|
31
|
-
type DepTarget = Ctor$1 |
|
|
15
|
+
type DepTarget = Ctor$1 | Func$1<never[], unknown>;
|
|
32
16
|
/**
|
|
33
17
|
* A stable string identifying an interface — the DI key.
|
|
34
18
|
*
|
|
@@ -38,12 +22,16 @@ type DepTarget = Ctor$1 | ((...args: never[]) => unknown);
|
|
|
38
22
|
type Token = string;
|
|
39
23
|
/**
|
|
40
24
|
* Marks a constructor parameter to be injected as a *factory* producing the
|
|
41
|
-
* registered token
|
|
42
|
-
*
|
|
43
|
-
*
|
|
25
|
+
* registered type token, rather than a resolved instance. The factory's own
|
|
26
|
+
* call signature is determined by the caller-supplied `params` list.
|
|
27
|
+
*
|
|
28
|
+
* `type` is the token of the produced type T (replaces the former `.factory` field).
|
|
29
|
+
* `params` is the complete, authored-order list of caller-supplied parameter tokens;
|
|
30
|
+
* when present it pins the factory shape so it no longer drifts with registration state.
|
|
44
31
|
*/
|
|
45
32
|
interface FactoryRef {
|
|
46
|
-
readonly
|
|
33
|
+
readonly type: Token;
|
|
34
|
+
readonly params?: readonly Token[];
|
|
47
35
|
}
|
|
48
36
|
/**
|
|
49
37
|
* Marks a parameter to be injected with the live resolution scope itself,
|
|
@@ -56,20 +44,28 @@ interface FactoryRef {
|
|
|
56
44
|
interface ScopeRef {
|
|
57
45
|
readonly scope: true;
|
|
58
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* A set of alternative dependency slots tried in declaration order (first
|
|
49
|
+
* resolvable member wins). If no member is resolvable, resolution throws.
|
|
50
|
+
* Each member is itself a `DepSlot` — nesting is allowed.
|
|
51
|
+
*/
|
|
52
|
+
interface Union {
|
|
53
|
+
readonly union: readonly DepSlot[];
|
|
54
|
+
}
|
|
59
55
|
/**
|
|
60
56
|
* One positional slot in a constructor / factory signature:
|
|
61
57
|
* - a `Token` string — a container-resolved dependency,
|
|
62
|
-
* -
|
|
63
|
-
* - a `
|
|
64
|
-
* - a `
|
|
58
|
+
* - a `FactoryRef` — a factory-injected parameter (see `FactoryRef`),
|
|
59
|
+
* - a `ScopeRef` — the live resolution scope (see `ScopeRef`), or
|
|
60
|
+
* - a `Union` — member-level alternatives tried in order.
|
|
65
61
|
*/
|
|
66
|
-
type DepSlot = Token |
|
|
62
|
+
type DepSlot = Token | FactoryRef | ScopeRef | Union;
|
|
67
63
|
/**
|
|
68
64
|
* Per-constructor dependency metadata stored in the global WeakMap.
|
|
69
65
|
*
|
|
70
66
|
* `signatures` is an array of arrays: each element is one constructor signature
|
|
71
|
-
* (for overload support). `signatures[i][j]` is the `DepSlot` — a token,
|
|
72
|
-
* `
|
|
67
|
+
* (for overload support). `signatures[i][j]` is the `DepSlot` — a token, a
|
|
68
|
+
* `FactoryRef`, a `ScopeRef`, or a `Union` — for constructor parameter `j` of
|
|
73
69
|
* overload `i`.
|
|
74
70
|
*/
|
|
75
71
|
interface DepRecord {
|
|
@@ -90,7 +86,7 @@ interface DepRecord {
|
|
|
90
86
|
* prototype-chain walk — subclasses do NOT inherit the
|
|
91
87
|
* parent's record).
|
|
92
88
|
* @param signatures An array of signatures; each signature is a positional array
|
|
93
|
-
* of DepSlot (Token |
|
|
89
|
+
* of DepSlot (Token | FactoryRef | ScopeRef | Union) parallel to
|
|
94
90
|
* the target's parameter list.
|
|
95
91
|
*/
|
|
96
92
|
declare function defineDeps(target: DepTarget, signatures: readonly (readonly DepSlot[])[]): void;
|
|
@@ -115,7 +111,7 @@ declare function defineDeps(target: DepTarget, signatures: readonly (readonly De
|
|
|
115
111
|
* }
|
|
116
112
|
* ```
|
|
117
113
|
*/
|
|
118
|
-
declare function signature(...tokens: readonly DepSlot[]):
|
|
114
|
+
declare function signature(...tokens: readonly DepSlot[]): Func$1<[Ctor$1, ClassDecoratorContext], void>;
|
|
119
115
|
|
|
120
116
|
/**
|
|
121
117
|
* The builder returned by `forCtor`. Chainable — each `.signature()` call
|
|
@@ -124,8 +120,8 @@ declare function signature(...tokens: readonly DepSlot[]): (value: Ctor$1, _cont
|
|
|
124
120
|
interface ForCtorBuilder {
|
|
125
121
|
/**
|
|
126
122
|
* Appends one constructor signature (a positional array of DepSlot —
|
|
127
|
-
* Token |
|
|
128
|
-
* `this` for chaining.
|
|
123
|
+
* Token | FactoryRef | ScopeRef | Union) to the ctor's dependency metadata.
|
|
124
|
+
* Returns `this` for chaining.
|
|
129
125
|
*
|
|
130
126
|
* Each `.signature(...)` call is one overload. Chaining two calls is
|
|
131
127
|
* equivalent to stacking two `@signature` decorators.
|
|
@@ -145,6 +141,46 @@ interface ForCtorBuilder {
|
|
|
145
141
|
*/
|
|
146
142
|
declare function forCtor(ctor: Ctor$1): ForCtorBuilder;
|
|
147
143
|
|
|
144
|
+
/**
|
|
145
|
+
* @fnioc/core — the immutable substrate and dependency-metadata format.
|
|
146
|
+
*
|
|
147
|
+
* Exports:
|
|
148
|
+
* - `Token` — string alias for a DI key
|
|
149
|
+
* - `FactoryRef` — marks a signature slot as a factory-injected parameter
|
|
150
|
+
* - `ScopeRef` — marks a signature slot as the live resolution scope
|
|
151
|
+
* - `Union` — member-level alternative slots tried in declaration order
|
|
152
|
+
* - `DepSlot` — one positional slot: Token | FactoryRef | ScopeRef | Union
|
|
153
|
+
* - `Inject` — compile-time brand that pins a token for one arg
|
|
154
|
+
* - `DepTarget` — a ctor or factory function metadata attaches to
|
|
155
|
+
* - `DepRecord` — shape of per-constructor metadata in the WeakMap
|
|
156
|
+
* - `defineDeps` — the single write path into the global WeakMap
|
|
157
|
+
* - `getDeps` — the read path (consumed by @fnioc/di)
|
|
158
|
+
* - `union` — runtime helper: constructs a Union slot from member slots
|
|
159
|
+
* - `isFactoryRef` — type guard for FactoryRef slots
|
|
160
|
+
* - `isScopeRef` — type guard for ScopeRef slots
|
|
161
|
+
* - `isUnionSlot` — type guard for Union slots
|
|
162
|
+
* - `signature` — TC39 class decorator factory
|
|
163
|
+
* - `ForCtorBuilder` — return type of `forCtor`
|
|
164
|
+
* - `forCtor` — fluent free-function for third-party classes
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Constructs a `Union` slot — a set of alternative dependency slots tried in
|
|
169
|
+
* declaration order. The first resolvable member wins; if none is resolvable,
|
|
170
|
+
* resolution throws.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* forCtor(Handler).signature(
|
|
175
|
+
* union("pkg:IRedis", "pkg:IMemoryCache"),
|
|
176
|
+
* "pkg:ILogger",
|
|
177
|
+
* );
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
declare function union(...slots: DepSlot[]): Union;
|
|
181
|
+
|
|
182
|
+
type Func<in Args extends readonly any[] = any[], out Return = any> = (...args: Args) => Return;
|
|
183
|
+
|
|
148
184
|
interface Ctor<in Args extends readonly any[] = any[], out Instance = any> {
|
|
149
185
|
new(...args: Args): Instance;
|
|
150
186
|
prototype: Instance;
|
|
@@ -154,15 +190,15 @@ interface Ctor<in Args extends readonly any[] = any[], out Instance = any> {
|
|
|
154
190
|
* A registration-level factory function. Its parameters are filled by the
|
|
155
191
|
* engine at resolve time, the same way a class constructor's are: a factory
|
|
156
192
|
* WITH a `defineDeps` record has each parameter resolved by its slot (token →
|
|
157
|
-
* resolved instance, `ScopeRef` → the live
|
|
193
|
+
* resolved instance, `ScopeRef` → the live provider, hole → caller-supplied); a
|
|
158
194
|
* factory WITHOUT a record is the plugin-less escape hatch and is called with
|
|
159
|
-
* the live
|
|
195
|
+
* the live provider as its single argument (`(sp) => …`).
|
|
160
196
|
*
|
|
161
197
|
* May be async — it can return a `Promise<T>`. The container never awaits; the
|
|
162
198
|
* Promise flows through the sync resolution channel as a value (§"Async as
|
|
163
199
|
* values"). A consumer that depends on it declares `Promise<T>` and awaits.
|
|
164
200
|
*/
|
|
165
|
-
type Factory =
|
|
201
|
+
type Factory = Func<any[], unknown>;
|
|
166
202
|
/** A class registration: a token bound to a concrete constructor. */
|
|
167
203
|
interface ClassRegistration {
|
|
168
204
|
readonly kind: "class";
|
|
@@ -192,230 +228,240 @@ interface ValueRegistration {
|
|
|
192
228
|
/** Any registration the engine can resolve. */
|
|
193
229
|
type Registration = ClassRegistration | FactoryRegistration | ValueRegistration;
|
|
194
230
|
/**
|
|
195
|
-
* The
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
231
|
+
* The named lifetime tag for a registration. `"singleton"` and `"transient"`
|
|
232
|
+
* are the built-in names; `U` is the user-declared scope-name union (defaults
|
|
233
|
+
* to `"scoped"`). Transient is represented by the ABSENCE of a lifetime tag
|
|
234
|
+
* (`undefined` on the registration), not by the string `"transient"`.
|
|
235
|
+
*/
|
|
236
|
+
type Lifetime<U extends string = "scoped"> = "singleton" | "transient" | U;
|
|
237
|
+
/**
|
|
238
|
+
* The minimal resolution surface — resolve tokens and get factories. Injected
|
|
239
|
+
* into factory parameters typed `Resolver` (and for the plugin-less escape
|
|
240
|
+
* hatch as the sole argument of a record-less factory).
|
|
199
241
|
*
|
|
200
|
-
* `resolve` has
|
|
201
|
-
*
|
|
242
|
+
* `resolve` has two published shapes (the tokenless authoring form
|
|
243
|
+
* `resolve<T>()` is a PURE TYPING contributed by the `@fnioc/transformer`
|
|
244
|
+
* augmentation, not part of di's published surface):
|
|
202
245
|
* - `resolve<T>(token)` — explicit token, typed return.
|
|
203
246
|
* - `resolve(token)` — explicit token, `unknown` return (dynamic).
|
|
204
247
|
*/
|
|
205
|
-
interface
|
|
206
|
-
resolve<T>(): T;
|
|
248
|
+
interface Resolver {
|
|
207
249
|
resolve<T>(token: Token): T;
|
|
208
250
|
resolve(token: Token): unknown;
|
|
209
251
|
/**
|
|
210
|
-
* Returns a FACTORY for
|
|
211
|
-
*
|
|
212
|
-
*
|
|
252
|
+
* Returns a FACTORY for `type` rather than an instance. When `params` is
|
|
253
|
+
* absent or empty, returns a strict zero-arg `() => T` — every ctor slot must
|
|
254
|
+
* resolve from the container. When `params` is present, it is the complete
|
|
255
|
+
* authored-order list of caller-supplied parameter tokens; the returned factory
|
|
256
|
+
* has shape `(...params) => T`. The authored `resolve<(a: A) => T>()` lowers
|
|
257
|
+
* to `resolveFactory("pkg:T", ["pkg:A"])`.
|
|
213
258
|
*/
|
|
214
|
-
resolveFactory(
|
|
215
|
-
|
|
259
|
+
resolveFactory(type: Token, params?: readonly Token[]): unknown;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* The scope-creation surface. Injected into factory parameters typed
|
|
263
|
+
* `ScopeFactory`, and implemented by `ServiceProvider`.
|
|
264
|
+
*/
|
|
265
|
+
interface ScopeFactory<S extends string = string> {
|
|
266
|
+
createScope(...args: "scoped" extends S ? [name?: S] : [name: S]): ServiceProvider<S>;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* @deprecated Use `Resolver` instead. Kept for backwards compatibility.
|
|
270
|
+
*
|
|
271
|
+
* The resolution surface a factory receives — either as an injected `ScopeRef`
|
|
272
|
+
* parameter, or (plugin-less escape hatch) as the sole argument of a
|
|
273
|
+
* record-less factory.
|
|
274
|
+
*/
|
|
275
|
+
interface ResolveScope extends Resolver {
|
|
276
|
+
createScope(name: string): ServiceProvider;
|
|
216
277
|
}
|
|
217
278
|
|
|
218
279
|
/**
|
|
219
|
-
* A node in the
|
|
220
|
-
*
|
|
221
|
-
*
|
|
280
|
+
* A scope frame — a node in the parent-linked chain. Holds this scope's name,
|
|
281
|
+
* its instance cache, an ordered list for disposal, and an optional parent.
|
|
282
|
+
* It does NOT hold registrations (those live sealed on the ServiceProvider).
|
|
222
283
|
*
|
|
223
|
-
* The
|
|
224
|
-
*
|
|
284
|
+
* The special "no frame" on the root ServiceProvider means transient-only /
|
|
285
|
+
* unscoped resolution — attempting to resolve a scoped registration from the
|
|
286
|
+
* root will throw MissingScopeError.
|
|
225
287
|
*/
|
|
226
|
-
declare class Scope
|
|
227
|
-
/** This scope's name
|
|
228
|
-
readonly name:
|
|
229
|
-
/** The parent scope, or
|
|
230
|
-
|
|
231
|
-
/** The builder's base registration map (shared, walked last). */
|
|
232
|
-
private readonly baseRegistrations;
|
|
233
|
-
/**
|
|
234
|
-
* Local override registrations held at this scope (shadow ancestors). Each
|
|
235
|
-
* token maps to a LIST in registration order; the most-recent (last) entry
|
|
236
|
-
* wins, mirroring the builder's service collection.
|
|
237
|
-
*/
|
|
238
|
-
private readonly localRegistrations;
|
|
288
|
+
declare class Scope {
|
|
289
|
+
/** This scope's name — must match the registration's lifetime tag. */
|
|
290
|
+
readonly name: string;
|
|
291
|
+
/** The parent scope, or omitted for the topmost frame. */
|
|
292
|
+
readonly parent?: Scope | undefined;
|
|
239
293
|
/** Instances this scope owns and caches, keyed by token. */
|
|
240
|
-
|
|
294
|
+
readonly cache: Map<Token, unknown>;
|
|
241
295
|
/** Owned instances in construction order — disposed in reverse. */
|
|
242
|
-
|
|
243
|
-
private disposed;
|
|
296
|
+
readonly owned: unknown[];
|
|
244
297
|
constructor(
|
|
245
|
-
/** This scope's name
|
|
246
|
-
name:
|
|
247
|
-
/** The parent scope, or
|
|
248
|
-
parent
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
298
|
+
/** This scope's name — must match the registration's lifetime tag. */
|
|
299
|
+
name: string,
|
|
300
|
+
/** The parent scope, or omitted for the topmost frame. */
|
|
301
|
+
parent?: Scope | undefined);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* The public container surface. Implements `Resolver` (resolve + resolveFactory)
|
|
305
|
+
* and `ScopeFactory` (createScope), plus native `Disposable`/`AsyncDisposable`.
|
|
306
|
+
*
|
|
307
|
+
* `S` is the user-declared scope-name union. The root (`DiBuilder.build()`)
|
|
308
|
+
* has an EMPTY scope slot — it acts as the unscoped root that owns singletons
|
|
309
|
+
* when the root name is "singleton", reached by the first `createScope("singleton")`.
|
|
310
|
+
* Wait — actually the root SP from build() DOES have a scope frame (named after
|
|
311
|
+
* the builder's rootName), exactly as before: `build()` creates a root SP
|
|
312
|
+
* with `new Scope(rootName)` as its frame.
|
|
313
|
+
*/
|
|
314
|
+
declare class ServiceProvider<S extends string = string> implements Resolver, ScopeFactory<S>, Disposable, AsyncDisposable {
|
|
315
|
+
/** The sealed registration map (shared across all providers in the tree). */
|
|
316
|
+
private readonly registrations;
|
|
317
|
+
private disposed;
|
|
259
318
|
/**
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
* this scope and its descendants only, most-recent-wins.
|
|
319
|
+
* The scope frame for this provider. `undefined` means this is the "unscoped"
|
|
320
|
+
* root — a sentinel that exists only for transient-only trees (no build()
|
|
321
|
+
* call sets this to undefined in normal usage; build() always sets a root name).
|
|
264
322
|
*/
|
|
265
|
-
private
|
|
323
|
+
private readonly frame;
|
|
324
|
+
constructor(
|
|
325
|
+
/** The sealed registration map (shared across all providers in the tree). */
|
|
326
|
+
registrations: ReadonlyMap<Token, Registration[]>,
|
|
327
|
+
/** This provider's scope frame, if any. */
|
|
328
|
+
frame?: Scope);
|
|
266
329
|
/**
|
|
267
|
-
*
|
|
268
|
-
*
|
|
330
|
+
* The name of this provider's scope frame. Throws if the provider has no
|
|
331
|
+
* frame (unscoped root). Kept for backwards-compatibility with tests that
|
|
332
|
+
* inspect `root.name`.
|
|
269
333
|
*/
|
|
270
|
-
|
|
334
|
+
get name(): S;
|
|
271
335
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
336
|
+
* Creates a child `ServiceProvider` whose scope frame is a new `Scope` named
|
|
337
|
+
* `name`, parented to this provider's frame (or a top-level frame if this
|
|
338
|
+
* provider is unscoped).
|
|
339
|
+
*
|
|
340
|
+
* Default name `"scoped"` is accepted only when `"scoped"` ∈ S (the
|
|
341
|
+
* conditional-rest-param type ensures this at the call site).
|
|
275
342
|
*/
|
|
276
|
-
|
|
277
|
-
/** Registers a scope-local VALUE override — the instance itself, no lifetime. */
|
|
278
|
-
addValue(token: Token, value: unknown): this;
|
|
343
|
+
createScope(...args: "scoped" extends S ? [name?: S] : [name: S]): ServiceProvider<S>;
|
|
279
344
|
/**
|
|
280
|
-
* Resolves a token to an instance, walking the
|
|
281
|
-
*
|
|
282
|
-
* cycle-detection stack.
|
|
345
|
+
* Resolves a token to an instance, walking the scope chain for the owning
|
|
346
|
+
* frame. The public entry point starts a fresh cycle-detection stack.
|
|
283
347
|
*/
|
|
284
|
-
resolve<T>(): T;
|
|
285
348
|
resolve<T>(token: Token): T;
|
|
286
349
|
resolve(token: Token): unknown;
|
|
287
350
|
/**
|
|
288
|
-
* Returns a FACTORY for `
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
351
|
+
* Returns a FACTORY for `type` rather than an instance. When `params` is
|
|
352
|
+
* absent or empty, returns a strict zero-arg `() => T` — every ctor slot must
|
|
353
|
+
* resolve from the container (an unresolvable slot throws). When `params` is
|
|
354
|
+
* present, it is the complete authored-order list of caller-supplied parameter
|
|
355
|
+
* tokens; the returned factory has shape `(...params) => T`. The authored
|
|
356
|
+
* `resolve<(a: A) => T>()` lowers to `resolveFactory("pkg:T", ["pkg:A"])`.
|
|
294
357
|
*/
|
|
295
|
-
resolveFactory(
|
|
358
|
+
resolveFactory(type: Token, params?: readonly Token[]): unknown;
|
|
296
359
|
/**
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
* later `.add()`/`.add(...)` override beats an earlier one without deletion.
|
|
360
|
+
* Returns the most-recent registration for `token` from the sealed map.
|
|
361
|
+
* The sealed map is shared across all providers in the tree; local overrides
|
|
362
|
+
* are not supported in the new model (scope-local registration is deleted).
|
|
301
363
|
*/
|
|
302
364
|
private lookup;
|
|
303
365
|
/**
|
|
304
|
-
* Finds the nearest ancestor scope (inclusive
|
|
305
|
-
* `
|
|
366
|
+
* Finds the nearest ancestor scope frame (inclusive) whose name matches
|
|
367
|
+
* `scopeName`, walking UP the chain. Returns `undefined` when none matches.
|
|
306
368
|
*/
|
|
307
|
-
private findOwner;
|
|
308
|
-
/** The chain of scope names from
|
|
309
|
-
private chainNames;
|
|
369
|
+
private static findOwner;
|
|
370
|
+
/** The chain of scope names from `vantage` up to the root, for diagnostics. */
|
|
371
|
+
private static chainNames;
|
|
310
372
|
/**
|
|
311
|
-
* The internal resolver. `
|
|
312
|
-
*
|
|
313
|
-
* separate calls.
|
|
373
|
+
* The internal resolver. `vantage` is the scope frame the walk starts from.
|
|
374
|
+
* `stack` is the active resolution path (for cycle detection); it is shared
|
|
375
|
+
* across the whole `resolve()` call but never across separate calls.
|
|
314
376
|
*/
|
|
315
377
|
private resolveWith;
|
|
316
378
|
/**
|
|
317
|
-
* Builds an instance for `registration`. `
|
|
318
|
-
* chain the dependencies are resolved against — THE critical rule.
|
|
319
|
-
* factory override that is the scope passed to the closure; for a class it is
|
|
320
|
-
* the scope its ctor deps resolve relative to.
|
|
379
|
+
* Builds an instance for `registration`. `owningFrame` is the scope frame
|
|
380
|
+
* whose chain the dependencies are resolved against — THE critical rule.
|
|
321
381
|
*/
|
|
322
382
|
private instantiate;
|
|
323
383
|
/**
|
|
324
|
-
* The resolution view
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
* `stack` and resolving relative to THIS (the owning) scope.
|
|
384
|
+
* The resolution view injected for a `ScopeRef` parameter (`Resolver` or
|
|
385
|
+
* `ScopeFactory` typed param). Produces a ServiceProvider-like view that
|
|
386
|
+
* continues the active cycle `stack` and resolves relative to `owningFrame`.
|
|
328
387
|
*/
|
|
329
|
-
private
|
|
388
|
+
private makeProviderView;
|
|
330
389
|
/**
|
|
331
390
|
* Invokes a factory registration under the metadata-vs-scope rule:
|
|
332
391
|
* - factory WITH a `defineDeps` record → resolve each slot (token →
|
|
333
|
-
* resolved instance, `ScopeRef` → the live
|
|
334
|
-
* injected callable) and call `factory(...args)`;
|
|
392
|
+
* resolved instance, `ScopeRef` → the live provider view, `FactoryRef` →
|
|
393
|
+
* an injected callable) and call `factory(...args)`;
|
|
335
394
|
* - factory WITHOUT a record (the plugin-less escape hatch) → call
|
|
336
|
-
* `factory(
|
|
337
|
-
* Deps resolve relative to `
|
|
395
|
+
* `factory(providerView)` with the live provider view as its sole argument.
|
|
396
|
+
* Deps resolve relative to `owningFrame` (the owning scope) — §5.4.
|
|
338
397
|
*/
|
|
339
398
|
private invokeFactory;
|
|
340
399
|
/**
|
|
341
|
-
* Constructs a class instance
|
|
342
|
-
*
|
|
343
|
-
*
|
|
400
|
+
* Constructs a class instance, resolving its constructor dependencies
|
|
401
|
+
* relative to `owningFrame`. Performs greedy signature selection over the
|
|
402
|
+
* ctor's DepRecord, then fills each slot:
|
|
344
403
|
*
|
|
345
|
-
* - a string token → resolved through
|
|
404
|
+
* - a string token → resolved through the owning frame's chain (selection
|
|
346
405
|
* guarantees every string-token slot here is resolvable);
|
|
347
406
|
* - a `FactoryRef` → injected as a callable (see `makeFactory`);
|
|
348
|
-
* - a `ScopeRef` → the live
|
|
349
|
-
*
|
|
350
|
-
* A hole never reaches here on a direct resolve: it is an unresolvable token,
|
|
351
|
-
* so selection rejects any signature carrying one (falling to a shorter
|
|
352
|
-
* optional-overload, or throwing). Holes are filled by the caller only when
|
|
353
|
-
* the class is a factory target — see `buildPartitioned`.
|
|
407
|
+
* - a `ScopeRef` → the live provider view.
|
|
354
408
|
*/
|
|
355
409
|
private construct;
|
|
356
410
|
/**
|
|
357
411
|
* Builds the callable injected for a `FactoryRef` parameter.
|
|
358
412
|
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
413
|
+
* When `ref.params` is absent or empty, the factory is STRICT: every ctor slot
|
|
414
|
+
* of the target must resolve from the container. An unresolvable slot throws at
|
|
415
|
+
* build time (via `selectSignature`). The result is a zero-arg `() => T` that
|
|
416
|
+
* respects the target's registered lifetime.
|
|
417
|
+
*
|
|
418
|
+
* When `ref.params` is present, it is the COMPLETE authored-order list of
|
|
419
|
+
* caller-supplied parameter tokens. The caller-supplied set is pinned to those
|
|
420
|
+
* tokens (by first-occurrence left-to-right matching against ctor slots). A
|
|
421
|
+
* slot token that appears in `params` is caller-supplied even if it is also
|
|
422
|
+
* registered (caller wins). A slot that is neither claimed by `params` nor
|
|
423
|
+
* resolvable from the container → error. The factory shape is exactly
|
|
424
|
+
* `(...params) => T`; a fresh instance is built on every call (bypassing the
|
|
425
|
+
* instance cache — caller args differ per call so caching would be wrong).
|
|
365
426
|
*
|
|
366
427
|
* Lifetime semantics:
|
|
367
|
-
* - A ZERO-ARG
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
* instance on every call; a transient target yields a fresh one.
|
|
371
|
-
* - A PARAMETERIZED factory (the target has holes / unregistered params
|
|
372
|
-
* filled per call) constructs a FRESH instance on every call and BYPASSES
|
|
373
|
-
* the instance cache. Caller args differ per call, so caching would be
|
|
374
|
-
* wrong — two calls with different arguments must not collapse to one
|
|
375
|
-
* cached instance.
|
|
428
|
+
* - A ZERO-ARG (no-params) factory routes through the normal `resolve` path
|
|
429
|
+
* and RESPECTS the target's registered lifetime.
|
|
430
|
+
* - A PARAMETERIZED factory constructs a FRESH instance every call.
|
|
376
431
|
*
|
|
377
|
-
* The closure captures `
|
|
378
|
-
*
|
|
379
|
-
* factory-holding instance, exactly as a direct resolve would — so a factory
|
|
380
|
-
* captured by a singleton that tries to build a request-scoped target still
|
|
381
|
-
* throws `MissingScopeError` when invoked.
|
|
432
|
+
* The closure captures `owningFrame`. §5.4 holds at call time: the target's
|
|
433
|
+
* deps resolve relative to the scope that owns the factory-holding instance.
|
|
382
434
|
*/
|
|
383
435
|
private makeFactory;
|
|
384
436
|
/**
|
|
385
|
-
* Builds a factory target
|
|
386
|
-
*
|
|
387
|
-
* `
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
437
|
+
* Builds a factory target with the params-driven caller-supplied partition.
|
|
438
|
+
*
|
|
439
|
+
* `callerParams` is the authored-order list of tokens whose values are
|
|
440
|
+
* supplied by the caller (from the `FactoryRef.params` list). Each ctor slot
|
|
441
|
+
* whose token appears in `callerParams` (first-occurrence left-to-right match)
|
|
442
|
+
* takes the corresponding `callArgs` value; every other slot resolves from the
|
|
443
|
+
* container. A slot that is neither claimed nor resolvable → error (the factory
|
|
444
|
+
* cannot be built). A claimed slot that is also registered → caller wins.
|
|
445
|
+
*
|
|
446
|
+
* Always builds a fresh result — a parameterized factory bypasses the instance
|
|
447
|
+
* cache. Runs on a fresh cycle stack since the factory is invoked outside the
|
|
448
|
+
* original resolve.
|
|
449
|
+
*
|
|
450
|
+
* `signature` may be `undefined` when the target has no DepRecord (zero-arg
|
|
451
|
+
* ctor or record-less factory) — in that case args is empty.
|
|
393
452
|
*/
|
|
394
453
|
private buildPartitioned;
|
|
395
454
|
/**
|
|
396
455
|
* Greedy signature selection. Scans signatures longest → shortest and returns
|
|
397
456
|
* the first SATISFIABLE one. A slot is satisfiable when it is:
|
|
398
457
|
*
|
|
399
|
-
* - a `FactoryRef` — always satisfiable; injected as a callable
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
* - a `ScopeRef` — always satisfiable; filled with the live scope; or
|
|
404
|
-
* - a string token whose registration is resolvable in this (the owning)
|
|
405
|
-
* scope's chain.
|
|
458
|
+
* - a `FactoryRef` — always satisfiable; injected as a callable;
|
|
459
|
+
* - a `ScopeRef` — always satisfiable; filled with the live provider view;
|
|
460
|
+
* - a `Union` — satisfiable iff at least one member is resolvable; or
|
|
461
|
+
* - a string token whose registration exists in the sealed map.
|
|
406
462
|
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
* carries a shorter "without that arg" overload (emitted by the transformer);
|
|
410
|
-
* greedy selection falls to it when the longer one can't be satisfied, so the
|
|
411
|
-
* default / `undefined` applies. A required unfilled param has no such shorter
|
|
412
|
-
* overload and surfaces a `NoSatisfiableSignatureError` (build it via a
|
|
413
|
-
* factory instead). A signature is satisfiable iff every string-token slot is
|
|
414
|
-
* resolvable.
|
|
415
|
-
*
|
|
416
|
-
* - Equal-arity ties break by registration order (the order signatures appear
|
|
417
|
-
* in the DepRecord), which `sort`'s stability preserves.
|
|
418
|
-
* - None satisfiable ⇒ throw naming the unsatisfiable tokens.
|
|
463
|
+
* An unregistered string token is not satisfiable. Equal-arity ties break by
|
|
464
|
+
* registration order. None satisfiable ⇒ throw naming the unsatisfiable tokens.
|
|
419
465
|
*/
|
|
420
466
|
private selectSignature;
|
|
421
467
|
/**
|
|
@@ -423,19 +469,37 @@ declare class Scope<Scopes extends string = string> implements ResolveScope {
|
|
|
423
469
|
* there is no resolvability gate: a target's unregistered tokens are not
|
|
424
470
|
* unsatisfiable — they are the factory's caller-supplied parameters. So the
|
|
425
471
|
* choice is purely the longest signature, equal-arity ties broken by
|
|
426
|
-
* registration order
|
|
427
|
-
* caller has already checked `signatures.length > 0`).
|
|
472
|
+
* registration order.
|
|
428
473
|
*/
|
|
429
474
|
private selectTargetSignature;
|
|
430
475
|
/**
|
|
431
|
-
* True when `slot` is a registered string token
|
|
432
|
-
*
|
|
433
|
-
*
|
|
476
|
+
* True when `slot` is a registered string token in the sealed map. A
|
|
477
|
+
* `FactoryRef`, `ScopeRef`, or `Union` is not tested here — use
|
|
478
|
+
* `isResolvableSlot` for a full slot check.
|
|
434
479
|
*/
|
|
435
480
|
private isResolvable;
|
|
436
481
|
/**
|
|
437
|
-
*
|
|
438
|
-
*
|
|
482
|
+
* True when a slot is resolvable in ANY form:
|
|
483
|
+
* - `FactoryRef` / `ScopeRef` — always satisfiable (injected);
|
|
484
|
+
* - `Union` — satisfiable iff at least one member is resolvable (recursive);
|
|
485
|
+
* - string token — registered in the sealed map.
|
|
486
|
+
*/
|
|
487
|
+
private isResolvableSlot;
|
|
488
|
+
/**
|
|
489
|
+
* Resolves a `Union` slot: tries each member in declaration order and returns
|
|
490
|
+
* the first resolvable one. If no member is resolvable, throws
|
|
491
|
+
* `NoSatisfiableUnionError`.
|
|
492
|
+
*/
|
|
493
|
+
private resolveUnion;
|
|
494
|
+
/**
|
|
495
|
+
* Resolves a single `DepSlot` to its value, dispatching on slot kind.
|
|
496
|
+
* Used by `resolveUnion` to recurse into members.
|
|
497
|
+
*/
|
|
498
|
+
private resolveSlot;
|
|
499
|
+
/**
|
|
500
|
+
* Closes this provider synchronously, disposing the instances its scope frame
|
|
501
|
+
* owns in REVERSE construction order. Only native `Disposable` instances are
|
|
502
|
+
* disposed. NO cascade to child scopes.
|
|
439
503
|
*
|
|
440
504
|
* Throws `AsyncDisposalRequiredError` if any owned instance is a Promise
|
|
441
505
|
* (thenable) — a pending Promise cannot be disposed synchronously; the caller
|
|
@@ -443,9 +507,9 @@ declare class Scope<Scopes extends string = string> implements ResolveScope {
|
|
|
443
507
|
*/
|
|
444
508
|
dispose(): void;
|
|
445
509
|
/**
|
|
446
|
-
* Closes this
|
|
447
|
-
* first (so an async factory's result settles before teardown), then
|
|
448
|
-
* owned instances in REVERSE construction order — honoring both
|
|
510
|
+
* Closes this provider asynchronously. Awaits each owned Promise-valued
|
|
511
|
+
* instance first (so an async factory's result settles before teardown), then
|
|
512
|
+
* disposes owned instances in REVERSE construction order — honoring both
|
|
449
513
|
* `Symbol.asyncDispose` and `Symbol.dispose`. Idempotent.
|
|
450
514
|
*/
|
|
451
515
|
disposeAsync(): Promise<void>;
|
|
@@ -467,23 +531,17 @@ declare class Scope<Scopes extends string = string> implements ResolveScope {
|
|
|
467
531
|
*/
|
|
468
532
|
interface AddBuilder<Scopes extends string> {
|
|
469
533
|
/**
|
|
470
|
-
* Attaches the lifetime. Must name a declared
|
|
471
|
-
*
|
|
472
|
-
* Two call shapes, by design (PRD §7):
|
|
473
|
-
* - AUTHORED `.as<"singleton">()` — the scope name is a TYPE argument; the
|
|
474
|
-
* `S extends Scopes` bound is the compile-time captive-misconfiguration
|
|
475
|
-
* guard. No value argument is passed; this form is never executed (the
|
|
476
|
-
* transformer rewrites it before it runs).
|
|
477
|
-
* - LOWERED `.as("singleton")` — the transformer rewrites the type
|
|
478
|
-
* argument to a value argument. This is the form the engine executes; the
|
|
479
|
-
* runtime reads the scope from the value arg.
|
|
534
|
+
* Attaches the lifetime — the RUNTIME (lowered) form. Must name a declared
|
|
535
|
+
* scope.
|
|
480
536
|
*
|
|
481
|
-
* `
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
|
|
486
|
-
|
|
537
|
+
* `.as("singleton")` is what the engine executes: the transformer rewrites the
|
|
538
|
+
* authored type-arg form (`.as<"singleton">()`) to this value-arg form before
|
|
539
|
+
* runtime, and a plugin-less caller writes it directly. The AUTHORED type-arg
|
|
540
|
+
* form (`.as<S extends Scopes>(): void`) is a PURE TYPING contributed by the
|
|
541
|
+
* `@fnioc/transformer` augmentation — it is not part of di's published surface,
|
|
542
|
+
* so it only type-checks when the transformer's types are in the program.
|
|
543
|
+
*/
|
|
544
|
+
as(scope: Scopes): void;
|
|
487
545
|
}
|
|
488
546
|
/**
|
|
489
547
|
* The registration builder.
|
|
@@ -527,18 +585,6 @@ declare class DiBuilder<Root extends string = "singleton", Children extends stri
|
|
|
527
585
|
* trailing `.as()` leaves the base (transient) registration in place.
|
|
528
586
|
*/
|
|
529
587
|
private appendScoped;
|
|
530
|
-
/**
|
|
531
|
-
* Type-only authoring overloads — the forms the transformer rewrites FROM:
|
|
532
|
-
* - `add<I>(C)` → `add("token", C)` (class)
|
|
533
|
-
* - `add<I>(fn)` → `addFactory("token", fn)` (factory; the transformer
|
|
534
|
-
* knows the arg is a function and routes it to `addFactory`).
|
|
535
|
-
* The ctor is typed `Ctor<any[], I>` (plain construct signature, so an
|
|
536
|
-
* abstract class is rejected); the factory is any `(...args) => I`. Neither
|
|
537
|
-
* runs post-transform — they exist purely so type-driven authoring
|
|
538
|
-
* type-checks before lowering.
|
|
539
|
-
*/
|
|
540
|
-
add<I>(ctor: Ctor<any[], I>): AddBuilder<Root | Children>;
|
|
541
|
-
add<I>(factory: (...args: any[]) => I): AddBuilder<Root | Children>;
|
|
542
588
|
/**
|
|
543
589
|
* Class registration — a string token bound to a concrete constructor. The
|
|
544
590
|
* runtime form: what the transformer emits for a class, and what a
|
|
@@ -550,29 +596,30 @@ declare class DiBuilder<Root extends string = "singleton", Children extends stri
|
|
|
550
596
|
* runtime form the transformer emits for an authored `add<I>(fn)`, and what a
|
|
551
597
|
* plugin-less caller writes directly.
|
|
552
598
|
*
|
|
553
|
-
* Parameter injection follows the metadata rule (see `
|
|
599
|
+
* Parameter injection follows the metadata rule (see `ServiceProvider`): a
|
|
554
600
|
* factory WITH a `defineDeps` record (emitted by the transformer) has each
|
|
555
601
|
* parameter injected by its slot; a record-less factory (the plugin-less
|
|
556
|
-
* escape hatch) is called with the live
|
|
557
|
-
* => T` and `
|
|
602
|
+
* escape hatch) is called with the live provider — type it `(sp: Resolver)
|
|
603
|
+
* => T` and `sp.resolve(...)` its own deps. Returns the `.as(scope?)`
|
|
558
604
|
* continuation so a factory caches at a named scope exactly like a class.
|
|
559
605
|
*/
|
|
560
|
-
addFactory(token: Token, factory:
|
|
606
|
+
addFactory(token: Token, factory: Func<[Resolver], unknown>): AddBuilder<Root | Children>;
|
|
561
607
|
/**
|
|
562
608
|
* Value registration — an already-built instance, no deps and no lifetime.
|
|
563
609
|
* Separate from `add` because a value may itself be a function (a callable
|
|
564
610
|
* service), which is structurally indistinguishable from a factory inside one
|
|
565
|
-
* overload.
|
|
611
|
+
* overload. The authoring form `addValue<I>(v)` (which lowers to
|
|
612
|
+
* `addValue("token", v)`) is a PURE TYPING contributed by the
|
|
613
|
+
* `@fnioc/transformer` augmentation, not part of di's published surface.
|
|
566
614
|
*/
|
|
567
|
-
addValue<I>(value: I): void;
|
|
568
615
|
addValue(token: Token, value: unknown): void;
|
|
569
616
|
/**
|
|
570
|
-
* Mints the root
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
617
|
+
* Mints the root ServiceProvider with a SEALED copy of the registration map.
|
|
618
|
+
* Sealing (deep-freezing the map and each per-token list) ensures that any
|
|
619
|
+
* `.add()` call on the builder after `build()` cannot mutate what the root
|
|
620
|
+
* and its descendants see — the container's view is fixed at construction time.
|
|
574
621
|
*/
|
|
575
|
-
build():
|
|
622
|
+
build(): ServiceProvider<Root | Children>;
|
|
576
623
|
}
|
|
577
624
|
|
|
578
625
|
/** Base class for every error the container raises. */
|
|
@@ -639,6 +686,14 @@ declare class FactoryTargetError extends DiError {
|
|
|
639
686
|
readonly reason: "unregistered" | "not-a-class";
|
|
640
687
|
constructor(factoryToken: Token, reason: "unregistered" | "not-a-class");
|
|
641
688
|
}
|
|
689
|
+
/**
|
|
690
|
+
* A `Union` slot was encountered during resolution but none of its member slots
|
|
691
|
+
* was resolvable. Resolution cannot proceed without at least one registered member.
|
|
692
|
+
*/
|
|
693
|
+
declare class NoSatisfiableUnionError extends DiError {
|
|
694
|
+
readonly members: readonly DepSlot[];
|
|
695
|
+
constructor(members: readonly DepSlot[]);
|
|
696
|
+
}
|
|
642
697
|
/**
|
|
643
698
|
* Sync `dispose()` was called on a scope that owns a Promise-valued (thenable)
|
|
644
699
|
* cached instance. A pending Promise cannot be disposed synchronously — the
|
|
@@ -648,5 +703,5 @@ declare class AsyncDisposalRequiredError extends DiError {
|
|
|
648
703
|
constructor();
|
|
649
704
|
}
|
|
650
705
|
|
|
651
|
-
export { AsyncDisposalRequiredError, CircularDependencyError, DiBuilder, DiError, FactoryTargetError, MissingMetadataError, MissingScopeError, NoSatisfiableSignatureError, Scope, UnregisteredTokenError, defineDeps, forCtor,
|
|
652
|
-
export type { AddBuilder, ClassRegistration, Ctor, DepRecord, Factory, FactoryRegistration, ForCtorBuilder, Registration, ResolveScope, Token, ValueRegistration };
|
|
706
|
+
export { AsyncDisposalRequiredError, CircularDependencyError, DiBuilder, DiError, FactoryTargetError, MissingMetadataError, MissingScopeError, NoSatisfiableSignatureError, NoSatisfiableUnionError, Scope, ServiceProvider, UnregisteredTokenError, defineDeps, forCtor, signature, union };
|
|
707
|
+
export type { AddBuilder, ClassRegistration, Ctor, DepRecord, Factory, FactoryRegistration, ForCtorBuilder, Lifetime, Registration, ResolveScope, Resolver, ScopeFactory, Token, Union, ValueRegistration };
|