@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thomas Butler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# @rhombus-std/di.core
|
|
2
|
+
|
|
3
|
+
> **Published abstractions package.** `@rhombus-std/di.core` is the abstractions
|
|
4
|
+
> substrate `@rhombus-std/di` depends on and re-exports — mirroring the reference
|
|
5
|
+
> DI split where the abstractions package ships the concrete registration
|
|
6
|
+
> collection and the runtime package supplies the provider. `@rhombus-std/di`
|
|
7
|
+
> keeps it **external** (not inlined) so there is one shared `@rhombus-std/di.core`
|
|
8
|
+
> module identity: the `ServiceManifestClass` `di` patches `build()` onto, and the
|
|
9
|
+
> class cross-package augmentations patch, are the same object. An app installs
|
|
10
|
+
> `@rhombus-std/di` (and `@rhombus-std/di.transformer` for the compile-time plugin)
|
|
11
|
+
> and reaches the authoring surface through di's re-exports; a **library** author
|
|
12
|
+
> depends on `@rhombus-std/di.core` directly to author registrations without the
|
|
13
|
+
> resolution engine.
|
|
14
|
+
|
|
15
|
+
The substrate for `ioc`. It ships the immutable dependency-signature data format
|
|
16
|
+
(`DepSlot` and its variants), the open-generic token grammar with its
|
|
17
|
+
guard/constructor helpers (`union`, `typeArg`), the registration ABI, and — the
|
|
18
|
+
runtime footprint — the concrete registration builder `ServiceManifestClass`
|
|
19
|
+
(`add` / `addFactory` / `addValue`; `build()` is a `@rhombus-std/di` extension)
|
|
20
|
+
plus the registration-time errors (`DiError`, `OpenTokenRegistrationError`). The
|
|
21
|
+
resolution engine (`ServiceProviderClass`) and resolution-time errors live in
|
|
22
|
+
[`@rhombus-std/di`](../di/README.md). There is no metadata store here, or anywhere
|
|
23
|
+
else: a constructor/factory's dependency signature rides on the registration
|
|
24
|
+
itself.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## `Token`
|
|
29
|
+
|
|
30
|
+
A plain `string`. The DI key identifying an interface. No branding, no literal types. Generated by the transformer at build time; passed explicitly in plugin-less usage.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
export type Token = string;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## `FactoryRef`
|
|
39
|
+
|
|
40
|
+
Marks a constructor parameter to be injected as a factory rather than a resolved instance.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
export interface FactoryRef {
|
|
44
|
+
readonly type: Token;
|
|
45
|
+
readonly params?: readonly Token[];
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`type` is the token of the produced type T. `params` is the complete, authored-order list of caller-supplied parameter tokens — passing it pins the factory's shape so it no longer drifts as registration state changes. Omit `params` to get a strict zero-arg `() => T` where every slot must resolve from the container.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## `RESOLVER_TOKEN` / the provider as a resolvable type
|
|
54
|
+
|
|
55
|
+
The provider is an intrinsically resolvable type — no dedicated slot kind. A parameter typed `Resolver` derives the ordinary token `RESOLVER_TOKEN`, and the engine resolves it to the nearest open scope's provider VIEW rather than a registration, so "I want the provider" is plain DI. A plugin-less author hand-feeds the exported constant in a signature:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { RESOLVER_TOKEN } from "@rhombus-std/di.core";
|
|
59
|
+
|
|
60
|
+
services.addFactory("app/IReport", (sp) => buildReport(sp), [[RESOLVER_TOKEN]]);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`isProviderToken(token)` is the runtime predicate the engine uses. (This retires the former `{ scope: true }` `ScopeRef` slot marker — the provider token subsumes it.)
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## `Union`
|
|
68
|
+
|
|
69
|
+
A slot that tries each member in declaration order and resolves to the first one that is registered.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
export interface Union {
|
|
73
|
+
readonly union: readonly DepSlot[];
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Members are tried in array order (first = highest precedence). If no member is resolvable, the container throws. Each member is itself a `DepSlot`, so nesting is allowed.
|
|
78
|
+
|
|
79
|
+
### `union(...slots)`
|
|
80
|
+
|
|
81
|
+
Ergonomic constructor:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
export function union(...slots: DepSlot[]): Union;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
services.add("pkg:IHandler", Handler, [[
|
|
89
|
+
union("pkg:IRedis", "pkg:IMemoryCache"),
|
|
90
|
+
"pkg:ILogger",
|
|
91
|
+
]]);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## `TypeArgRef`
|
|
97
|
+
|
|
98
|
+
Marks a constructor parameter to be injected with the **token string** of one of an open registration's type arguments — the `typeof(T)` analog for open-generic templates.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
export interface TypeArgRef {
|
|
102
|
+
readonly typeArg: number;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`typeArg` is the 1-based hole number (`{ typeArg: 1 }` names the argument bound to `$1`). It only ever appears raw inside an **open** registration's carried signature. At close time, substitution replaces it with a `LiteralRef` carrying the substituted argument's token string — a raw `TypeArgRef` reaching resolution is a bug (unsatisfiable; fails loud rather than silently falling into the token branch). Authored via `Typeof<T>` (below) or, by hand, `typeArg(n)`.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## `Inject<T, K extends Token>`
|
|
111
|
+
|
|
112
|
+
A phantom brand type. Pins the token for one constructor or factory parameter without changing the value type.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
declare const TOK: unique symbol;
|
|
116
|
+
export type Inject<T, K extends Token> = T & { readonly [TOK]?: K };
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The brand is optional — a plain `T` is still assignable. Zero runtime cost.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// Pin a specific token for one arg; others derive normally
|
|
123
|
+
class Handler {
|
|
124
|
+
constructor(
|
|
125
|
+
a: Inject<ICache, "pkg:redis-cache">,
|
|
126
|
+
b: ILogger,
|
|
127
|
+
) {}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Works in any type position the transformer reads: class ctor params, inline factory params, return types. Use it as the escape hatch for anonymous or purely structural types — types with no name that the transformer cannot otherwise tokenize — `Inject<{ n: number }, "my:opts">`. Named types (interfaces, classes, primitive keywords) derive a token on their own and do not need `Inject`.
|
|
132
|
+
|
|
133
|
+
Re-exported by `@rhombus-std/di.transformer`.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## `Hole<N, C>` and `$<N>`
|
|
138
|
+
|
|
139
|
+
Compile-time skolems — placeholders standing in for the `N`th type argument of an open-generic template (1-based, mirrors the `Inject` brand pattern above).
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
declare const HOLE: unique symbol;
|
|
143
|
+
export type Hole<N extends number, C = unknown> = C & { readonly [HOLE]?: N };
|
|
144
|
+
|
|
145
|
+
export type $<N extends number> = Hole<N>;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`$<N>` is unbounded sugar for the common unconstrained case (`$<1>` = `Hole<1>`, `$<2>` = `Hole<2>`, … no upper bound). Write `Hole<N, C>` directly when the implementation's type parameter carries a constraint the skolem needs to satisfy — `Hole<1, Entity>` **is** an `Entity` (the brand property is optional, so the intersection stays assignable to `C`), letting a constrained impl like `class SqlRepository<T extends Entity>` accept a hole as its type argument:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
services.add<IRepository<$<1>>>(SqlRepository<Hole<1, Entity>>);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Zero runtime footprint — like `Inject`, these are pure compile-time brands read structurally by `@rhombus-std/di.transformer`. See [`@rhombus-std/di.transformer`](../di.transformer/README.md#open-generics) for how they drive token derivation and instantiation expressions.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## `Typeof<T>`
|
|
159
|
+
|
|
160
|
+
A phantom brand marking a constructor parameter that receives the **token string** of type argument `T` — the `typeof(T)` analog (hence the name).
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
declare const ARG: unique symbol;
|
|
164
|
+
export type Typeof<T> = Token & { readonly [ARG]?: T };
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`Typeof<T>` is type-driven — the transformer infers the hole from `T`; its manual counterpart `typeArg(n)` is positional. The value type stays `Token` (a plain string) — the brand property is optional, so any string is assignable. When `T` resolves to a `Hole`, the transformer emits an open `TypeArgRef` slot (`{ typeArg: N }`) that substitution closes per registration; when `T` is concrete (a closed registration via an instantiation expression), it emits the derived token directly as a `LiteralRef` value slot — no substitution step needed.
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
class SqlRepository<T> implements IRepository<T> {
|
|
171
|
+
constructor(
|
|
172
|
+
private db: IDbConnection,
|
|
173
|
+
private entityToken: Typeof<T>,
|
|
174
|
+
) {}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
services.add<IRepository<$<1>>>(SqlRepository<$<1>>);
|
|
178
|
+
|
|
179
|
+
const userRepo = scope.resolve<IRepository<User>>();
|
|
180
|
+
// userRepo.entityToken === "pkg:User"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## `DepSlot`
|
|
186
|
+
|
|
187
|
+
One positional slot in a constructor signature:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
export type DepSlot =
|
|
191
|
+
| Token
|
|
192
|
+
| FactoryRef
|
|
193
|
+
| Union
|
|
194
|
+
| LiteralRef
|
|
195
|
+
| TypeArgRef;
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
- `Token` — a container-resolved dep (registered) or a caller-supplied param (unregistered); the live registration map determines which at resolve time. The intrinsic provider token (`RESOLVER_TOKEN`) resolves to the live provider view.
|
|
199
|
+
- `FactoryRef` — a factory-injected parameter.
|
|
200
|
+
- `Union` — member-level alternatives; first resolvable wins.
|
|
201
|
+
- `LiteralRef` — a singular literal or nullish-singleton value, injected directly, no container lookup, always satisfiable.
|
|
202
|
+
- `TypeArgRef` — the token string of an open registration's `N`th type argument; substituted to a `LiteralRef` at close time (see `TypeArgRef` above).
|
|
203
|
+
|
|
204
|
+
An unregistered token slot is caller-supplied by definition; a `TypeArgRef` slot is only ever satisfiable through substitution, never directly.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## `DepRecord`
|
|
209
|
+
|
|
210
|
+
The shape of a registration's carried dependency metadata — not stored anywhere globally; it's the type of the array a registration passes as its own signature(s).
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
export interface DepRecord {
|
|
214
|
+
readonly signatures: readonly (readonly DepSlot[])[];
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`signatures` holds one or many signature arrays, supporting constructor overloads without an ABI break — the engine picks the first satisfiable one, scanned longest to shortest (see [`@rhombus-std/di`](../di/README.md#greedy-overload-selection)).
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Authoring dependency signatures by hand
|
|
223
|
+
|
|
224
|
+
There is no global metadata store and no decorator. A signature rides directly
|
|
225
|
+
on the registration, passed as the optional **third argument** to
|
|
226
|
+
`@rhombus-std/di`'s `add` / `addFactory`:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { ServiceManifest } from "@rhombus-std/di";
|
|
230
|
+
|
|
231
|
+
const services = new ServiceManifest();
|
|
232
|
+
|
|
233
|
+
services.add("pkg:IHandler", Handler, [
|
|
234
|
+
["pkg:ILogger", "pkg:IDb"], // overload 0 — one signature per constructor overload
|
|
235
|
+
]);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`@rhombus-std/di.transformer` emits this array inline for every registration it can
|
|
239
|
+
statically extract a signature from, rewriting the type-driven `add<IFoo>(Foo)`
|
|
240
|
+
to `add("pkg:IFoo", Foo, [[...]])` — there is no separate prelude call and
|
|
241
|
+
nothing hoisted; the signature travels with the registration itself. Hand
|
|
242
|
+
authoring (no transformer) means writing the array yourself, using the slot
|
|
243
|
+
builders above (`union`, `typeArg`) plus plain token strings (including
|
|
244
|
+
`RESOLVER_TOKEN` for the provider), `FactoryRef` / `LiteralRef` object literals,
|
|
245
|
+
and the token-grammar helpers below for open generics. Omitting the third
|
|
246
|
+
argument leaves the registration
|
|
247
|
+
signature-less — fine for a zero-arg constructor, but resolution throws
|
|
248
|
+
`MissingMetadataError` for any constructor with parameters.
|
|
249
|
+
|
|
250
|
+
Because the array is keyed on the **registration record**, not on the
|
|
251
|
+
constructor function, one JS class can back any number of independent
|
|
252
|
+
registrations with different signatures — the mechanism open-generics
|
|
253
|
+
registrations depend on, where the same erased class serves every closing of a
|
|
254
|
+
template. See [Open-generic token grammar](#open-generic-token-grammar) below
|
|
255
|
+
and [`@rhombus-std/di`](../di/README.md#open-generics).
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Open-generic token grammar
|
|
260
|
+
|
|
261
|
+
Closing a generic is token algebra, not runtime type machinery — TypeScript generics are erased, so there is exactly one JS class per generic implementation. `@rhombus-std/di.transformer` renders the grammar below at build time; these functions are how `@rhombus-std/di`'s resolve-time fallback (and any hand-written manual registration) works with it directly.
|
|
262
|
+
|
|
263
|
+
**Closed-generic grammar (canonical, recursive):** `base<arg1,arg2>` — no whitespace around `<` `>` `,`. Each arg is itself a token, so nesting recurses (`pkg:IFoo<pkg:IBar<./src/Baz>>`). A **hole** is an arg that is exactly `$N` (decimal, `N ≥ 1`); a token containing a hole at any depth is an _open template_. Literal-type args keep their interior spaces/quotes (`"a" | "b"`) — the parser is quote-aware, so commas and angle brackets inside double quotes never count as separators.
|
|
264
|
+
|
|
265
|
+
### `closeToken(base, ...args)`
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
export function closeToken(base: Token, ...args: Token[]): Token;
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Renders the canonical form. With no args, returns `base` unchanged.
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
closeToken("pkg:IRepository", "pkg:User"); // "pkg:IRepository<pkg:User>"
|
|
275
|
+
closeToken("pkg:IMap", "string", "$1"); // "pkg:IMap<string,$1>"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### `parseToken(token)`
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
export interface ParsedToken {
|
|
282
|
+
readonly base: Token;
|
|
283
|
+
readonly args: readonly Token[];
|
|
284
|
+
}
|
|
285
|
+
export function parseToken(token: Token): ParsedToken | undefined;
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Splits a closed-generic token into its base and top-level args. Returns `undefined` for non-generic tokens (no top-level `<`) and for malformed input (unbalanced brackets, empty arg, trailing text after the closing `>`, unterminated quote) — callers fall through to their normal exact-match / unregistered-token handling either way.
|
|
289
|
+
|
|
290
|
+
### `isOpenToken(token)`
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
export function isOpenToken(token: Token): boolean;
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
True when `token` contains a hole (`$N`) at any depth — grammar-aware, so a `$N` inside a quoted literal arg is not a hole.
|
|
297
|
+
|
|
298
|
+
### `substituteToken(template, args)`
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
export function substituteToken(template: Token, args: readonly Token[]): Token;
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Grammar-aware, recursive substitution — NOT a naive string replace. A node that is exactly `$N` is replaced by `args[N - 1]` (which may itself be a closed-generic token); everything else recurses through the parsed structure. Throws `RangeError` if the template references a hole beyond the supplied args.
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
substituteToken("pkg:IRepository<$1>", ["pkg:User"]);
|
|
308
|
+
// → "pkg:IRepository<pkg:User>"
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### `substituteSignatures(signatures, args)`
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
export function substituteSignatures(
|
|
315
|
+
signatures: readonly (readonly DepSlot[])[],
|
|
316
|
+
args: readonly Token[],
|
|
317
|
+
): readonly (readonly DepSlot[])[];
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Substitutes `args` through every slot of every signature — the whole-record counterpart to `substituteToken`, used to close an open registration's carried dep signatures at resolve time. Per slot: a string token → `substituteToken`; a `FactoryRef` → its `type` and each `params` token substituted; a `Union` → members substituted recursively; a `TypeArgRef` → a `LiteralRef` carrying `args[typeArg - 1]`; a `LiteralRef` passes through unchanged.
|
|
321
|
+
|
|
322
|
+
### `typeArg(n)` and `isTypeArgRef`
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
export function typeArg(n: number): TypeArgRef;
|
|
326
|
+
export function isTypeArgRef(slot: DepSlot): slot is TypeArgRef;
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
`typeArg(n)` is the manual-authoring counterpart to `Typeof<T>` — build a `{ typeArg: n }` slot by hand when writing an open registration's signature array directly (no transformer). `isTypeArgRef` is the type guard, alongside `isFactoryRef` / `isUnionSlot` / `isLiteralRef`.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Versioning
|
|
334
|
+
|
|
335
|
+
`@rhombus-std/di.core` is versioned independently via release-please (semver). The dep-metadata format (`DepRecord`) is kept backward-compatible across `core` semver minors; a breaking change to the wire format would require a coordinated update across all packages.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Exports summary
|
|
340
|
+
|
|
341
|
+
| Export | Kind | Description |
|
|
342
|
+
| ---------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
343
|
+
| `Token` | Type alias | `string` — the DI key type. |
|
|
344
|
+
| `FactoryRef` | Interface | `{ type: Token; params?: readonly Token[] }` — factory-injected parameter slot. |
|
|
345
|
+
| `RESOLVER_TOKEN` | Const | The intrinsic provider token — resolves to the live provider view (a `Resolver`-typed param derives it). |
|
|
346
|
+
| `isProviderToken` | Function | `(token) => boolean` — true for the intrinsic provider token(s). |
|
|
347
|
+
| `Union` | Interface | `{ union: readonly DepSlot[] }` — member-level alternatives. |
|
|
348
|
+
| `union` | Function | Construct a `Union` slot from variadic `DepSlot` args. |
|
|
349
|
+
| `LiteralRef` | Interface | `{ value }` — a singular literal / nullish-singleton slot, injected directly. |
|
|
350
|
+
| `TypeArgRef` | Interface | `{ typeArg: number }` — the token string of an open registration's `N`th type argument. |
|
|
351
|
+
| `Inject` | Type alias | Phantom brand — pins a token for one param without changing the value type. |
|
|
352
|
+
| `Hole` | Type alias | `Hole<N, C>` — compile-time skolem for the `N`th type argument of an open template, optionally constrained to `C`. |
|
|
353
|
+
| `$` | Type alias | `$<N>` — unbounded unconstrained sugar for `Hole<N>`. |
|
|
354
|
+
| `Typeof` | Type alias | Phantom brand — a ctor param receiving the token string of type argument `T` (`typeof(T)` analog). |
|
|
355
|
+
| `DepSlot` | Type alias | `Token \| FactoryRef \| Union \| LiteralRef \| TypeArgRef` — one positional slot in a signature. |
|
|
356
|
+
| `DepTarget` | Type alias | `Ctor \| Func<never[], unknown>` — a ctor or factory function a signature describes. |
|
|
357
|
+
| `DepRecord` | Interface | `{ signatures }` — per-registration signature-array shape. |
|
|
358
|
+
| `isFactoryRef` | Function | Type guard for `FactoryRef` slots. |
|
|
359
|
+
| `isUnionSlot` | Function | Type guard for `Union` slots. |
|
|
360
|
+
| `isLiteralRef` | Function | Type guard for `LiteralRef` slots. |
|
|
361
|
+
| `closeToken` | Function | Render the canonical closed-generic token `base<a,b>`. |
|
|
362
|
+
| `parseToken` | Function | Parse a closed-generic token into `{ base, args }`, or `undefined`. |
|
|
363
|
+
| `isOpenToken` | Function | True when a token contains a hole (`$N`) at any depth. |
|
|
364
|
+
| `substituteToken` | Function | Grammar-aware substitution of holes in a token template. |
|
|
365
|
+
| `substituteSignatures` | Function | Substitute type args through every slot of every signature. |
|
|
366
|
+
| `typeArg` | Function | Build a `TypeArgRef` slot (`{ typeArg: n }`) by hand. |
|
|
367
|
+
| `isTypeArgRef` | Function | Type guard for `TypeArgRef` slots. |
|