@codefast/di 0.10.1 → 0.12.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/CHANGELOG.md +376 -0
- package/README.md +94 -17
- package/dist/{ambient/active-container.d.ts → ambient-container.d.ts} +7 -2
- package/dist/{ambient/active-container.js → ambient-container.js} +6 -1
- package/dist/container/{binding-builders.d.ts → binding-chain.d.ts} +27 -16
- package/dist/container/binding-chain.js +254 -0
- package/dist/container/container.d.ts +11 -2
- package/dist/container/container.js +293 -73
- package/dist/core/binding-builders.d.ts +114 -0
- package/dist/core/binding-declaration.d.ts +127 -0
- package/dist/core/binding-declaration.js +187 -0
- package/dist/core/binding.d.ts +62 -138
- package/dist/core/binding.js +54 -17
- package/dist/core/module.d.ts +8 -5
- package/dist/core/module.js +17 -3
- package/dist/core/registry.d.ts +17 -28
- package/dist/core/registry.js +92 -133
- package/dist/core/state-epoch.d.ts +18 -1
- package/dist/core/state-epoch.js +17 -0
- package/dist/core/tag.js +1 -1
- package/dist/decorators/inject.js +6 -5
- package/dist/decorators/injectable.js +4 -2
- package/dist/decorators/{lifecycle-decorators.js → lifecycle.js} +9 -3
- package/dist/decorators/metadata-record.d.ts +9 -0
- package/dist/decorators/metadata-record.js +20 -0
- package/dist/{errors/errors.d.ts → errors.d.ts} +84 -9
- package/dist/{errors/errors.js → errors.js} +101 -17
- package/dist/index.d.ts +14 -10
- package/dist/index.js +7 -6
- package/dist/injection/{resolve-options.js → dependency-slot.js} +6 -4
- package/dist/injection/descriptor.d.ts +1 -1
- package/dist/injection/descriptor.js +3 -7
- package/dist/introspection/dependency-graph.d.ts +8 -3
- package/dist/introspection/dependency-graph.js +48 -26
- package/dist/introspection/explanation.d.ts +101 -0
- package/dist/introspection/explanation.js +151 -0
- package/dist/introspection/graph-adapters/reactflow.js +6 -4
- package/dist/introspection/inspector.d.ts +9 -2
- package/dist/introspection/inspector.js +30 -28
- package/dist/lifecycle/{lifecycle-manager.d.ts → hooks.d.ts} +9 -2
- package/dist/lifecycle/{lifecycle-manager.js → hooks.js} +59 -30
- package/dist/lifecycle/{scope-manager.d.ts → scopes.d.ts} +3 -5
- package/dist/lifecycle/{scope-manager.js → scopes.js} +36 -29
- package/dist/metadata/{metadata-reader-token.d.ts → reader-token.d.ts} +1 -1
- package/dist/metadata/{symbol-metadata-reader.d.ts → symbol-reader.d.ts} +1 -1
- package/dist/metadata/{symbol-metadata-reader.js → symbol-reader.js} +1 -1
- package/dist/metadata/{metadata-types.d.ts → types.d.ts} +1 -1
- package/dist/metadata/types.js +1 -0
- package/dist/metadata/verify.d.ts +43 -0
- package/dist/metadata/verify.js +133 -0
- package/dist/metadata/{verifying-metadata-reader.d.ts → verifying-reader.d.ts} +5 -4
- package/dist/metadata/verifying-reader.js +53 -0
- package/dist/resolution/async-fan-out.d.ts +12 -0
- package/dist/resolution/async-fan-out.js +26 -0
- package/dist/resolution/cache/activation-need.d.ts +1 -2
- package/dist/resolution/cache/activation-need.js +16 -19
- package/dist/resolution/cache/class-introspector.d.ts +38 -36
- package/dist/resolution/cache/class-introspector.js +79 -159
- package/dist/resolution/cache/{binding-lookup-cache.d.ts → lookup.d.ts} +7 -9
- package/dist/resolution/cache/{binding-lookup-cache.js → lookup.js} +70 -25
- package/dist/resolution/context.d.ts +16 -24
- package/dist/resolution/context.js +47 -56
- package/dist/resolution/path.d.ts +128 -0
- package/dist/resolution/path.js +167 -0
- package/dist/resolution/plan/{plan-codegen.d.ts → codegen.d.ts} +5 -4
- package/dist/resolution/plan/{plan-codegen.js → codegen.js} +58 -32
- package/dist/resolution/plan/{instantiation-plan.d.ts → compiler.d.ts} +1 -1
- package/dist/resolution/plan/{instantiation-plan.js → compiler.js} +64 -24
- package/dist/resolution/resolver.d.ts +11 -12
- package/dist/resolution/resolver.js +573 -356
- package/dist/resolution/select/{binding-select.d.ts → candidates.d.ts} +30 -0
- package/dist/resolution/select/{binding-select.js → candidates.js} +51 -10
- package/dist/resolution/select/constraints.js +1 -1
- package/package.json +87 -67
- package/dist/container/binding-builders.js +0 -305
- package/dist/core/binding-scope.d.ts +0 -12
- package/dist/core/binding-scope.js +0 -12
- package/dist/metadata/verifying-metadata-reader.js +0 -31
- package/dist/resolution/path/resolution-path.d.ts +0 -93
- package/dist/resolution/path/resolution-path.js +0 -116
- /package/dist/{metadata/metadata-types.js → core/binding-builders.js} +0 -0
- /package/dist/decorators/{lifecycle-decorators.d.ts → lifecycle.d.ts} +0 -0
- /package/dist/injection/{resolve-options.d.ts → dependency-slot.d.ts} +0 -0
- /package/dist/{errors → introspection}/diagnostics.d.ts +0 -0
- /package/dist/{errors → introspection}/diagnostics.js +0 -0
- /package/dist/metadata/{metadata-keys.d.ts → keys.d.ts} +0 -0
- /package/dist/metadata/{metadata-keys.js → keys.js} +0 -0
- /package/dist/metadata/{metadata-reader-token.js → reader-token.js} +0 -0
package/dist/core/binding.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BindingTag, TagKeyMask } from "#core/tag";
|
|
2
2
|
import type { Token } from "#core/token";
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
3
|
+
import type { BindingConstraint, BindingIdentifier, BindingScope, Constructor, ResolutionContext, ResolutionFrame } from "#core/types";
|
|
4
|
+
import type { InjectionDescriptor } from "#injection/descriptor";
|
|
5
5
|
/**
|
|
6
6
|
* The criterion set a binding registers under and a request matches against.
|
|
7
7
|
*
|
|
@@ -32,6 +32,14 @@ export declare function createBindingSlot(tags: ReadonlyArray<BindingTag>): Bind
|
|
|
32
32
|
* @since 0.3.16-canary.0
|
|
33
33
|
*/
|
|
34
34
|
export declare function bindingSlotEquals(left: BindingSlot, right: BindingSlot): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Returns the slot with one criterion added, replacing any earlier criterion of the same key.
|
|
37
|
+
*
|
|
38
|
+
* @remarks One criterion per key: re-tagging a key replaces it rather than asking for both values.
|
|
39
|
+
*
|
|
40
|
+
* @since 0.11.0
|
|
41
|
+
*/
|
|
42
|
+
export declare function withSlotCriterion(slot: BindingSlot, criterion: BindingTag): BindingSlot;
|
|
35
43
|
/**
|
|
36
44
|
* Cached singleton absent — distinguishes "not resolved yet" from a cached `undefined`.
|
|
37
45
|
*
|
|
@@ -48,6 +56,15 @@ export declare const NO_INSTANCE: unique symbol;
|
|
|
48
56
|
* @since 0.3.16-canary.0
|
|
49
57
|
*/
|
|
50
58
|
export declare const DEFAULT_BINDING_SLOT: BindingSlot;
|
|
59
|
+
/**
|
|
60
|
+
* Renders a tag value for a diagnostic, never throwing.
|
|
61
|
+
*
|
|
62
|
+
* @remarks A tag value is caller data — a bigint, a null-prototype object, a throwing `toString` —
|
|
63
|
+
* so stringifying it must not become the error that masks the real one.
|
|
64
|
+
*
|
|
65
|
+
* @since 0.11.0
|
|
66
|
+
*/
|
|
67
|
+
export declare function stringifyTagValue(value: unknown): string;
|
|
51
68
|
/**
|
|
52
69
|
* Formats a slot for diagnostics — `default`, or its `name:`/`tag:` parts.
|
|
53
70
|
*
|
|
@@ -57,12 +74,13 @@ export declare function bindingSlotToString(slot: BindingSlot): string;
|
|
|
57
74
|
interface BindingBase<Value> {
|
|
58
75
|
readonly identifier: BindingIdentifier;
|
|
59
76
|
/**
|
|
60
|
-
* True while this binding
|
|
77
|
+
* True while this binding is being resolved on the current synchronous call stack.
|
|
61
78
|
*
|
|
62
|
-
* @remarks
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
79
|
+
* @remarks Every synchronous cycle guard reads this flag and nothing else: synchronous code does not
|
|
80
|
+
* interleave, so the flag *is* exact path membership at any depth. The async lanes hold it only for a
|
|
81
|
+
* factory's synchronous prefix, or for the seeded path a synchronous call from an async level runs
|
|
82
|
+
* over. Not optional: the binding builder always initializes it, and a field that may be absent is a
|
|
83
|
+
* field that can cost the shared hidden class. Resolver-owned; callers never set it.
|
|
66
84
|
*/
|
|
67
85
|
inFlight: boolean;
|
|
68
86
|
/**
|
|
@@ -73,6 +91,21 @@ interface BindingBase<Value> {
|
|
|
73
91
|
* @remarks Resolver-owned bookkeeping — `registry.add` normalizes it, so callers never set it.
|
|
74
92
|
*/
|
|
75
93
|
frame: ResolutionFrame | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* The context a transient factory root is handed, built once per binding: the root's path is its own frame
|
|
96
|
+
* alone, so every resolve of the root reads the same one.
|
|
97
|
+
*
|
|
98
|
+
* @remarks Resolver-owned bookkeeping, cleared with the frame it is built over.
|
|
99
|
+
*/
|
|
100
|
+
rootContext: ResolutionContext | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* The activation need last computed for this binding, stamped with the versions it was computed
|
|
103
|
+
* under, or {@link NO_ACTIVATION_STAMP}.
|
|
104
|
+
*
|
|
105
|
+
* @remarks Resolver-owned bookkeeping: a field the level reads beats a per-resolver map that a
|
|
106
|
+
* container resolving each binding once would build and never read again.
|
|
107
|
+
*/
|
|
108
|
+
activationStamp: number;
|
|
76
109
|
/**
|
|
77
110
|
* Cached singleton instance, or {@link NO_INSTANCE}.
|
|
78
111
|
*
|
|
@@ -80,6 +113,13 @@ interface BindingBase<Value> {
|
|
|
80
113
|
* a field read replaces a keyed lookup on the hottest resolve shape there is.
|
|
81
114
|
*/
|
|
82
115
|
instance: unknown;
|
|
116
|
+
/**
|
|
117
|
+
* The key every container's scoped cache files this binding's instance under.
|
|
118
|
+
*
|
|
119
|
+
* @remarks Chain-owned. It starts as the binding's id, and a scope verb that changes the scope mints a new one, so
|
|
120
|
+
* a scoped instance any child cached under an earlier scope is never found again. The id itself stays stable.
|
|
121
|
+
*/
|
|
122
|
+
scopedCacheKey: BindingIdentifier;
|
|
83
123
|
readonly token: Token<Value> | Constructor<Value>;
|
|
84
124
|
readonly slot: BindingSlot;
|
|
85
125
|
readonly predicate?: BindingConstraint | undefined;
|
|
@@ -188,150 +228,34 @@ export interface AliasBinding<Value> extends BindingBase<Value> {
|
|
|
188
228
|
*/
|
|
189
229
|
export type Binding<Value = unknown> = ClassBinding<Value> | DynamicBinding<Value> | DynamicAsyncBinding<Value> | ResolvedBinding<Value> | ResolvedAsyncBinding<Value> | ConstantBinding<Value> | AliasBinding<Value>;
|
|
190
230
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
* @since 0.3.16-canary.0
|
|
194
|
-
*/
|
|
195
|
-
export declare function generateBindingId(): BindingIdentifier;
|
|
196
|
-
/**
|
|
197
|
-
* Writable view of the one selection field a chain may refine without re-registering: nothing
|
|
198
|
-
* indexes on the predicate, so the registry rewrites it in place and re-homes the binding itself.
|
|
199
|
-
*
|
|
200
|
-
* @since 0.10.0
|
|
201
|
-
*/
|
|
202
|
-
export interface PredicateField {
|
|
203
|
-
predicate: BindingConstraint | undefined;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Narrows a registered binding to its predicate for the registry to rewrite.
|
|
207
|
-
*
|
|
208
|
-
* @since 0.10.0
|
|
209
|
-
*/
|
|
210
|
-
export declare function writablePredicate(binding: Binding): PredicateField;
|
|
211
|
-
/**
|
|
212
|
-
* Writable view of collection membership, which the registry sets because it decides the binding's map.
|
|
213
|
-
*
|
|
214
|
-
* @since 0.10.0
|
|
215
|
-
*/
|
|
216
|
-
export interface MembershipField {
|
|
217
|
-
isMany: boolean;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Narrows a registered binding to its membership flag for the registry to set.
|
|
221
|
-
*
|
|
222
|
-
* @since 0.10.0
|
|
223
|
-
*/
|
|
224
|
-
export declare function writableMembership(binding: Binding): MembershipField;
|
|
225
|
-
/**
|
|
226
|
-
* Drops the memoized resolution frame, for a refinement that changes what the frame reports.
|
|
227
|
-
*
|
|
228
|
-
* @remarks The frame derives from `scope` and `slot`, both of which a chain now writes in place on
|
|
229
|
-
* the registered object, so every such refinement clears it.
|
|
230
|
-
*
|
|
231
|
-
* @since 0.5.0-canary.9
|
|
232
|
-
*/
|
|
233
|
-
export declare function clearBindingFrame<Value>(binding: Binding<Value>): void;
|
|
234
|
-
/**
|
|
235
|
-
* Common slot-constraint + id methods shared by all concrete binding builders.
|
|
236
|
-
*
|
|
237
|
-
* @since 0.3.16-canary.0
|
|
238
|
-
*/
|
|
239
|
-
export interface SlotConstrainedBuilder<Names extends string = string> {
|
|
240
|
-
/** Narrows the binding to requests the predicate accepts, evaluated on every resolve. */
|
|
241
|
-
when(predicate: BindingConstraint): this;
|
|
242
|
-
/** Declares the binding's slot name, one of the names the token declares. */
|
|
243
|
-
whenNamed(name: Names): this;
|
|
244
|
-
/** Declares one criterion of the binding's slot, replacing any earlier criterion of the same key. */
|
|
245
|
-
whenTagged(criterion: BindingTag): this;
|
|
246
|
-
/** Keeps the binding on the default slot, the one an unconstrained request selects. */
|
|
247
|
-
whenDefault(): this;
|
|
248
|
-
/**
|
|
249
|
-
* Makes the binding a collection member: one of several the token's `resolveAll` returns, never
|
|
250
|
-
* what a single `resolve` selects, and outside slot last-wins. It keeps the default slot.
|
|
251
|
-
*/
|
|
252
|
-
many(): this;
|
|
253
|
-
/** The identifier this binding is registered under. */
|
|
254
|
-
id(): BindingIdentifier;
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* The `to*` step of the fluent chain, choosing what a token resolves to.
|
|
231
|
+
* The scope a binding resolves under.
|
|
258
232
|
*
|
|
259
|
-
* @since
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
to(type: Constructor<Value>): BindingBuilder<Value, Names>;
|
|
263
|
-
toSelf(): BindingBuilder<Value, Names>;
|
|
264
|
-
toConstantValue(value: Value): ConstantBindingBuilder<Value, Names>;
|
|
265
|
-
toDynamic(factory: (ctx: ResolutionContext) => Value): BindingBuilder<Value, Names>;
|
|
266
|
-
toDynamicAsync(factory: (ctx: ResolutionContext) => Promise<Value>): BindingBuilder<Value, Names>;
|
|
267
|
-
toResolved<const Deps extends ReadonlyArray<InjectableDependency>>(factory: (...args: {
|
|
268
|
-
[K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]>;
|
|
269
|
-
}) => Value, deps: Deps): BindingBuilder<Value, Names>;
|
|
270
|
-
toResolvedAsync<const Deps extends ReadonlyArray<InjectableDependency>>(factory: (...args: {
|
|
271
|
-
[K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]>;
|
|
272
|
-
}) => Promise<Value>, deps: Deps): BindingBuilder<Value, Names>;
|
|
273
|
-
toAlias(target: Token<Value> | Constructor<Value>): AliasBindingBuilder<Names>;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* The scope-selection step of the fluent chain.
|
|
233
|
+
* @remarks Every kind declares one — an alias declares `transient`, since it defers scoping to the
|
|
234
|
+
* binding it points at — so this is a field read, kept as a named function because it is the
|
|
235
|
+
* vocabulary validation and introspection speak.
|
|
277
236
|
*
|
|
278
237
|
* @since 0.3.16-canary.0
|
|
279
238
|
*/
|
|
280
|
-
export
|
|
281
|
-
singleton(): SingletonBindingBuilder<Value>;
|
|
282
|
-
transient(): TransientBindingBuilder<Value>;
|
|
283
|
-
scoped(): ScopedBindingBuilder<Value>;
|
|
284
|
-
}
|
|
239
|
+
export declare function effectiveBindingScope(binding: Binding): BindingScope;
|
|
285
240
|
/**
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
* @since 0.3.16-canary.0
|
|
289
|
-
*/
|
|
290
|
-
export interface ConstantBindingBuilder<Value, Names extends string = string> extends SlotConstrainedBuilder<Names> {
|
|
291
|
-
onActivation(fn: ActivationHandler<Value>): SingletonLifecycleBuilder<Value>;
|
|
292
|
-
onDeactivation(fn: DeactivationHandler<Value>): SingletonLifecycleBuilder<Value>;
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* The fluent chain for an alias — slot constraints only, since scoping belongs to the target.
|
|
241
|
+
* Returns a process-unique identifier for a new binding.
|
|
296
242
|
*
|
|
297
243
|
* @since 0.3.16-canary.0
|
|
298
244
|
*/
|
|
299
|
-
export
|
|
300
|
-
}
|
|
245
|
+
export declare function generateBindingId(): BindingIdentifier;
|
|
301
246
|
/**
|
|
302
|
-
* The
|
|
247
|
+
* The stamp of a binding whose activation need has not been computed under the current versions.
|
|
303
248
|
*
|
|
304
|
-
* @since 0.
|
|
249
|
+
* @since 0.11.0
|
|
305
250
|
*/
|
|
306
|
-
export
|
|
307
|
-
onActivation(fn: ActivationHandler<Value>): this;
|
|
308
|
-
onDeactivation(fn: DeactivationHandler<Value>): this;
|
|
309
|
-
id(): BindingIdentifier;
|
|
310
|
-
}
|
|
251
|
+
export declare const NO_ACTIVATION_STAMP = -1;
|
|
311
252
|
/**
|
|
312
|
-
*
|
|
253
|
+
* Drops the memoized resolution frame, for a scope change that alters what the frame reports.
|
|
313
254
|
*
|
|
314
|
-
* @
|
|
315
|
-
|
|
316
|
-
export interface TransientBindingBuilder<Value> {
|
|
317
|
-
onActivation(fn: ActivationHandler<Value>): this;
|
|
318
|
-
id(): BindingIdentifier;
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* The fluent chain after `scoped()`, sharing the `transient()` surface.
|
|
255
|
+
* @remarks The frame derives from `scope` and `slot`; the slot is final once the binding registers, so
|
|
256
|
+
* only a scope written in place on the registered object clears it.
|
|
322
257
|
*
|
|
323
|
-
* @since 0.
|
|
324
|
-
*/
|
|
325
|
-
export interface ScopedBindingBuilder<Value> extends TransientBindingBuilder<Value> {
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* The fluent chain a constant enters once a lifecycle hook is added.
|
|
329
|
-
*
|
|
330
|
-
* @since 0.3.16-canary.0
|
|
258
|
+
* @since 0.5.0-canary.9
|
|
331
259
|
*/
|
|
332
|
-
export
|
|
333
|
-
onActivation(fn: ActivationHandler<Value>): this;
|
|
334
|
-
onDeactivation(fn: DeactivationHandler<Value>): this;
|
|
335
|
-
id(): BindingIdentifier;
|
|
336
|
-
}
|
|
260
|
+
export declare function clearBindingFrame<Value>(binding: Binding<Value>): void;
|
|
337
261
|
export {};
|
package/dist/core/binding.js
CHANGED
|
@@ -36,6 +36,24 @@ export function bindingSlotEquals(left, right) {
|
|
|
36
36
|
}
|
|
37
37
|
return true;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns the slot with one criterion added, replacing any earlier criterion of the same key.
|
|
41
|
+
*
|
|
42
|
+
* @remarks One criterion per key: re-tagging a key replaces it rather than asking for both values.
|
|
43
|
+
*
|
|
44
|
+
* @since 0.11.0
|
|
45
|
+
*/
|
|
46
|
+
export function withSlotCriterion(slot, criterion) {
|
|
47
|
+
const tags = [...slot.tags];
|
|
48
|
+
const existingIndex = tags.findIndex((existing) => existing.key === criterion.key);
|
|
49
|
+
if (existingIndex === -1) {
|
|
50
|
+
tags.push(criterion);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
tags[existingIndex] = criterion;
|
|
54
|
+
}
|
|
55
|
+
return createBindingSlot(tags);
|
|
56
|
+
}
|
|
39
57
|
/**
|
|
40
58
|
* Cached singleton absent — distinguishes "not resolved yet" from a cached `undefined`.
|
|
41
59
|
*
|
|
@@ -52,6 +70,22 @@ export const NO_INSTANCE = Symbol("di:no-instance");
|
|
|
52
70
|
* @since 0.3.16-canary.0
|
|
53
71
|
*/
|
|
54
72
|
export const DEFAULT_BINDING_SLOT = { name: undefined, tags: Object.freeze([]), keyMask: NO_TAG_KEYS };
|
|
73
|
+
/**
|
|
74
|
+
* Renders a tag value for a diagnostic, never throwing.
|
|
75
|
+
*
|
|
76
|
+
* @remarks A tag value is caller data — a bigint, a null-prototype object, a throwing `toString` —
|
|
77
|
+
* so stringifying it must not become the error that masks the real one.
|
|
78
|
+
*
|
|
79
|
+
* @since 0.11.0
|
|
80
|
+
*/
|
|
81
|
+
export function stringifyTagValue(value) {
|
|
82
|
+
try {
|
|
83
|
+
return String(value);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return "<unprintable>";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
55
89
|
/**
|
|
56
90
|
* Formats a slot for diagnostics — `default`, or its `name:`/`tag:` parts.
|
|
57
91
|
*
|
|
@@ -70,10 +104,22 @@ export function bindingSlotToString(slot) {
|
|
|
70
104
|
if (criterion.key === slotName) {
|
|
71
105
|
continue;
|
|
72
106
|
}
|
|
73
|
-
parts.push(`tag:${criterion.key.name}=${
|
|
107
|
+
parts.push(`tag:${criterion.key.name}=${stringifyTagValue(criterion.value)}`);
|
|
74
108
|
}
|
|
75
109
|
return parts.join(",");
|
|
76
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The scope a binding resolves under.
|
|
113
|
+
*
|
|
114
|
+
* @remarks Every kind declares one — an alias declares `transient`, since it defers scoping to the
|
|
115
|
+
* binding it points at — so this is a field read, kept as a named function because it is the
|
|
116
|
+
* vocabulary validation and introspection speak.
|
|
117
|
+
*
|
|
118
|
+
* @since 0.3.16-canary.0
|
|
119
|
+
*/
|
|
120
|
+
export function effectiveBindingScope(binding) {
|
|
121
|
+
return binding.scope;
|
|
122
|
+
}
|
|
77
123
|
// ── ID generation ────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
78
124
|
let bindingIdCounter = 0;
|
|
79
125
|
/**
|
|
@@ -86,29 +132,20 @@ export function generateBindingId() {
|
|
|
86
132
|
return bindingIdCounter;
|
|
87
133
|
}
|
|
88
134
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* @since 0.10.0
|
|
92
|
-
*/
|
|
93
|
-
export function writablePredicate(binding) {
|
|
94
|
-
return binding;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Narrows a registered binding to its membership flag for the registry to set.
|
|
135
|
+
* The stamp of a binding whose activation need has not been computed under the current versions.
|
|
98
136
|
*
|
|
99
|
-
* @since 0.
|
|
137
|
+
* @since 0.11.0
|
|
100
138
|
*/
|
|
101
|
-
export
|
|
102
|
-
return binding;
|
|
103
|
-
}
|
|
139
|
+
export const NO_ACTIVATION_STAMP = -1;
|
|
104
140
|
/**
|
|
105
|
-
* Drops the memoized resolution frame, for a
|
|
141
|
+
* Drops the memoized resolution frame, for a scope change that alters what the frame reports.
|
|
106
142
|
*
|
|
107
|
-
* @remarks The frame derives from `scope` and `slot
|
|
108
|
-
*
|
|
143
|
+
* @remarks The frame derives from `scope` and `slot`; the slot is final once the binding registers, so
|
|
144
|
+
* only a scope written in place on the registered object clears it.
|
|
109
145
|
*
|
|
110
146
|
* @since 0.5.0-canary.9
|
|
111
147
|
*/
|
|
112
148
|
export function clearBindingFrame(binding) {
|
|
113
149
|
binding.frame = undefined;
|
|
150
|
+
binding.rootContext = undefined;
|
|
114
151
|
}
|
package/dist/core/module.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type { BindToBuilder } from "#core/binding";
|
|
1
|
+
import type { BindToBuilder } from "#core/binding-builders";
|
|
2
|
+
import type { BindingDeclaration, DeclaredBinding } from "#core/binding-declaration";
|
|
2
3
|
import type { Token } from "#core/token";
|
|
3
4
|
import type { Constructor } from "#core/types";
|
|
4
5
|
declare const SYNC_MODULE_BRAND: unique symbol;
|
|
5
6
|
declare const ASYNC_MODULE_BRAND: unique symbol;
|
|
6
7
|
/**
|
|
7
|
-
* Key for
|
|
8
|
-
*
|
|
8
|
+
* Key for what a module applies when it loads: its setup callback, or a declared module's
|
|
9
|
+
* declarations. A symbol (not exported from the package root) keeps it out of autocomplete.
|
|
9
10
|
*
|
|
10
11
|
* @since 0.5.0-canary.7
|
|
11
12
|
*/
|
|
@@ -18,7 +19,7 @@ export declare const MODULE_SETUP: unique symbol;
|
|
|
18
19
|
export interface SyncModule {
|
|
19
20
|
readonly name: string;
|
|
20
21
|
readonly [SYNC_MODULE_BRAND]: true;
|
|
21
|
-
readonly [MODULE_SETUP]: (builder: ModuleBuilder) => void
|
|
22
|
+
readonly [MODULE_SETUP]: ((builder: ModuleBuilder) => void) | ReadonlyArray<DeclaredBinding>;
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* A named group of bindings whose setup is async, applied via `loadAsync()`.
|
|
@@ -55,6 +56,7 @@ export interface AsyncModuleBuilder {
|
|
|
55
56
|
*/
|
|
56
57
|
export declare const SyncModule: {
|
|
57
58
|
create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule;
|
|
59
|
+
fromBindings(name: string, declarations: ReadonlyArray<BindingDeclaration>): SyncModule;
|
|
58
60
|
};
|
|
59
61
|
/**
|
|
60
62
|
* The companion factory that creates `AsyncModule` values.
|
|
@@ -65,12 +67,13 @@ export declare const AsyncModule: {
|
|
|
65
67
|
create(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
|
|
66
68
|
};
|
|
67
69
|
/**
|
|
68
|
-
* The unified module factory — `create` for sync modules, `createAsync` for async ones.
|
|
70
|
+
* The unified module factory — `create` and `fromBindings` for sync modules, `createAsync` for async ones.
|
|
69
71
|
*
|
|
70
72
|
* @since 0.3.16-canary.0
|
|
71
73
|
*/
|
|
72
74
|
export declare const Module: {
|
|
73
75
|
create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule;
|
|
76
|
+
fromBindings(name: string, declarations: ReadonlyArray<BindingDeclaration>): SyncModule;
|
|
74
77
|
createAsync(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
|
|
75
78
|
};
|
|
76
79
|
/**
|
package/dist/core/module.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { asDeclaredBinding } from "#core/binding-declaration";
|
|
1
2
|
// ── Branded types (runtime symbols for branding) ─────────────────────────────────────────────────────────────────────
|
|
2
3
|
const SYNC_MODULE_BRAND = Symbol("di:sync-module");
|
|
3
4
|
const ASYNC_MODULE_BRAND = Symbol("di:async-module");
|
|
4
5
|
/**
|
|
5
|
-
* Key for
|
|
6
|
-
*
|
|
6
|
+
* Key for what a module applies when it loads: its setup callback, or a declared module's
|
|
7
|
+
* declarations. A symbol (not exported from the package root) keeps it out of autocomplete.
|
|
7
8
|
*
|
|
8
9
|
* @since 0.5.0-canary.7
|
|
9
10
|
*/
|
|
@@ -22,6 +23,16 @@ export const SyncModule = {
|
|
|
22
23
|
[MODULE_SETUP]: setup,
|
|
23
24
|
};
|
|
24
25
|
},
|
|
26
|
+
fromBindings(name, declarations) {
|
|
27
|
+
// Copied, so a later write to the caller's list cannot change what the module loads. Not frozen:
|
|
28
|
+
// every load walks this list, and V8 reads a frozen array's elements on a slower path.
|
|
29
|
+
const declared = declarations.map((declaration, index) => asDeclaredBinding(declaration, name, index));
|
|
30
|
+
return {
|
|
31
|
+
name,
|
|
32
|
+
[SYNC_MODULE_BRAND]: true,
|
|
33
|
+
[MODULE_SETUP]: declared,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
25
36
|
};
|
|
26
37
|
/**
|
|
27
38
|
* The companion factory that creates `AsyncModule` values.
|
|
@@ -39,7 +50,7 @@ export const AsyncModule = {
|
|
|
39
50
|
};
|
|
40
51
|
// ── Module — unified API ─────────────────────────────────────────────────────────────────────────────────────────────
|
|
41
52
|
/**
|
|
42
|
-
* The unified module factory — `create` for sync modules, `createAsync` for async ones.
|
|
53
|
+
* The unified module factory — `create` and `fromBindings` for sync modules, `createAsync` for async ones.
|
|
43
54
|
*
|
|
44
55
|
* @since 0.3.16-canary.0
|
|
45
56
|
*/
|
|
@@ -47,6 +58,9 @@ export const Module = {
|
|
|
47
58
|
create(name, setup) {
|
|
48
59
|
return SyncModule.create(name, setup);
|
|
49
60
|
},
|
|
61
|
+
fromBindings(name, declarations) {
|
|
62
|
+
return SyncModule.fromBindings(name, declarations);
|
|
63
|
+
},
|
|
50
64
|
createAsync(name, setup) {
|
|
51
65
|
return AsyncModule.create(name, setup);
|
|
52
66
|
},
|
package/dist/core/registry.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type { Binding } from "#core/binding";
|
|
2
2
|
import type { BindingTag } from "#core/tag";
|
|
3
3
|
import type { Token } from "#core/token";
|
|
4
|
-
import type {
|
|
4
|
+
import type { BindingIdentifier, Constructor } from "#core/types";
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @since 0.3.16-canary.0
|
|
6
|
+
* @since 0.11.0
|
|
9
7
|
*/
|
|
10
8
|
export declare class BindingRegistry {
|
|
11
9
|
#private;
|
|
@@ -13,6 +11,8 @@ export declare class BindingRegistry {
|
|
|
13
11
|
get version(): number;
|
|
14
12
|
/** Whether a constant has ever been registered here, and so whether teardown has anything to sweep. */
|
|
15
13
|
get hasHeldConstantBinding(): boolean;
|
|
14
|
+
/** Whether an alias has ever been registered here, and so whether a candidate list can hold one. */
|
|
15
|
+
get hasHeldAlias(): boolean;
|
|
16
16
|
/** Whether the deferred tagged-slot index has had to be built. */
|
|
17
17
|
get isTaggedIndexBuilt(): boolean;
|
|
18
18
|
/** Whether an id-keyed operation has had to build the id index. */
|
|
@@ -49,8 +49,6 @@ export declare class BindingRegistry {
|
|
|
49
49
|
* to ask, and a lone binding's one-element list is never materialised here.
|
|
50
50
|
*/
|
|
51
51
|
getRecorded(token: Token<unknown> | Constructor): ReadonlyArray<Binding>;
|
|
52
|
-
/** How many bindings a token holds, without materialising a lone binding's list. */
|
|
53
|
-
countBindings(token: Token<unknown> | Constructor): number;
|
|
54
52
|
/** Get binding by ID. */
|
|
55
53
|
getById(id: BindingIdentifier): Binding | undefined;
|
|
56
54
|
/** Check if any binding exists for token. */
|
|
@@ -59,8 +57,6 @@ export declare class BindingRegistry {
|
|
|
59
57
|
allBindings(): ReadonlyArray<Binding>;
|
|
60
58
|
/** Remove all bindings. Returns all removed. */
|
|
61
59
|
clear(): ReadonlyArray<Binding>;
|
|
62
|
-
/** Whether a slot-based binding currently occupies `binding`'s slot, so adding it would displace. */
|
|
63
|
-
hasSlotOccupant(binding: Binding): boolean;
|
|
64
60
|
/**
|
|
65
61
|
* The binding indexed under one criterion.
|
|
66
62
|
*
|
|
@@ -77,30 +73,23 @@ export declare class BindingRegistry {
|
|
|
77
73
|
* against the request — first-criterion bucketing only guarantees each candidate appears once.
|
|
78
74
|
*/
|
|
79
75
|
getMultiTagged(token: Token<unknown> | Constructor, criterion: BindingTag): ReadonlyArray<Binding> | undefined;
|
|
80
|
-
/** A token's lone default-slot binding — the first read of every synchronous resolve. */
|
|
81
|
-
getFastDefault(token: Token<unknown> | Constructor): Binding | undefined;
|
|
82
76
|
/**
|
|
83
|
-
*
|
|
84
|
-
* whether it was live; the caller registers it again with `add`.
|
|
77
|
+
* Whether any binding for the token carries a `when()` predicate.
|
|
85
78
|
*
|
|
86
|
-
* @remarks
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*/
|
|
90
|
-
reslot(binding: Binding, rewrite: () => void): boolean;
|
|
91
|
-
/**
|
|
92
|
-
* Marks a live binding as a collection member in place, moving it out of the lone map: a member is
|
|
93
|
-
* never the token's lone default answer, and nothing else indexes on membership.
|
|
79
|
+
* @remarks Only a token that keeps a record can hold a predicate — a lone binding is default-slot
|
|
80
|
+
* with none — so an index fast lane consults this to decline to full selection when the
|
|
81
|
+
* more-specific rule's predicate step could apply.
|
|
94
82
|
*/
|
|
95
|
-
|
|
83
|
+
hasPredicateCandidate(token: Token<unknown> | Constructor): boolean;
|
|
84
|
+
/** The binding holding a token's default slot — lone or recorded — or `undefined` when none does. */
|
|
85
|
+
getDefaultSlotBinding(token: Token<unknown> | Constructor): Binding | undefined;
|
|
96
86
|
/**
|
|
97
|
-
*
|
|
87
|
+
* The token's default-slot alias, which forwards a request's criteria to its target, or `undefined`.
|
|
98
88
|
*
|
|
99
|
-
* @remarks
|
|
100
|
-
*
|
|
101
|
-
* binding moves to a record because the lone map holds default-slot bindings with no predicate.
|
|
89
|
+
* @remarks A registry that never held an alias answers without probing, so a request missing every slot of a
|
|
90
|
+
* token pays for forwarding only where forwarding can happen.
|
|
102
91
|
*/
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
|
|
92
|
+
defaultSlotAlias(token: Token<unknown> | Constructor): Binding | undefined;
|
|
93
|
+
/** A token's lone default-slot binding — the first read of every synchronous resolve. */
|
|
94
|
+
getFastDefault(token: Token<unknown> | Constructor): Binding | undefined;
|
|
106
95
|
}
|