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