@fnioc/di 1.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/dist/index.d.ts CHANGED
@@ -1,8 +1,633 @@
1
- export { DiBuilder } from "./builder.js";
2
- export type { AddBuilder } from "./builder.js";
3
- export { Scope } from "./scope.js";
4
- export type { Ctor, Factory, OverrideSpec, Registration, ClassRegistration, FactoryRegistration, ValueRegistration, ResolveScope, } from "./types.js";
5
- export { DiError, UnregisteredTokenError, MissingScopeError, MissingMetadataError, NoSatisfiableSignatureError, FactoryTargetError, CircularDependencyError, AsyncDisposalRequiredError, } from "./errors.js";
6
- export { signature, forCtor, hole } from "@fnioc/core";
7
- export type { Token, DepRecord, ForCtorBuilder } from "@fnioc/core";
8
- //# sourceMappingURL=index.d.ts.map
1
+ type Func$1<in Args extends readonly any[] = any[], out Return = any> = (...args: Args) => Return;
2
+
3
+ interface Ctor$1<in Args extends readonly any[] = any[], out Instance = any> {
4
+ new(...args: Args): Instance;
5
+ prototype: Instance;
6
+ }
7
+
8
+ /**
9
+ * The hole sentinel: marks a constructor parameter as caller-supplied rather
10
+ * than container-resolved.
11
+ *
12
+ * Used in signatures to communicate "this position is a hole — the factory
13
+ * caller supplies this argument, not the DI container." Detected by identity
14
+ * (`slot === hole`), and `null` is the sentinel value: it is exactly what the
15
+ * transformer emits for a hole slot, so the authoring surface and the lowered
16
+ * output agree on one representation.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * @signature("pkg:ILogger", hole, "pkg:IDb")
21
+ * class SqlRepo { constructor(log: ILogger, tableName: string, db: IDb) { ... } }
22
+ * ```
23
+ */
24
+ declare const hole: null;
25
+
26
+ /**
27
+ * Anything dependency metadata can be attached to: a class constructor (its
28
+ * deps are the ctor parameters) or a factory function (its deps are the call
29
+ * parameters). Both are objects, so both are valid global-WeakMap keys. The
30
+ * `never[]` rest keeps any concrete function assignable here regardless of its
31
+ * own parameter list.
32
+ */
33
+ type DepTarget = Ctor$1 | Func$1<never[], unknown>;
34
+ /**
35
+ * A stable string identifying an interface — the DI key.
36
+ *
37
+ * No branding, no literal types. Generated by the transformer from a TypeScript
38
+ * type at compile time; referenced by hand when using the manual authoring surfaces.
39
+ */
40
+ type Token = string;
41
+ /**
42
+ * Marks a constructor parameter to be injected as a *factory* producing the
43
+ * registered token `factory`, rather than a resolved instance. The factory's
44
+ * own call signature is the target ctor's unregistered params, partitioned at
45
+ * resolve time.
46
+ */
47
+ interface FactoryRef {
48
+ readonly factory: Token;
49
+ }
50
+ /**
51
+ * Marks a parameter to be injected with the live resolution scope itself,
52
+ * rather than a resolved token. Emitted for a factory parameter whose type is
53
+ * `ResolveScope` — the engine fills the slot with the scope the factory is
54
+ * resolved into, so the factory body can `scope.resolve(...)` / `createScope`
55
+ * dynamically. Only meaningful on registration-level factory functions (a class
56
+ * ctor never receives the scope); on a ctor it would simply never match.
57
+ */
58
+ interface ScopeRef {
59
+ readonly scope: true;
60
+ }
61
+ /**
62
+ * One positional slot in a constructor / factory signature:
63
+ * - a `Token` string — a container-resolved dependency,
64
+ * - the `hole` sentinel — a caller-supplied parameter,
65
+ * - a `FactoryRef` — a factory-injected parameter (see `FactoryRef`), or
66
+ * - a `ScopeRef` — the live resolution scope (see `ScopeRef`).
67
+ */
68
+ type DepSlot = Token | typeof hole | FactoryRef | ScopeRef;
69
+ /**
70
+ * Per-constructor dependency metadata stored in the global WeakMap.
71
+ *
72
+ * `signatures` is an array of arrays: each element is one constructor signature
73
+ * (for overload support). `signatures[i][j]` is the `DepSlot` — a token, the
74
+ * `hole` sentinel, or a `FactoryRef` — for constructor parameter `j` of
75
+ * overload `i`.
76
+ */
77
+ interface DepRecord {
78
+ readonly signatures: readonly (readonly DepSlot[])[];
79
+ }
80
+
81
+ /**
82
+ * The single write path into the global WeakMap.
83
+ *
84
+ * Both the transformer-emitted code and `@signature` / `forCtor` funnel through
85
+ * this function. No other code writes to the store.
86
+ *
87
+ * Merge semantics: appends each incoming signature to the ctor's existing
88
+ * DepRecord, deduping exact-equal signatures (same length + same elements in
89
+ * order). Creates the record from scratch if the ctor is not yet registered.
90
+ *
91
+ * @param target The exact constructor OR factory function to annotate (no
92
+ * prototype-chain walk — subclasses do NOT inherit the
93
+ * parent's record).
94
+ * @param signatures An array of signatures; each signature is a positional array
95
+ * of DepSlot (Token | hole | FactoryRef | ScopeRef) parallel to
96
+ * the target's parameter list.
97
+ */
98
+ declare function defineDeps(target: DepTarget, signatures: readonly (readonly DepSlot[])[]): void;
99
+
100
+ /**
101
+ * TC39 class decorator factory that registers one constructor signature.
102
+ *
103
+ * Stacking multiple `@signature` decorators registers multiple overloads.
104
+ * TypeScript evaluates class decorators bottom-up, so the bottom-most
105
+ * `@signature` call runs first and appends first; tests should assert that
106
+ * ALL signatures are present rather than relying on a specific order.
107
+ *
108
+ * Returns `void` — does not replace the class.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * // Two overloads: one with a logger, one without
113
+ * @signature("pkg:ILogger", "pkg:IDb")
114
+ * @signature("pkg:IDb")
115
+ * class MyService {
116
+ * constructor(logOrDb: ILogger | IDb, db?: IDb) { ... }
117
+ * }
118
+ * ```
119
+ */
120
+ declare function signature(...tokens: readonly DepSlot[]): Func$1<[Ctor$1, ClassDecoratorContext], void>;
121
+
122
+ /**
123
+ * The builder returned by `forCtor`. Chainable — each `.signature()` call
124
+ * appends one overload to the ctor's DepRecord.
125
+ */
126
+ interface ForCtorBuilder {
127
+ /**
128
+ * Appends one constructor signature (a positional array of DepSlot —
129
+ * Token | hole | FactoryRef) to the ctor's dependency metadata. Returns
130
+ * `this` for chaining.
131
+ *
132
+ * Each `.signature(...)` call is one overload. Chaining two calls is
133
+ * equivalent to stacking two `@signature` decorators.
134
+ */
135
+ signature(...tokens: readonly DepSlot[]): ForCtorBuilder;
136
+ }
137
+ /**
138
+ * Fluent free-function for registering dependency metadata on a constructor
139
+ * without using a decorator — useful for third-party classes you do not own.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * forCtor(ThirdPartyService)
144
+ * .signature("pkg:IDb")
145
+ * .signature("pkg:ILogger", "pkg:IDb"); // second overload
146
+ * ```
147
+ */
148
+ declare function forCtor(ctor: Ctor$1): ForCtorBuilder;
149
+
150
+ type Func<in Args extends readonly any[] = any[], out Return = any> = (...args: Args) => Return;
151
+
152
+ interface Ctor<in Args extends readonly any[] = any[], out Instance = any> {
153
+ new(...args: Args): Instance;
154
+ prototype: Instance;
155
+ }
156
+
157
+ /**
158
+ * A registration-level factory function. Its parameters are filled by the
159
+ * engine at resolve time, the same way a class constructor's are: a factory
160
+ * WITH a `defineDeps` record has each parameter resolved by its slot (token →
161
+ * resolved instance, `ScopeRef` → the live provider, hole → caller-supplied); a
162
+ * factory WITHOUT a record is the plugin-less escape hatch and is called with
163
+ * the live provider as its single argument (`(sp) => …`).
164
+ *
165
+ * May be async — it can return a `Promise<T>`. The container never awaits; the
166
+ * Promise flows through the sync resolution channel as a value (§"Async as
167
+ * values"). A consumer that depends on it declares `Promise<T>` and awaits.
168
+ */
169
+ type Factory = Func<any[], unknown>;
170
+ /** A class registration: a token bound to a concrete constructor. */
171
+ interface ClassRegistration {
172
+ readonly kind: "class";
173
+ readonly ctor: Ctor;
174
+ /**
175
+ * The lifetime — the scope name that owns and caches the instance.
176
+ * `undefined` means transient (never cached; a fresh instance per resolve).
177
+ */
178
+ readonly scope: string | undefined;
179
+ }
180
+ /** A factory-function registration — its params are injected like a ctor's. */
181
+ interface FactoryRegistration {
182
+ readonly kind: "factory";
183
+ readonly factory: Factory;
184
+ /**
185
+ * The lifetime — the scope name that owns and caches the result. `undefined`
186
+ * means transient (the factory runs on every resolve). Attached via `.as()`,
187
+ * exactly like a class registration.
188
+ */
189
+ readonly scope: string | undefined;
190
+ }
191
+ /** A value registration — an already-built instance, no lifetime. */
192
+ interface ValueRegistration {
193
+ readonly kind: "value";
194
+ readonly useValue: unknown;
195
+ }
196
+ /** Any registration the engine can resolve. */
197
+ type Registration = ClassRegistration | FactoryRegistration | ValueRegistration;
198
+ /**
199
+ * The named lifetime tag for a registration. `"singleton"` and `"transient"`
200
+ * are the built-in names; `U` is the user-declared scope-name union (defaults
201
+ * to `"scoped"`). Transient is represented by the ABSENCE of a lifetime tag
202
+ * (`undefined` on the registration), not by the string `"transient"`.
203
+ */
204
+ type Lifetime<U extends string = "scoped"> = "singleton" | "transient" | U;
205
+ /**
206
+ * The minimal resolution surface — resolve tokens and get factories. Injected
207
+ * into factory parameters typed `Resolver` (and for the plugin-less escape
208
+ * hatch as the sole argument of a record-less factory).
209
+ *
210
+ * `resolve` has two published shapes (the tokenless authoring form
211
+ * `resolve<T>()` is a PURE TYPING contributed by the `@fnioc/transformer`
212
+ * augmentation, not part of di's published surface):
213
+ * - `resolve<T>(token)` — explicit token, typed return.
214
+ * - `resolve(token)` — explicit token, `unknown` return (dynamic).
215
+ */
216
+ interface Resolver {
217
+ resolve<T>(token: Token): T;
218
+ resolve(token: Token): unknown;
219
+ /**
220
+ * Returns a FACTORY for the token rather than an instance — the resolve-site
221
+ * mirror of a `FactoryRef` ctor param. The authored `resolve<(a: A) => T>()`
222
+ * (a function-typed type arg) lowers to this.
223
+ */
224
+ resolveFactory(token: Token): unknown;
225
+ }
226
+ /**
227
+ * The scope-creation surface. Injected into factory parameters typed
228
+ * `ScopeFactory`, and implemented by `ServiceProvider`.
229
+ */
230
+ interface ScopeFactory<S extends string = string> {
231
+ createScope(...args: "scoped" extends S ? [name?: S] : [name: S]): ServiceProvider<S>;
232
+ }
233
+ /**
234
+ * @deprecated Use `Resolver` instead. Kept for backwards compatibility.
235
+ *
236
+ * The resolution surface a factory receives — either as an injected `ScopeRef`
237
+ * parameter, or (plugin-less escape hatch) as the sole argument of a
238
+ * record-less factory.
239
+ */
240
+ interface ResolveScope extends Resolver {
241
+ createScope(name: string): ServiceProvider;
242
+ }
243
+
244
+ /**
245
+ * A scope frame — a node in the parent-linked chain. Holds this scope's name,
246
+ * its instance cache, an ordered list for disposal, and an optional parent.
247
+ * It does NOT hold registrations (those live sealed on the ServiceProvider).
248
+ *
249
+ * The special "no frame" on the root ServiceProvider means transient-only /
250
+ * unscoped resolution — attempting to resolve a scoped registration from the
251
+ * root will throw MissingScopeError.
252
+ */
253
+ declare class Scope {
254
+ /** This scope's name — must match the registration's lifetime tag. */
255
+ readonly name: string;
256
+ /** The parent scope, or omitted for the topmost frame. */
257
+ readonly parent?: Scope | undefined;
258
+ /** Instances this scope owns and caches, keyed by token. */
259
+ readonly cache: Map<Token, unknown>;
260
+ /** Owned instances in construction order — disposed in reverse. */
261
+ readonly owned: unknown[];
262
+ constructor(
263
+ /** This scope's name — must match the registration's lifetime tag. */
264
+ name: string,
265
+ /** The parent scope, or omitted for the topmost frame. */
266
+ parent?: Scope | undefined);
267
+ }
268
+ /**
269
+ * The public container surface. Implements `Resolver` (resolve + resolveFactory)
270
+ * and `ScopeFactory` (createScope), plus native `Disposable`/`AsyncDisposable`.
271
+ *
272
+ * `S` is the user-declared scope-name union. The root (`DiBuilder.build()`)
273
+ * has an EMPTY scope slot — it acts as the unscoped root that owns singletons
274
+ * when the root name is "singleton", reached by the first `createScope("singleton")`.
275
+ * Wait — actually the root SP from build() DOES have a scope frame (named after
276
+ * the builder's rootName), exactly as before: `build()` creates a root SP
277
+ * with `new Scope(rootName)` as its frame.
278
+ */
279
+ declare class ServiceProvider<S extends string = string> implements Resolver, ScopeFactory<S>, Disposable, AsyncDisposable {
280
+ /** The sealed registration map (shared across all providers in the tree). */
281
+ private readonly registrations;
282
+ private disposed;
283
+ /**
284
+ * The scope frame for this provider. `undefined` means this is the "unscoped"
285
+ * root — a sentinel that exists only for transient-only trees (no build()
286
+ * call sets this to undefined in normal usage; build() always sets a root name).
287
+ */
288
+ private readonly frame;
289
+ constructor(
290
+ /** The sealed registration map (shared across all providers in the tree). */
291
+ registrations: ReadonlyMap<Token, Registration[]>,
292
+ /** This provider's scope frame, if any. */
293
+ frame?: Scope);
294
+ /**
295
+ * The name of this provider's scope frame. Throws if the provider has no
296
+ * frame (unscoped root). Kept for backwards-compatibility with tests that
297
+ * inspect `root.name`.
298
+ */
299
+ get name(): S;
300
+ /**
301
+ * Creates a child `ServiceProvider` whose scope frame is a new `Scope` named
302
+ * `name`, parented to this provider's frame (or a top-level frame if this
303
+ * provider is unscoped).
304
+ *
305
+ * Default name `"scoped"` is accepted only when `"scoped"` ∈ S (the
306
+ * conditional-rest-param type ensures this at the call site).
307
+ */
308
+ createScope(...args: "scoped" extends S ? [name?: S] : [name: S]): ServiceProvider<S>;
309
+ /**
310
+ * Resolves a token to an instance, walking the scope chain for the owning
311
+ * frame. The public entry point starts a fresh cycle-detection stack.
312
+ */
313
+ resolve<T>(token: Token): T;
314
+ resolve(token: Token): unknown;
315
+ /**
316
+ * Returns a FACTORY for `token` rather than an instance — the resolve-site
317
+ * mirror of a `FactoryRef` ctor param. The authored `resolve<(a: A) => T>()`
318
+ * lowers here. The returned callable exposes the target's UNREGISTERED /
319
+ * caller-supplied parameters in order (PRD §7 "Partial / positional
320
+ * factories"); a fully-resolvable target yields a zero-arg lazy factory that
321
+ * respects the target's registered lifetime.
322
+ */
323
+ resolveFactory(token: Token): unknown;
324
+ /**
325
+ * Returns the most-recent registration for `token` from the sealed map.
326
+ * The sealed map is shared across all providers in the tree; local overrides
327
+ * are not supported in the new model (scope-local registration is deleted).
328
+ */
329
+ private lookup;
330
+ /**
331
+ * Finds the nearest ancestor scope frame (inclusive) whose name matches
332
+ * `scopeName`, walking UP the chain. Returns `undefined` when none matches.
333
+ */
334
+ private static findOwner;
335
+ /** The chain of scope names from `vantage` up to the root, for diagnostics. */
336
+ private static chainNames;
337
+ /**
338
+ * The internal resolver. `vantage` is the scope frame the walk starts from.
339
+ * `stack` is the active resolution path (for cycle detection); it is shared
340
+ * across the whole `resolve()` call but never across separate calls.
341
+ */
342
+ private resolveWith;
343
+ /**
344
+ * Builds an instance for `registration`. `owningFrame` is the scope frame
345
+ * whose chain the dependencies are resolved against — THE critical rule.
346
+ */
347
+ private instantiate;
348
+ /**
349
+ * The resolution view injected for a `ScopeRef` parameter (`Resolver` or
350
+ * `ScopeFactory` typed param). Produces a ServiceProvider-like view that
351
+ * continues the active cycle `stack` and resolves relative to `owningFrame`.
352
+ */
353
+ private makeProviderView;
354
+ /**
355
+ * Invokes a factory registration under the metadata-vs-scope rule:
356
+ * - factory WITH a `defineDeps` record → resolve each slot (token →
357
+ * resolved instance, `ScopeRef` → the live provider view, `FactoryRef` →
358
+ * an injected callable) and call `factory(...args)`;
359
+ * - factory WITHOUT a record (the plugin-less escape hatch) → call
360
+ * `factory(providerView)` with the live provider view as its sole argument.
361
+ * Deps resolve relative to `owningFrame` (the owning scope) — §5.4.
362
+ */
363
+ private invokeFactory;
364
+ /**
365
+ * Constructs a class instance, resolving its constructor dependencies
366
+ * relative to `owningFrame`. Performs greedy signature selection over the
367
+ * ctor's DepRecord, then fills each slot:
368
+ *
369
+ * - a string token → resolved through the owning frame's chain (selection
370
+ * guarantees every string-token slot here is resolvable);
371
+ * - a `FactoryRef` → injected as a callable (see `makeFactory`);
372
+ * - a `ScopeRef` → the live provider view.
373
+ */
374
+ private construct;
375
+ /**
376
+ * Builds the callable injected for a `FactoryRef` parameter.
377
+ *
378
+ * The target ctor's signature is partitioned at CALL time against the live
379
+ * registration map: each slot that is a registered token is resolved; each
380
+ * slot that is an unregistered token or a `hole` takes the next
381
+ * caller-supplied argument, positionally. The injected callable therefore
382
+ * exposes only the target's unregistered parameters, in their relative order.
383
+ *
384
+ * Lifetime semantics:
385
+ * - A ZERO-ARG factory routes through the normal `resolve` path, so it
386
+ * RESPECTS the target's registered lifetime.
387
+ * - A PARAMETERIZED factory constructs a FRESH instance on every call and
388
+ * BYPASSES the instance cache. Caller args differ per call, so caching
389
+ * would be wrong.
390
+ *
391
+ * The closure captures `owningFrame`. §5.4 holds at call time: the target's
392
+ * deps resolve relative to the scope that owns the factory-holding instance.
393
+ */
394
+ private makeFactory;
395
+ /**
396
+ * Builds a factory target, partitioning its already-selected signature
397
+ * against the live registration map: a registered token is resolved; a
398
+ * `ScopeRef` is the live provider view; a `FactoryRef` is injected; an
399
+ * unregistered token or a `hole` takes the next caller-supplied argument
400
+ * positionally. A class target is `new`ed, a factory target is called.
401
+ * Always a fresh result — a parameterized factory bypasses the instance cache.
402
+ * Runs on a fresh cycle stack since the factory is invoked outside the
403
+ * original resolve.
404
+ */
405
+ private buildPartitioned;
406
+ /**
407
+ * Greedy signature selection. Scans signatures longest → shortest and returns
408
+ * the first SATISFIABLE one. A slot is satisfiable when it is:
409
+ *
410
+ * - a `FactoryRef` — always satisfiable; injected as a callable;
411
+ * - a `ScopeRef` — always satisfiable; filled with the live provider view; or
412
+ * - a string token whose registration exists in the sealed map.
413
+ *
414
+ * A `hole` (`null`) is NOT satisfiable on a direct resolve. An unregistered
415
+ * string token is also not satisfiable. Equal-arity ties break by registration
416
+ * order. None satisfiable ⇒ throw naming the unsatisfiable tokens.
417
+ */
418
+ private selectSignature;
419
+ /**
420
+ * Greedy signature selection for a FACTORY TARGET. Unlike `selectSignature`,
421
+ * there is no resolvability gate: a target's unregistered tokens are not
422
+ * unsatisfiable — they are the factory's caller-supplied parameters. So the
423
+ * choice is purely the longest signature, equal-arity ties broken by
424
+ * registration order.
425
+ */
426
+ private selectTargetSignature;
427
+ /**
428
+ * True when `slot` is a registered string token in the sealed map. A hole
429
+ * (`null`), `FactoryRef`, or `ScopeRef` is not a registered token, so it is
430
+ * never "resolvable" in this sense.
431
+ */
432
+ private isResolvable;
433
+ /**
434
+ * Closes this provider synchronously, disposing the instances its scope frame
435
+ * owns in REVERSE construction order. Only native `Disposable` instances are
436
+ * disposed. NO cascade to child scopes.
437
+ *
438
+ * Throws `AsyncDisposalRequiredError` if any owned instance is a Promise
439
+ * (thenable) — a pending Promise cannot be disposed synchronously; the caller
440
+ * must use `disposeAsync()`. Idempotent: a second call is a no-op.
441
+ */
442
+ dispose(): void;
443
+ /**
444
+ * Closes this provider asynchronously. Awaits each owned Promise-valued
445
+ * instance first (so an async factory's result settles before teardown), then
446
+ * disposes owned instances in REVERSE construction order — honoring both
447
+ * `Symbol.asyncDispose` and `Symbol.dispose`. Idempotent.
448
+ */
449
+ disposeAsync(): Promise<void>;
450
+ /** Drops owned references after disposal so they can be collected. */
451
+ private clear;
452
+ /** Native `using` support — delegates to `dispose()`. */
453
+ [Symbol.dispose](): void;
454
+ /** Native `await using` support — delegates to `disposeAsync()`. */
455
+ [Symbol.asyncDispose](): Promise<void>;
456
+ }
457
+
458
+ /**
459
+ * The continuation returned by a class `DiBuilder.add`. Carries the just-added
460
+ * registration so `.as()` can attach its lifetime in place. An `.add()` with no
461
+ * trailing `.as()` leaves the registration scopeless ⇒ transient.
462
+ *
463
+ * `Scopes` is threaded so `.as()` only accepts a declared scope name —
464
+ * compile-time captive-misconfiguration guard at the registration site.
465
+ */
466
+ interface AddBuilder<Scopes extends string> {
467
+ /**
468
+ * Attaches the lifetime — the RUNTIME (lowered) form. Must name a declared
469
+ * scope.
470
+ *
471
+ * `.as("singleton")` is what the engine executes: the transformer rewrites the
472
+ * authored type-arg form (`.as<"singleton">()`) to this value-arg form before
473
+ * runtime, and a plugin-less caller writes it directly. The AUTHORED type-arg
474
+ * form (`.as<S extends Scopes>(): void`) is a PURE TYPING contributed by the
475
+ * `@fnioc/transformer` augmentation — it is not part of di's published surface,
476
+ * so it only type-checks when the transformer's types are in the program.
477
+ */
478
+ as(scope: Scopes): void;
479
+ }
480
+ /**
481
+ * The registration builder.
482
+ *
483
+ * `Root` is the root scope's name (the app-lifetime tag singletons bind to);
484
+ * `Children` is the union of declarable child-scope names. The scopes
485
+ * `.as()`/`.createScope` accept are `Root | Children`. `"transient"` is NOT a
486
+ * member — transient is the absence of a scope, not a scope.
487
+ *
488
+ * @example
489
+ * ```ts
490
+ * const services = new DiBuilder<"singleton", "request">();
491
+ * services.add("pkg:ILogger", ConsoleLogger).as("singleton"); // lowered form
492
+ * const root = services.build(); // mints the "singleton" root
493
+ * const logger = root.resolve<ILogger>("pkg:ILogger");
494
+ * const req = root.createScope("request"); // nested child scope
495
+ * ```
496
+ */
497
+ declare class DiBuilder<Root extends string = "singleton", Children extends string = never> {
498
+ /**
499
+ * The service collection: each token maps to a LIST of registrations in
500
+ * registration order. Registering a token appends; resolution picks the
501
+ * most-recent (last) registration. Earlier registrations are retained, which
502
+ * is what lets a later `.add()` override an earlier one without deletion.
503
+ */
504
+ private readonly registrations;
505
+ /**
506
+ * The root scope's runtime name. `Root` is erased at runtime, so the name is
507
+ * captured at construction (defaulting to `"singleton"`, matching the `Root`
508
+ * default). Most callers never set it — the default covers the common
509
+ * `DiBuilder<"singleton", …>` case; pass it only when `Root` is non-default.
510
+ */
511
+ private readonly rootName;
512
+ constructor(rootName?: Root);
513
+ /** Appends a registration to `token`'s list, creating the list on first use. */
514
+ private append;
515
+ /**
516
+ * Appends a scopeless `class`/`factory` base registration and returns the
517
+ * `.as(scope?)` continuation. `.as()` appends a fresh SCOPED copy so the
518
+ * array's last entry wins; a bare `.add(...)`/`.addFactory(...)` with no
519
+ * trailing `.as()` leaves the base (transient) registration in place.
520
+ */
521
+ private appendScoped;
522
+ /**
523
+ * Class registration — a string token bound to a concrete constructor. The
524
+ * runtime form: what the transformer emits for a class, and what a
525
+ * plugin-less caller writes directly. Returns the `.as(scope?)` continuation.
526
+ */
527
+ add(token: Token, ctor: Ctor): AddBuilder<Root | Children>;
528
+ /**
529
+ * Factory registration — a string token bound to a factory function. The
530
+ * runtime form the transformer emits for an authored `add<I>(fn)`, and what a
531
+ * plugin-less caller writes directly.
532
+ *
533
+ * Parameter injection follows the metadata rule (see `ServiceProvider`): a
534
+ * factory WITH a `defineDeps` record (emitted by the transformer) has each
535
+ * parameter injected by its slot; a record-less factory (the plugin-less
536
+ * escape hatch) is called with the live provider — type it `(sp: Resolver)
537
+ * => T` and `sp.resolve(...)` its own deps. Returns the `.as(scope?)`
538
+ * continuation so a factory caches at a named scope exactly like a class.
539
+ */
540
+ addFactory(token: Token, factory: Func<[Resolver], unknown>): AddBuilder<Root | Children>;
541
+ /**
542
+ * Value registration — an already-built instance, no deps and no lifetime.
543
+ * Separate from `add` because a value may itself be a function (a callable
544
+ * service), which is structurally indistinguishable from a factory inside one
545
+ * overload. The authoring form `addValue<I>(v)` (which lowers to
546
+ * `addValue("token", v)`) is a PURE TYPING contributed by the
547
+ * `@fnioc/transformer` augmentation, not part of di's published surface.
548
+ */
549
+ addValue(token: Token, value: unknown): void;
550
+ /**
551
+ * Mints the root ServiceProvider with a SEALED copy of the registration map.
552
+ * Sealing (deep-freezing the map and each per-token list) ensures that any
553
+ * `.add()` call on the builder after `build()` cannot mutate what the root
554
+ * and its descendants see — the container's view is fixed at construction time.
555
+ */
556
+ build(): ServiceProvider<Root | Children>;
557
+ }
558
+
559
+ /** Base class for every error the container raises. */
560
+ declare class DiError extends Error {
561
+ constructor(message: string);
562
+ }
563
+ /**
564
+ * A token was requested but no registration exists for it anywhere in the
565
+ * resolving scope's chain (nor on the builder's base map).
566
+ */
567
+ declare class UnregisteredTokenError extends DiError {
568
+ readonly token: Token;
569
+ constructor(token: Token);
570
+ }
571
+ /**
572
+ * A registration carries a lifetime scope, but no ancestor scope in the
573
+ * resolving chain has a matching name. This is the captive-dependency /
574
+ * misconfiguration detector — the engine never auto-creates a scope to satisfy
575
+ * the lifetime.
576
+ */
577
+ declare class MissingScopeError extends DiError {
578
+ readonly token: Token;
579
+ readonly scope: string;
580
+ readonly availableScopes: readonly string[];
581
+ constructor(token: Token, scope: string, availableScopes: readonly string[]);
582
+ }
583
+ /**
584
+ * A constructor with parameters has no DepRecord in the WeakMap — the
585
+ * transformer never saw it and it was never hand-annotated.
586
+ */
587
+ declare class MissingMetadataError extends DiError {
588
+ readonly token: Token;
589
+ readonly ctorName: string;
590
+ constructor(token: Token, ctorName: string);
591
+ }
592
+ /**
593
+ * A constructor has DepRecord signatures, but none of them is directly
594
+ * satisfiable in the owning scope (every signature names at least one token
595
+ * that is not registered, or contains a hole this phase cannot fill).
596
+ */
597
+ declare class NoSatisfiableSignatureError extends DiError {
598
+ readonly token: Token;
599
+ readonly ctorName: string;
600
+ readonly unsatisfiable: readonly Token[];
601
+ constructor(token: Token, ctorName: string, unsatisfiable: readonly Token[]);
602
+ }
603
+ /**
604
+ * A token reappeared on the active resolution stack — the dependency graph has
605
+ * a cycle. The message includes the full path that closed the loop.
606
+ */
607
+ declare class CircularDependencyError extends DiError {
608
+ readonly path: readonly Token[];
609
+ constructor(path: readonly Token[]);
610
+ }
611
+ /**
612
+ * A constructor parameter is typed as a factory of some token (a `FactoryRef`),
613
+ * but that token cannot be turned into a factory: either it is not registered,
614
+ * or it is registered as a `useValue` / `useFactory` override rather than a
615
+ * class. A factory injects a callable that constructs the target class on
616
+ * demand, so the target must be a class registration.
617
+ */
618
+ declare class FactoryTargetError extends DiError {
619
+ readonly factoryToken: Token;
620
+ readonly reason: "unregistered" | "not-a-class";
621
+ constructor(factoryToken: Token, reason: "unregistered" | "not-a-class");
622
+ }
623
+ /**
624
+ * Sync `dispose()` was called on a scope that owns a Promise-valued (thenable)
625
+ * cached instance. A pending Promise cannot be disposed synchronously — the
626
+ * caller must use `disposeAsync()`.
627
+ */
628
+ declare class AsyncDisposalRequiredError extends DiError {
629
+ constructor();
630
+ }
631
+
632
+ export { AsyncDisposalRequiredError, CircularDependencyError, DiBuilder, DiError, FactoryTargetError, MissingMetadataError, MissingScopeError, NoSatisfiableSignatureError, Scope, ServiceProvider, UnregisteredTokenError, defineDeps, forCtor, hole, signature };
633
+ export type { AddBuilder, ClassRegistration, Ctor, DepRecord, Factory, FactoryRegistration, ForCtorBuilder, Lifetime, Registration, ResolveScope, Resolver, ScopeFactory, Token, ValueRegistration };