@rhombus-std/di.core 0.0.0-alpha.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/LICENSE +21 -0
- package/README.md +367 -0
- package/dist/index.d.ts +775 -0
- package/dist/index.js +299 -0
- package/package.json +41 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,775 @@
|
|
|
1
|
+
type Func<in Args extends readonly any[] = any[], out Return = any> = (...args: Args) => Return;
|
|
2
|
+
|
|
3
|
+
interface Ctor<in Args extends readonly any[] = any[], out Instance = any> {
|
|
4
|
+
new(...args: Args): Instance;
|
|
5
|
+
prototype: Instance;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Anything a dependency signature can describe: a class constructor (its deps
|
|
10
|
+
* are the ctor parameters) or a factory function (its deps are the call
|
|
11
|
+
* parameters). The `never[]` rest keeps any concrete function assignable here
|
|
12
|
+
* regardless of its own parameter list.
|
|
13
|
+
*/
|
|
14
|
+
type DepTarget = Ctor | Func<never[], unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* A stable string identifying an interface — the DI key.
|
|
17
|
+
*
|
|
18
|
+
* No branding, no literal types. Generated by the transformer from a TypeScript
|
|
19
|
+
* type at compile time; referenced by hand when using the manual authoring surfaces.
|
|
20
|
+
*/
|
|
21
|
+
type Token = string;
|
|
22
|
+
/**
|
|
23
|
+
* Marks a constructor parameter to be injected as a *factory* producing the
|
|
24
|
+
* registered type token, rather than a resolved instance. The factory's own
|
|
25
|
+
* call signature is determined by the caller-supplied `params` list.
|
|
26
|
+
*
|
|
27
|
+
* `type` is the token of the produced type T (replaces the former `.factory` field).
|
|
28
|
+
* `params` is the complete, authored-order list of caller-supplied parameter tokens;
|
|
29
|
+
* when present it pins the factory shape so it no longer drifts with registration state.
|
|
30
|
+
*/
|
|
31
|
+
interface FactoryRef {
|
|
32
|
+
readonly type: Token;
|
|
33
|
+
readonly params?: readonly Token[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A set of alternative dependency slots tried in declaration order (first
|
|
37
|
+
* resolvable member wins). If no member is resolvable, resolution throws.
|
|
38
|
+
* Each member is itself a `DepSlot` — nesting is allowed.
|
|
39
|
+
*/
|
|
40
|
+
interface Union {
|
|
41
|
+
readonly union: readonly DepSlot[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A SINGULAR (non-union) type that supplies its value directly — no container
|
|
45
|
+
* lookup. Emitted for:
|
|
46
|
+
* - a non-union literal param (`"dev"`, `42`, `true`, `1n`) → its value, and
|
|
47
|
+
* - a whole-type `void` / `undefined` → `undefined`; a whole-type `null` →
|
|
48
|
+
* `null` (a singleton type has exactly one inhabitant, so it is supplied
|
|
49
|
+
* directly, NOT tokenized — Rule 2).
|
|
50
|
+
* The engine injects `value` verbatim. A LITERAL/typed UNION (`"a" | "b"`,
|
|
51
|
+
* `Foo | undefined`) is NOT a `LiteralRef`: a literal union stays a resolved
|
|
52
|
+
* token, and a nullish union is stripped by the optional/overload path. Always
|
|
53
|
+
* satisfiable — the value is self-supplying.
|
|
54
|
+
*
|
|
55
|
+
* NOTE: `value` may legitimately be `undefined` (the `void`/`undefined` case),
|
|
56
|
+
* so a `LiteralRef` is identified by the PRESENCE of the `value` key, never by
|
|
57
|
+
* `value !== undefined`. See `isLiteralRef`.
|
|
58
|
+
*/
|
|
59
|
+
interface LiteralRef {
|
|
60
|
+
readonly value: string | number | boolean | bigint | undefined | null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Marks a parameter to be injected with the TOKEN STRING of one of the
|
|
64
|
+
* registration's type arguments — the `typeof(T)` analog for open-generic
|
|
65
|
+
* templates. `typeArg` is the 1-based hole number (`{ typeArg: 1 }` names the
|
|
66
|
+
* argument bound to `$1`). At close time, substitution replaces the slot with
|
|
67
|
+
* a `LiteralRef` carrying the substituted argument's token string; a raw
|
|
68
|
+
* (unsubstituted) `TypeArgRef` reaching resolution is an error.
|
|
69
|
+
*/
|
|
70
|
+
interface TypeArgRef {
|
|
71
|
+
readonly typeArg: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* One positional slot in a constructor / factory signature:
|
|
75
|
+
* - a `Token` string — a container-resolved dependency (a plain `Resolver`
|
|
76
|
+
* token resolves to the live provider view — see `RESOLVER_TOKEN`),
|
|
77
|
+
* - a `FactoryRef` — a factory-injected parameter (see `FactoryRef`),
|
|
78
|
+
* - a `Union` — member-level alternatives tried in order,
|
|
79
|
+
* - a `LiteralRef` — a singular literal supplying its value directly, or
|
|
80
|
+
* - a `TypeArgRef` — the token string of a type argument (see `TypeArgRef`).
|
|
81
|
+
*/
|
|
82
|
+
type DepSlot = Token | FactoryRef | Union | LiteralRef | TypeArgRef;
|
|
83
|
+
/**
|
|
84
|
+
* Per-constructor dependency metadata carried on a registration.
|
|
85
|
+
*
|
|
86
|
+
* `signatures` is an array of arrays: each element is one constructor signature
|
|
87
|
+
* (for overload support). `signatures[i][j]` is the `DepSlot` — a token, a
|
|
88
|
+
* `FactoryRef`, a `Union`, or a `LiteralRef` — for constructor parameter `j` of
|
|
89
|
+
* overload `i`.
|
|
90
|
+
*/
|
|
91
|
+
interface DepRecord {
|
|
92
|
+
readonly signatures: readonly (readonly DepSlot[])[];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The result of parsing a closed-generic token `base<arg1,arg2>` into its base
|
|
96
|
+
* and top-level args. A pure data shape (the parse routine that produces it is a
|
|
97
|
+
* runtime helper that lives in `@rhombus-std/di`); kept here so the type surface a
|
|
98
|
+
* consumer references stays in the types-only substrate.
|
|
99
|
+
*/
|
|
100
|
+
interface ParsedToken {
|
|
101
|
+
readonly base: Token;
|
|
102
|
+
readonly args: readonly Token[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Compile-time phantom brand that pins a specific token for one constructor or
|
|
107
|
+
* factory parameter, overriding the token the transformer would normally derive.
|
|
108
|
+
*
|
|
109
|
+
* The value type stays `T` — a plain `T` is assignable because the brand
|
|
110
|
+
* property is optional. Zero runtime footprint.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* class Handler {
|
|
115
|
+
* constructor(
|
|
116
|
+
* cache: Inject<ICache, "pkg:redis-cache">, // pinned token
|
|
117
|
+
* log: ILogger, // derived normally
|
|
118
|
+
* ) {}
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
declare const TOK: unique symbol;
|
|
123
|
+
type Inject<T, K extends Token> = T & {
|
|
124
|
+
readonly [TOK]?: K;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Compile-time skolem standing in for the `N`th type argument of an open
|
|
128
|
+
* template (1-based). Writing `add<IRepository<$<1>>>(SqlRepository<$<1>>)` binds
|
|
129
|
+
* the hole; the transformer derives `$N` wherever a Hole-branded type appears.
|
|
130
|
+
*
|
|
131
|
+
* `C` is the constraint carrier: `Hole<1, Entity>` IS an `Entity` (the brand
|
|
132
|
+
* property is optional, so the intersection stays assignable to `C`), which
|
|
133
|
+
* lets a constrained implementation `class Repo<T extends Entity>` accept a
|
|
134
|
+
* hole as its type argument. Zero runtime footprint.
|
|
135
|
+
*/
|
|
136
|
+
declare const HOLE: unique symbol;
|
|
137
|
+
type Hole<N extends number, C = unknown> = C & {
|
|
138
|
+
readonly [HOLE]?: N;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Unbounded sugar for the common unconstrained hole: `$<1>`, `$<2>`, … `$<N>`.
|
|
142
|
+
* `$<N>` is exactly `Hole<N>`; reach for `Hole<N, C>` when the impl's type
|
|
143
|
+
* parameter carries a constraint the skolem must satisfy.
|
|
144
|
+
*/
|
|
145
|
+
type $<N extends number> = Hole<N>;
|
|
146
|
+
/**
|
|
147
|
+
* Compile-time phantom brand marking a constructor parameter that receives the
|
|
148
|
+
* TOKEN STRING of type argument `T` — the `typeof(T)` analog (hence the name).
|
|
149
|
+
* The value type stays `Token` (a plain string is assignable; the brand
|
|
150
|
+
* property is optional).
|
|
151
|
+
*
|
|
152
|
+
* `Typeof<T>` is type-driven: the transformer infers the hole from `T`. The
|
|
153
|
+
* manual counterpart `typeArg(n)` is positional — a plugin-less author names
|
|
154
|
+
* the hole by number.
|
|
155
|
+
*
|
|
156
|
+
* When `T` is a Hole, the transformer emits an open `{ typeArg: N }` slot that
|
|
157
|
+
* substitution closes per registration; when `T` is concrete, it emits the
|
|
158
|
+
* derived token directly as a literal value slot. Zero runtime footprint.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* class SqlRepository<T> {
|
|
163
|
+
* constructor(readonly entityToken: Typeof<T>) {}
|
|
164
|
+
* }
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
declare const ARG: unique symbol;
|
|
168
|
+
type Typeof<T> = Token & {
|
|
169
|
+
readonly [ARG]?: T;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* ONE overload's own non-call properties, carried across the peeling recursion so
|
|
174
|
+
* a callable-with-statics keeps them. `Pick<T, keyof T>` is `{}` for a bare
|
|
175
|
+
* function type and the static side for a constructor type.
|
|
176
|
+
*/
|
|
177
|
+
type OverloadProps<T> = Pick<T, keyof T>;
|
|
178
|
+
/**
|
|
179
|
+
* Peel an intersection of call signatures (an overloaded function type) into a
|
|
180
|
+
* UNION of its individual signatures. The technique (Vojtěch Mašek / type-fest):
|
|
181
|
+
* `infer` matches the LAST signature and emits it, then recurses with the
|
|
182
|
+
* accumulator intersected back in so the next match resolves to the PRECEDING
|
|
183
|
+
* overload. Bounded — each step strips one signature, terminating once the
|
|
184
|
+
* accumulator already subsumes the whole overload set (`TAccumulator extends
|
|
185
|
+
* TOverload`).
|
|
186
|
+
*/
|
|
187
|
+
type OverloadUnionRecursive<TOverload, TAccumulator = unknown> = TOverload extends (...args: infer TArgs) => infer TReturn ? TAccumulator extends TOverload ? never : OverloadUnionRecursive<TAccumulator & TOverload, TAccumulator & ((...args: TArgs) => TReturn) & OverloadProps<TOverload>> | ((...args: TArgs) => TReturn) : never;
|
|
188
|
+
/**
|
|
189
|
+
* The UNION of a function type's individual call-signature overloads. Seeds the
|
|
190
|
+
* recursion with a `() => never` overload hoisted to the FRONT of the
|
|
191
|
+
* intersection (required for the bounded recursion to fire), then excludes that
|
|
192
|
+
* sentinel from the result unless `T` genuinely is `() => never`.
|
|
193
|
+
*/
|
|
194
|
+
type OverloadUnion<T extends (...args: any[]) => any> = Exclude<OverloadUnionRecursive<(() => never) & T>, T extends () => never ? never : () => never>;
|
|
195
|
+
/**
|
|
196
|
+
* Every overload's parameter tuple for a function type `T`, as a union — the
|
|
197
|
+
* overload-faithful analog of the builtin `Parameters<T>`. For a `T` with
|
|
198
|
+
* signatures `(a: A)` and `(a: B, b: C)` this is `[a: A] | [a: B, b: C]`; a
|
|
199
|
+
* single-overload function yields its one tuple.
|
|
200
|
+
*/
|
|
201
|
+
type OverloadedParameters<T extends (...args: any[]) => any> = Parameters<OverloadUnion<T>>;
|
|
202
|
+
/** The construct-signature counterpart of {@link OverloadProps} — the static side. */
|
|
203
|
+
type ConstructorOverloadProps<T> = Pick<T, keyof T>;
|
|
204
|
+
/**
|
|
205
|
+
* The construct-signature counterpart of {@link OverloadUnionRecursive}: peels an
|
|
206
|
+
* intersection of CONSTRUCT signatures (an overloaded constructor type) into a
|
|
207
|
+
* union of its individual signatures. A concrete `new` is used, NOT `abstract
|
|
208
|
+
* new`: intersecting an abstract construct signature with a concrete class's
|
|
209
|
+
* `new` signatures derails overload inference (it collapses to `any`), and the
|
|
210
|
+
* sole consumer — a factory that does `new C(...args)` — needs a concrete
|
|
211
|
+
* constructor anyway.
|
|
212
|
+
*/
|
|
213
|
+
type ConstructorOverloadUnionRecursive<TOverload, TAccumulator = unknown> = TOverload extends new (...args: infer TArgs) => infer TReturn ? TAccumulator extends TOverload ? never : ConstructorOverloadUnionRecursive<TAccumulator & TOverload, TAccumulator & (new (...args: TArgs) => TReturn) & ConstructorOverloadProps<TOverload>> | (new (...args: TArgs) => TReturn) : never;
|
|
214
|
+
/** The construct-signature counterpart of {@link OverloadUnion}. */
|
|
215
|
+
type ConstructorOverloadUnion<T extends new (...args: any[]) => any> = Exclude<ConstructorOverloadUnionRecursive<(new () => never) & T>, T extends new () => never ? never : new () => never>;
|
|
216
|
+
/**
|
|
217
|
+
* Every construct-overload's parameter tuple for a constructor type `T`, as a
|
|
218
|
+
* union — the overload-faithful analog of the builtin `ConstructorParameters<T>`.
|
|
219
|
+
* For a `C` with constructors `(a: A)` and `(a: B, b: C)`,
|
|
220
|
+
* `OverloadedConstructorParameters<typeof C>` is `[a: A] | [a: B, b: C]`; a
|
|
221
|
+
* single-overload ctor yields its one tuple and a zero-arg ctor yields `[]`.
|
|
222
|
+
* Constrained to a concrete (`new`-able) constructor — an abstract class has no
|
|
223
|
+
* constructible instance, and the factory that consumes this must `new` its
|
|
224
|
+
* argument.
|
|
225
|
+
*/
|
|
226
|
+
type OverloadedConstructorParameters<T extends new (...args: any[]) => any> = ConstructorParameters<ConstructorOverloadUnion<T>>;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The continuation returned by a class `ServiceManifest.add`. Carries the just-added
|
|
230
|
+
* registration so `.as()` can attach its lifetime in place. An `.add()` with no
|
|
231
|
+
* trailing `.as()` leaves the registration scopeless ⇒ transient.
|
|
232
|
+
*
|
|
233
|
+
* `Scopes` is threaded so `.as()` only accepts a declared scope name —
|
|
234
|
+
* compile-time guard at the registration site. The authored type-arg form
|
|
235
|
+
* `.as<"scope">()` is DECLARATION-MERGED onto this interface by the
|
|
236
|
+
* `@rhombus-std/di.transformer` augmentation — a pure typing that surfaces only
|
|
237
|
+
* when the transformer is in the program.
|
|
238
|
+
*/
|
|
239
|
+
interface AddBuilder<Scopes extends string> {
|
|
240
|
+
/**
|
|
241
|
+
* Attaches the lifetime — the RUNTIME (lowered) form. Must name a declared
|
|
242
|
+
* scope.
|
|
243
|
+
*
|
|
244
|
+
* `.as("singleton")` is what the engine executes: the transformer rewrites the
|
|
245
|
+
* authored type-arg form (`.as<"singleton">()`) to this value-arg form before
|
|
246
|
+
* runtime, and a plugin-less caller writes it directly. The AUTHORED type-arg
|
|
247
|
+
* form (`.as<S extends Scopes>(): void`) is a PURE TYPING contributed by the
|
|
248
|
+
* `@rhombus-std/di.transformer` augmentation — it is not part of di's published surface,
|
|
249
|
+
* so it only type-checks when the transformer's types are in the program.
|
|
250
|
+
*/
|
|
251
|
+
as(scope: Scopes): void;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* The AUTHORING INTERFACE for the registration collection — the base surface a
|
|
255
|
+
* lib author types a setup function against, and the interface the concrete
|
|
256
|
+
* `ServiceManifestClass` (in this same package) implements. It names the three
|
|
257
|
+
* runtime registration methods (`add` / `addFactory` / `addValue`) plus `build`.
|
|
258
|
+
*
|
|
259
|
+
* It is also the interface-first public surface a di consumer holds: di's public
|
|
260
|
+
* `ServiceManifest` type is `ServiceManifestBase<S, ServiceProvider<S>>` (not the
|
|
261
|
+
* impl class), so the type-driven authoring forms (`add<I>(C)`, `addFactory<I>(fn)`,
|
|
262
|
+
* `addValue<I>(v)`) the `@rhombus-std/di.transformer` DECLARATION-MERGES onto this
|
|
263
|
+
* interface surface on a consumer's `services.add<I>(...)`. An interface picks up
|
|
264
|
+
* those merged overloads; the impl class would not — the same reason the provider
|
|
265
|
+
* surface is an interface.
|
|
266
|
+
*
|
|
267
|
+
* `Provider` is the type `build()` returns. A core-only lib author never calls
|
|
268
|
+
* `build()` (the application does), so it defaults to `unknown`; `@rhombus-std/di`
|
|
269
|
+
* binds it to the concrete `ServiceProvider<Scopes>` when its class implements
|
|
270
|
+
* this interface. Keeping it generic is what lets this interface live in the
|
|
271
|
+
* types-only substrate without referencing di's runtime provider type.
|
|
272
|
+
*/
|
|
273
|
+
interface ServiceManifestBase<Scopes extends string = "singleton", Provider = unknown> {
|
|
274
|
+
/**
|
|
275
|
+
* Class registration — a string token bound to a concrete constructor. The
|
|
276
|
+
* optional third `signatures` arg carries the positional dep signatures ON the
|
|
277
|
+
* registration (a lib author authors them as plain `DepSlot` data literals).
|
|
278
|
+
*/
|
|
279
|
+
add(token: Token, ctor: Ctor, signatures?: readonly (readonly DepSlot[])[]): AddBuilder<Scopes>;
|
|
280
|
+
/**
|
|
281
|
+
* Factory registration — a string token bound to a factory function, its call
|
|
282
|
+
* parameters injected by the optional third `signatures` arg.
|
|
283
|
+
*/
|
|
284
|
+
addFactory(token: Token, factory: Func<any[], unknown>, signatures?: readonly (readonly DepSlot[])[]): AddBuilder<Scopes>;
|
|
285
|
+
/** Value registration — an already-built instance, no deps and no lifetime. */
|
|
286
|
+
addValue(token: Token, value: unknown): void;
|
|
287
|
+
/** Seals the collection and returns the built provider. */
|
|
288
|
+
build(): Provider;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The minimal resolution surface — resolve tokens and get factories. A factory
|
|
293
|
+
* (or ctor) parameter typed `Resolver` is injected with the live provider view:
|
|
294
|
+
* the type derives the intrinsic provider token (`RESOLVER_TOKEN`), which the
|
|
295
|
+
* engine resolves to the view relative to the resolving frame — "I want the
|
|
296
|
+
* provider" is plain DI, no dedicated slot kind.
|
|
297
|
+
*
|
|
298
|
+
* `resolve` has two published shapes here; the tokenless authoring form
|
|
299
|
+
* `resolve<T>()` (and the factory form `resolve<F>()`) is a PURE TYPING the
|
|
300
|
+
* `@rhombus-std/di.transformer` DECLARATION-MERGES onto THIS interface (via
|
|
301
|
+
* `declare module "@rhombus-std/di.core"`), so it lights up only when the
|
|
302
|
+
* transformer is in the TypeScript program. Merging onto the interface (rather
|
|
303
|
+
* than a separate carrier) is what lets both a factory parameter typed `Resolver`
|
|
304
|
+
* AND the `ServiceProvider` interface a consumer holds pick up the authored form
|
|
305
|
+
* — an interface inherits a base interface's merged overloads; a class would not,
|
|
306
|
+
* which is exactly why the public provider surface is this interface, not the
|
|
307
|
+
* impl class.
|
|
308
|
+
* - `resolve<T>(token)` — explicit token, typed return.
|
|
309
|
+
* - `resolve(token)` — explicit token, `unknown` return (dynamic).
|
|
310
|
+
*/
|
|
311
|
+
interface Resolver {
|
|
312
|
+
resolve<T>(token: Token): T;
|
|
313
|
+
resolve(token: Token): unknown;
|
|
314
|
+
/**
|
|
315
|
+
* Resolves asynchronously — the only path that may satisfy `T` via a
|
|
316
|
+
* `Promise<T>` registration. Always returns a Promise; a lookup miss whose
|
|
317
|
+
* honest `Promise<T>` registration exists is awaited and delivers `T`.
|
|
318
|
+
*/
|
|
319
|
+
resolveAsync<T>(token: Token): Promise<T>;
|
|
320
|
+
resolveAsync(token: Token): Promise<unknown>;
|
|
321
|
+
/**
|
|
322
|
+
* Non-throwing resolve — returns the resolved instance, or `undefined` when
|
|
323
|
+
* `token` is UNREGISTERED. Mirrors the reference DI's nullable `GetService<T>`
|
|
324
|
+
* against `resolve`'s throwing `GetRequiredService` (#25). A bare nullable, not
|
|
325
|
+
* a tuple: a resolved service is always a truthy instance, so `undefined`
|
|
326
|
+
* unambiguously means "not registered".
|
|
327
|
+
*
|
|
328
|
+
* Only an unregistered TOKEN yields `undefined`. A registered token whose
|
|
329
|
+
* construction fails for another reason (a missing dependency, a cycle, an
|
|
330
|
+
* async-only construction) throws exactly as `resolve` would — `tryResolve`
|
|
331
|
+
* softens the "is it registered?" miss, nothing else.
|
|
332
|
+
*
|
|
333
|
+
* The tokenless authoring form `tryResolve<T>()` is the pure typing the
|
|
334
|
+
* `@rhombus-std/di.transformer` DECLARATION-MERGES onto this interface.
|
|
335
|
+
* - `tryResolve<T>(token)` — explicit token, typed nullable return.
|
|
336
|
+
* - `tryResolve(token)` — explicit token, `unknown` return (dynamic).
|
|
337
|
+
*/
|
|
338
|
+
tryResolve<T>(token: Token): T | undefined;
|
|
339
|
+
tryResolve(token: Token): unknown;
|
|
340
|
+
/**
|
|
341
|
+
* A token-based registration predicate — `true` when `token` would resolve
|
|
342
|
+
* (a registration exists, directly or via an open-generic closing), `false`
|
|
343
|
+
* otherwise. Mirrors the reference DI's `IServiceProviderIsService.IsService`
|
|
344
|
+
* (#23); being token-based, it also covers the keyed case in one method. Does
|
|
345
|
+
* NOT attempt construction — a registered token whose dependencies are missing
|
|
346
|
+
* still reports `true` (it IS a service; building it is a separate concern).
|
|
347
|
+
*
|
|
348
|
+
* The tokenless authoring form `isService<T>()` is the pure typing the
|
|
349
|
+
* `@rhombus-std/di.transformer` DECLARATION-MERGES onto this interface.
|
|
350
|
+
*/
|
|
351
|
+
isService(token: Token): boolean;
|
|
352
|
+
/**
|
|
353
|
+
* Returns a FACTORY for `type` rather than an instance. When `params` is
|
|
354
|
+
* absent or empty, returns a strict zero-arg `() => T` — every ctor slot must
|
|
355
|
+
* resolve from the container. When `params` is present, it is the complete
|
|
356
|
+
* authored-order list of caller-supplied parameter tokens; the returned factory
|
|
357
|
+
* has shape `(...params) => T`. The authored `resolve<(a: A) => T>()` lowers
|
|
358
|
+
* to `resolveFactory("pkg:T", ["pkg:A"])`.
|
|
359
|
+
*/
|
|
360
|
+
resolveFactory(type: Token, params?: readonly Token[]): unknown;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* The scope-creation surface. Injected into factory parameters typed
|
|
364
|
+
* `ScopeFactory`, and implemented by the provider impl. `createScope` returns
|
|
365
|
+
* the `ServiceProvider` INTERFACE (the abstractions seam), never the impl class.
|
|
366
|
+
*/
|
|
367
|
+
interface ScopeFactory<S extends string = string> {
|
|
368
|
+
createScope(...args: "scoped" extends S ? [name?: S] : [name: S]): ServiceProvider<S>;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* The PUBLIC container surface a consumer holds — the abstractions seam mirroring
|
|
372
|
+
* MEDI's `IServiceProvider`. Composes the resolution surface (`Resolver`, which
|
|
373
|
+
* carries the tokenless authoring forms via `ResolverAuthoring`), scope creation
|
|
374
|
+
* (`ScopeFactory`), and native `Disposable` / `AsyncDisposable`. The concrete
|
|
375
|
+
* `ServiceProviderClass` in `@rhombus-std/di` implements this; `build()` and
|
|
376
|
+
* `createScope()` return it rather than the class so consumers program against
|
|
377
|
+
* the interface.
|
|
378
|
+
*
|
|
379
|
+
* `S` is the user-declared scope-name union.
|
|
380
|
+
*/
|
|
381
|
+
interface ServiceProvider<S extends string = string> extends Resolver, ScopeFactory<S>, Disposable, AsyncDisposable {
|
|
382
|
+
/**
|
|
383
|
+
* The name of this provider's open scope frame. Throws if the provider is
|
|
384
|
+
* frameless (no scope open — e.g. the provider straight from `build()`).
|
|
385
|
+
*/
|
|
386
|
+
readonly name: S;
|
|
387
|
+
/**
|
|
388
|
+
* Closes this provider synchronously, disposing the instances its scope frame
|
|
389
|
+
* owns in reverse construction order. Throws `AsyncDisposalRequiredError` if an
|
|
390
|
+
* owned instance is a pending Promise. Idempotent.
|
|
391
|
+
*/
|
|
392
|
+
dispose(): void;
|
|
393
|
+
/**
|
|
394
|
+
* Closes this provider asynchronously, awaiting owned Promise-valued instances
|
|
395
|
+
* before disposing them in reverse construction order. Idempotent.
|
|
396
|
+
*/
|
|
397
|
+
disposeAsync(): Promise<void>;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* @deprecated Use `Resolver` instead. Kept for backwards compatibility.
|
|
401
|
+
*
|
|
402
|
+
* The resolution surface a factory receives when it declares a provider-typed
|
|
403
|
+
* parameter. Like `Resolver`, its token is intrinsic — the engine fills the
|
|
404
|
+
* parameter with the live provider view — with `createScope` added.
|
|
405
|
+
*/
|
|
406
|
+
interface ResolveScope extends Resolver {
|
|
407
|
+
createScope(name: string): ServiceProvider;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* The named lifetime tag for a registration. `"singleton"` and `"transient"`
|
|
411
|
+
* are the built-in names; `U` is the user-declared scope-name union (defaults
|
|
412
|
+
* to `"scoped"`). Transient is represented by the ABSENCE of a lifetime tag
|
|
413
|
+
* (`undefined` on the registration), not by the string `"transient"`.
|
|
414
|
+
*/
|
|
415
|
+
type Lifetime<U extends string = "scoped"> = "singleton" | "transient" | U;
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* A registration-level factory function. Its parameters are filled by the
|
|
419
|
+
* engine at resolve time, the same way a class constructor's are: each parameter
|
|
420
|
+
* is resolved by its slot (token → resolved instance, provider token → the live
|
|
421
|
+
* provider view, hole → caller-supplied). A factory with no signatures runs with
|
|
422
|
+
* no injected args — it declares the deps it wants, nothing is auto-supplied.
|
|
423
|
+
*
|
|
424
|
+
* May be async — it can return a `Promise<T>`. The container never awaits; the
|
|
425
|
+
* Promise flows through the sync resolution channel as a value (§"Async as
|
|
426
|
+
* values"). A consumer that depends on it declares `Promise<T>` and awaits.
|
|
427
|
+
*/
|
|
428
|
+
type Factory = Func<any[], unknown>;
|
|
429
|
+
/**
|
|
430
|
+
* Builds an instance from its resolved positional args. The single normalized
|
|
431
|
+
* form the three authoring kinds collapse into at registration time:
|
|
432
|
+
* - class → `(...a) => new Ctor(...a)`
|
|
433
|
+
* - value → `() => value`
|
|
434
|
+
* - factory → the factory function itself
|
|
435
|
+
*/
|
|
436
|
+
type Producer = Func<any[], unknown>;
|
|
437
|
+
/**
|
|
438
|
+
* A single normalized registration — ONE "producer" shape for all three
|
|
439
|
+
* authoring kinds (class / value / factory). The builder wraps each into a
|
|
440
|
+
* `produce` closure at registration time (see `Producer`); the engine dispatches
|
|
441
|
+
* on this one shape, calling `produce(...args)` uniformly rather than switching
|
|
442
|
+
* on a `kind` discriminant.
|
|
443
|
+
*/
|
|
444
|
+
interface Registration {
|
|
445
|
+
/** Builds the instance from the resolved positional args (see `Producer`). */
|
|
446
|
+
readonly produce: Producer;
|
|
447
|
+
/**
|
|
448
|
+
* The lifetime — the scope name that owns and caches the instance. `undefined`
|
|
449
|
+
* means transient (never cached; produced fresh per resolve). A value is
|
|
450
|
+
* always transient: a value IS its instance, so ownership/caching is moot and
|
|
451
|
+
* a value that is itself a `Promise` is returned raw, never awaited.
|
|
452
|
+
*/
|
|
453
|
+
readonly scope: string | undefined;
|
|
454
|
+
/**
|
|
455
|
+
* Registration-carried dep signatures — the positional slots that feed
|
|
456
|
+
* `produce`, and the sole signature channel now that the global metadata store
|
|
457
|
+
* is retired. Emitted inline by the transformer (`add`/`addFactory` third arg)
|
|
458
|
+
* and hand-fed by a plugin-less caller. Absent or empty means `produce` takes
|
|
459
|
+
* no injected args (a zero-arg ctor, a value, or a signature-less factory).
|
|
460
|
+
*/
|
|
461
|
+
readonly signatures?: readonly (readonly DepSlot[])[];
|
|
462
|
+
/**
|
|
463
|
+
* The producer's diagnostic name — the ctor / factory name, carried EXPLICITLY
|
|
464
|
+
* because a wrapper closure (`(...a) => new Ctor(...a)`) reports `""` for its
|
|
465
|
+
* own `.name`. Empty string for a value. Feeds the `MissingMetadataError` /
|
|
466
|
+
* `NoSatisfiableSignatureError` diagnostics.
|
|
467
|
+
*/
|
|
468
|
+
readonly name: string;
|
|
469
|
+
/**
|
|
470
|
+
* The original constructor arity (`Ctor.length`), carried EXPLICITLY because a
|
|
471
|
+
* rest-param wrapper reports `0` for its own `.length`. Drives the
|
|
472
|
+
* missing-metadata signal: a signature-less producer whose `arity` is nonzero
|
|
473
|
+
* (a class ctor that needs args) throws `MissingMetadataError`. `0` for a value
|
|
474
|
+
* or a factory — a signature-less factory simply runs with no injected args.
|
|
475
|
+
*/
|
|
476
|
+
readonly arity: number;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* An OPEN registration — a class bound to an open template token whose type
|
|
480
|
+
* arguments are all holes (`pkg:IRepo<$1>`). It never resolves directly;
|
|
481
|
+
* resolving a closed token that misses the exact map matches against these
|
|
482
|
+
* (base + arity + repeated-hole equality, last registered wins), substitutes
|
|
483
|
+
* the closing's arg tokens through the carried signatures, and synthesizes an
|
|
484
|
+
* ordinary class `Registration` (a ctor-wrapping producer) memoized per closed
|
|
485
|
+
* token.
|
|
486
|
+
*/
|
|
487
|
+
interface OpenRegistration {
|
|
488
|
+
/** The full template token as registered (`pkg:IRepo<$1>`). */
|
|
489
|
+
readonly template: Token;
|
|
490
|
+
/** The template's base (`pkg:IRepo`) — the open-table key. */
|
|
491
|
+
readonly base: Token;
|
|
492
|
+
/**
|
|
493
|
+
* The parsed top-level args of the template — each exactly a hole (`$N`).
|
|
494
|
+
* Length is the arity; repeated holes (`["$1","$1"]`) constrain a match to
|
|
495
|
+
* equal arg tokens.
|
|
496
|
+
*/
|
|
497
|
+
readonly pattern: readonly Token[];
|
|
498
|
+
readonly ctor: Ctor;
|
|
499
|
+
/** The lifetime tag, applied per closing. `undefined` means transient. */
|
|
500
|
+
readonly scope: string | undefined;
|
|
501
|
+
/**
|
|
502
|
+
* The template dep signatures (holes and `TypeArgRef`s still open) —
|
|
503
|
+
* substituted per closing. When absent, the closing has no template to
|
|
504
|
+
* substitute (a zero-arg ctor closes to a bare `new Ctor()`).
|
|
505
|
+
*/
|
|
506
|
+
readonly signatures?: readonly (readonly DepSlot[])[];
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* The sealed, immutable snapshot a `ServiceManifestClass` hands to the engine.
|
|
510
|
+
* `ServiceManifestClass.seal()` deep-freezes its registration tables into this
|
|
511
|
+
* shape; `@rhombus-std/di`'s `build()` extension reads it to construct the
|
|
512
|
+
* provider (the engine-constructing half stays in the runtime package). This is
|
|
513
|
+
* the seam that lets the collection live in di.core while provider construction
|
|
514
|
+
* lives in di.
|
|
515
|
+
*/
|
|
516
|
+
interface SealedManifest {
|
|
517
|
+
readonly registrations: ReadonlyMap<Token, readonly Registration[]>;
|
|
518
|
+
readonly openRegistrations: ReadonlyMap<Token, readonly OpenRegistration[]>;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* The registration builder.
|
|
523
|
+
*
|
|
524
|
+
* `Scopes` is the union of declarable scope names — the tags `.as()` and
|
|
525
|
+
* `.createScope()` accept (default `"singleton"`). There is no root: scopes are
|
|
526
|
+
* uniform tags, and `"singleton"` is just a tag you happen to open once at the
|
|
527
|
+
* top. `"transient"` is NOT a member — transient is the absence of a scope, not
|
|
528
|
+
* a scope. A registration whose tagged scope is not open at resolution time
|
|
529
|
+
* resolves transiently (fresh instance, no cache).
|
|
530
|
+
*
|
|
531
|
+
* @example
|
|
532
|
+
* ```ts
|
|
533
|
+
* const services = new ServiceManifest<"singleton" | "request">();
|
|
534
|
+
* services.add("pkg:ILogger", ConsoleLogger).as("singleton"); // lowered form
|
|
535
|
+
* const provider = services.build(); // no frame pre-opened
|
|
536
|
+
* const app = provider.createScope("singleton"); // open the singleton frame
|
|
537
|
+
* const logger = app.resolve<ILogger>("pkg:ILogger");
|
|
538
|
+
* const req = app.createScope("request"); // nested child scope
|
|
539
|
+
* ```
|
|
540
|
+
*
|
|
541
|
+
* NOTE: this is the IMPLEMENTATION class. The public `ServiceManifest` TYPE
|
|
542
|
+
* (below) is the interface consumers hold; the public `ServiceManifest` VALUE
|
|
543
|
+
* (`new ServiceManifest<S>()`) lives in `@rhombus-std/di`, which also patches
|
|
544
|
+
* `build()` onto this prototype. The class is exported so cross-package fluent
|
|
545
|
+
* augmentations can prototype-patch it (their authored typings merge onto the
|
|
546
|
+
* di.core interfaces, never onto this class directly).
|
|
547
|
+
*/
|
|
548
|
+
declare class ServiceManifestClass<Scopes extends string = "singleton"> implements ServiceManifestBase<Scopes, ServiceProvider<Scopes>> {
|
|
549
|
+
#private;
|
|
550
|
+
constructor();
|
|
551
|
+
/**
|
|
552
|
+
* Class registration — a string token bound to a concrete constructor. The
|
|
553
|
+
* runtime form: what the transformer emits for a class, and what a
|
|
554
|
+
* plugin-less caller writes directly. Returns the `.as(scope?)` continuation.
|
|
555
|
+
*
|
|
556
|
+
* The optional third `signatures` param carries the dep signatures ON the
|
|
557
|
+
* registration record — the sole signature channel now that the global
|
|
558
|
+
* metadata store is retired. The transformer emits it inline for every
|
|
559
|
+
* constructed class (`add(token, ctor, [[...]])`); a plugin-less caller
|
|
560
|
+
* hand-feeds it directly. Keying signatures on the registration (not on the
|
|
561
|
+
* ctor object) is what lets one JS class close differently per registration —
|
|
562
|
+
* an open template and its closings never collide.
|
|
563
|
+
*
|
|
564
|
+
* An OPEN template token (`pkg:IRepo<$1>` — every type arg a hole) routes
|
|
565
|
+
* into the open-registration table instead of the exact map; resolution
|
|
566
|
+
* closes it per requested token. Mixing concrete args and holes in the
|
|
567
|
+
* service token throws (v1 all-holes rule).
|
|
568
|
+
*/
|
|
569
|
+
add(token: Token, ctor: Ctor, signatures?: readonly (readonly DepSlot[])[]): AddBuilder<Scopes>;
|
|
570
|
+
/**
|
|
571
|
+
* Factory registration — a string token bound to a factory function. The
|
|
572
|
+
* runtime form the transformer emits for an authored `add<I>(fn)` /
|
|
573
|
+
* `addFactory<I>(fn)`, and what a plugin-less caller writes directly.
|
|
574
|
+
*
|
|
575
|
+
* Parameter injection follows the metadata rule (see `ServiceProvider`): each
|
|
576
|
+
* parameter is injected by its slot from the registration-carried signatures
|
|
577
|
+
* (the optional third arg, emitted inline by the transformer). A factory that
|
|
578
|
+
* wants the live provider declares it as an ordinary parameter (a provider-typed
|
|
579
|
+
* slot); a signature-less factory simply runs with no injected args — nothing is
|
|
580
|
+
* auto-supplied. Returns the `.as(scope?)` continuation so a factory caches at a
|
|
581
|
+
* named scope exactly like a class.
|
|
582
|
+
*
|
|
583
|
+
* The implementation signature admits the single-arg authoring form
|
|
584
|
+
* (`addFactory<I>(fn)`) so the `@rhombus-std/di.transformer` overload merges onto it —
|
|
585
|
+
* that form never runs post-transform, and the runtime guard below fails a
|
|
586
|
+
* plugin-less call loud rather than registering junk (mirrors `add`).
|
|
587
|
+
*/
|
|
588
|
+
addFactory(token: Token, factory: Factory, signatures?: readonly (readonly DepSlot[])[]): AddBuilder<Scopes>;
|
|
589
|
+
/**
|
|
590
|
+
* Value registration — an already-built instance, no deps and no lifetime.
|
|
591
|
+
* Separate from `add` because a value may itself be a function (a callable
|
|
592
|
+
* service), which is structurally indistinguishable from a factory inside one
|
|
593
|
+
* overload. The authoring form `addValue<I>(v)` (which lowers to
|
|
594
|
+
* `addValue("token", v)`) is a PURE TYPING contributed by the
|
|
595
|
+
* `@rhombus-std/di.transformer` augmentation, not part of di's published surface.
|
|
596
|
+
*/
|
|
597
|
+
addValue(token: Token, value: unknown): void;
|
|
598
|
+
/**
|
|
599
|
+
* Seals the collection into an immutable snapshot — the SEALING half of
|
|
600
|
+
* `build()`. Deep-freezing the maps and each per-token list ensures that any
|
|
601
|
+
* `.add()` call on the builder after sealing cannot mutate what the provider
|
|
602
|
+
* and its descendants see — the container's view is fixed at build time.
|
|
603
|
+
*
|
|
604
|
+
* This is the collection's own concern, so it lives here in di.core. The
|
|
605
|
+
* ENGINE-CONSTRUCTING half — turning this snapshot into a `ServiceProvider` —
|
|
606
|
+
* is a `@rhombus-std/di` extension (`build()` below), because it needs the
|
|
607
|
+
* runtime resolution engine di.core deliberately does not depend on.
|
|
608
|
+
*/
|
|
609
|
+
seal(): SealedManifest;
|
|
610
|
+
/**
|
|
611
|
+
* Seals the collection and returns the built `ServiceProvider`.
|
|
612
|
+
*
|
|
613
|
+
* The IMPLEMENTATION lives in `@rhombus-std/di`, not here — mirroring the
|
|
614
|
+
* reference DI split where the collection ships in the abstractions package
|
|
615
|
+
* but the provider-building entry is a runtime-package extension. Importing
|
|
616
|
+
* `@rhombus-std/di` PROTOTYPE-PATCHES this method onto `ServiceManifestClass`
|
|
617
|
+
* at load time (`services.seal()` → `new ServiceProviderClass(...)`), exactly
|
|
618
|
+
* how a cross-package fluent-authoring augmentation patches the concrete
|
|
619
|
+
* builder. The stub below is what runs if the runtime was never imported.
|
|
620
|
+
*
|
|
621
|
+
* NO frame is pre-opened: the returned provider is frameless. There is no
|
|
622
|
+
* root scope — resolving a tagged registration with no matching frame open
|
|
623
|
+
* yields a transient instance, and an untagged registration is transient as
|
|
624
|
+
* always. Open a scope explicitly with `createScope(name)` when you want a
|
|
625
|
+
* tagged registration to cache.
|
|
626
|
+
*/
|
|
627
|
+
build(): ServiceProvider<Scopes>;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* The public registration-builder INTERFACE a di consumer holds — the
|
|
631
|
+
* `ServiceManifestBase` interface bound to the concrete provider `build()`
|
|
632
|
+
* returns (the ME `IServiceCollection` analog). Interface-first (not the impl
|
|
633
|
+
* class) so the `@rhombus-std/di.transformer` augmentation — which merges the
|
|
634
|
+
* authored `add<I>()` / `.as<"scope">()` forms onto `ServiceManifestBase` —
|
|
635
|
+
* surfaces on a consumer typing against `ServiceManifest<S>`. A class would not
|
|
636
|
+
* inherit those augmented overloads; the interface does.
|
|
637
|
+
*
|
|
638
|
+
* The constructor side (`ServiceManifestCtor`) and the constructible
|
|
639
|
+
* `ServiceManifest` VALUE live in `@rhombus-std/di`, alongside the `build()`
|
|
640
|
+
* prototype-patch that makes `new ServiceManifest().build()` produce a provider.
|
|
641
|
+
*/
|
|
642
|
+
type ServiceManifest<S extends string = "singleton"> = ServiceManifestBase<S, ServiceProvider<S>>;
|
|
643
|
+
|
|
644
|
+
/** True when `slot` is a `FactoryRef` (carries a `.type` token). */
|
|
645
|
+
declare function isFactoryRef(slot: DepSlot): slot is FactoryRef;
|
|
646
|
+
/** True when `slot` is a `Union` (carries a `.union` array of member slots). */
|
|
647
|
+
declare function isUnionSlot(slot: DepSlot): slot is Union;
|
|
648
|
+
/**
|
|
649
|
+
* True when `slot` is a `LiteralRef` — an object slot carrying a `value` key.
|
|
650
|
+
* The value supplies a singular literal directly (`"dev"`, `42`, `true`, `1n`)
|
|
651
|
+
* OR the lone inhabitant of `void` / `undefined` / `null`.
|
|
652
|
+
*
|
|
653
|
+
* Identified by the PRESENCE of the `value` key (`"value" in slot`), never by
|
|
654
|
+
* `value !== undefined` — `value` is legitimately `undefined` for the
|
|
655
|
+
* `void`/`undefined` case. No other slot kind (FactoryRef `.type`, Union
|
|
656
|
+
* `.union`) carries a `value` key, so this is unambiguous.
|
|
657
|
+
*/
|
|
658
|
+
declare function isLiteralRef(slot: DepSlot): slot is LiteralRef;
|
|
659
|
+
/**
|
|
660
|
+
* True when `slot` is a `TypeArgRef` — an object slot carrying a numeric
|
|
661
|
+
* `typeArg` key (the 1-based hole number). Key-disjoint from every other slot
|
|
662
|
+
* kind (FactoryRef `.type`, Union `.union`, LiteralRef `.value`), so the check
|
|
663
|
+
* is unambiguous.
|
|
664
|
+
*/
|
|
665
|
+
declare function isTypeArgRef(slot: DepSlot): slot is TypeArgRef;
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Constructs a `Union` slot — a set of alternative dependency slots tried in
|
|
669
|
+
* declaration order. The first resolvable member wins; if none is resolvable,
|
|
670
|
+
* resolution throws.
|
|
671
|
+
*
|
|
672
|
+
* @example
|
|
673
|
+
* ```ts
|
|
674
|
+
* services.add("pkg:IHandler", Handler, [[
|
|
675
|
+
* union("pkg:IRedis", "pkg:IMemoryCache"),
|
|
676
|
+
* "pkg:ILogger",
|
|
677
|
+
* ]]);
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
680
|
+
declare function union(...slots: DepSlot[]): Union;
|
|
681
|
+
/**
|
|
682
|
+
* Constructs a `TypeArgRef` slot — a parameter that receives the TOKEN STRING
|
|
683
|
+
* of the registration's `n`th type argument (1-based, matching `$n`). Used on
|
|
684
|
+
* the manual authoring surface for hole-template signatures; substitution
|
|
685
|
+
* closes it into a literal value slot per closing.
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```ts
|
|
689
|
+
* services.add("app/IRepo<$1>", SqlRepository, [[typeArg(1), "app/IDb"]]);
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
declare function typeArg(n: number): TypeArgRef;
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Renders the canonical closed-generic form `base<arg1,arg2>`. With no args,
|
|
696
|
+
* returns `base` unchanged. Args may themselves be closed-generic tokens
|
|
697
|
+
* (nesting) or holes (`$N` — producing an open template).
|
|
698
|
+
*/
|
|
699
|
+
declare function closeToken(base: Token, ...args: Token[]): Token;
|
|
700
|
+
/**
|
|
701
|
+
* Parses a closed-generic token into its base and top-level args.
|
|
702
|
+
*
|
|
703
|
+
* Returns `undefined` for non-generic tokens (no top-level `<`) AND for
|
|
704
|
+
* malformed input (empty base, unbalanced brackets, empty arg, trailing text
|
|
705
|
+
* after the closing `>`, unterminated quote) — callers fall through to their
|
|
706
|
+
* exact-match / unregistered-token handling either way.
|
|
707
|
+
*
|
|
708
|
+
* The scan is depth-tracked over `<` / `>` and quote-aware for double quotes
|
|
709
|
+
* (backslash escapes honored), so literal-type args like `"a,b" | "<c>"` split
|
|
710
|
+
* correctly.
|
|
711
|
+
*/
|
|
712
|
+
declare function parseToken(token: Token): ParsedToken | undefined;
|
|
713
|
+
/**
|
|
714
|
+
* True when `token` contains a hole (`$N`) at any depth — i.e. it is an open
|
|
715
|
+
* template rather than a resolvable token. Grammar-aware: a `$N` inside a
|
|
716
|
+
* quoted literal arg is NOT a hole.
|
|
717
|
+
*/
|
|
718
|
+
declare function isOpenToken(token: Token): boolean;
|
|
719
|
+
/**
|
|
720
|
+
* Substitutes hole nodes in an open template with the supplied argument tokens
|
|
721
|
+
* (1-based: `$1` → `args[0]`). Grammar-aware and recursive — a node that is
|
|
722
|
+
* exactly `$N` is replaced by the arg token (which may itself be
|
|
723
|
+
* closed-generic); this is NOT a naive string replace, so a `$N` inside a
|
|
724
|
+
* quoted literal arg survives untouched.
|
|
725
|
+
*
|
|
726
|
+
* Throws `RangeError` when the template references a hole beyond the supplied
|
|
727
|
+
* args — callers match arity before substituting.
|
|
728
|
+
*/
|
|
729
|
+
declare function substituteToken(template: Token, args: readonly Token[]): Token;
|
|
730
|
+
/**
|
|
731
|
+
* Substitutes the supplied argument tokens through every slot of every
|
|
732
|
+
* signature, producing the closed signatures for one closing of an open
|
|
733
|
+
* registration:
|
|
734
|
+
* - a string token → `substituteToken`,
|
|
735
|
+
* - a `FactoryRef` → `type` and each `params` token substituted,
|
|
736
|
+
* - a `Union` → members substituted recursively,
|
|
737
|
+
* - a `TypeArgRef` → a `LiteralRef` carrying `args[typeArg - 1]` (the
|
|
738
|
+
* substituted argument's token string),
|
|
739
|
+
* - a `LiteralRef` → unchanged.
|
|
740
|
+
*/
|
|
741
|
+
declare function substituteSignatures(signatures: readonly (readonly DepSlot[])[], args: readonly Token[]): readonly (readonly DepSlot[])[];
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* The token a `Resolver`-typed parameter derives to. The engine resolves it to
|
|
745
|
+
* the live provider view (the scope-generic-free `Resolver` surface, per #24)
|
|
746
|
+
* relative to the resolving frame, rather than to a registration. Exported so a
|
|
747
|
+
* plugin-less author can hand-feed it in a signature (`[[RESOLVER_TOKEN]]`)
|
|
748
|
+
* without spelling the package-qualified string by hand.
|
|
749
|
+
*/
|
|
750
|
+
declare const RESOLVER_TOKEN: Token;
|
|
751
|
+
/**
|
|
752
|
+
* True when `token` is an intrinsic provider token — one the engine resolves to
|
|
753
|
+
* the live provider view instead of a registration. Always satisfiable during
|
|
754
|
+
* signature selection, and reported as a service by `isService`.
|
|
755
|
+
*/
|
|
756
|
+
declare function isProviderToken(token: Token): boolean;
|
|
757
|
+
|
|
758
|
+
/** Base class for every error the container raises. */
|
|
759
|
+
declare class DiError extends Error {
|
|
760
|
+
constructor(message: string);
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* An open template token was passed to a registration method that cannot
|
|
764
|
+
* accept one: `addValue`/`addFactory` (open registrations are class-only), or
|
|
765
|
+
* `add` with a template whose type arguments are not ALL holes (v1 forbids
|
|
766
|
+
* mixing concrete args and holes in the service token).
|
|
767
|
+
*/
|
|
768
|
+
declare class OpenTokenRegistrationError extends DiError {
|
|
769
|
+
readonly token: Token;
|
|
770
|
+
readonly method: "add" | "addFactory" | "addValue";
|
|
771
|
+
constructor(token: Token, method: "add" | "addFactory" | "addValue");
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
export { DiError, OpenTokenRegistrationError, RESOLVER_TOKEN, ServiceManifestClass, closeToken, isFactoryRef, isLiteralRef, isOpenToken, isProviderToken, isTypeArgRef, isUnionSlot, parseToken, substituteSignatures, substituteToken, typeArg, union };
|
|
775
|
+
export type { $, AddBuilder, Ctor, DepRecord, DepSlot, DepTarget, Factory, FactoryRef, Hole, Inject, Lifetime, LiteralRef, OpenRegistration, OverloadedConstructorParameters, OverloadedParameters, ParsedToken, Producer, Registration, ResolveScope, Resolver, ScopeFactory, SealedManifest, ServiceManifest, ServiceManifestBase, ServiceProvider, Token, TypeArgRef, Typeof, Union };
|