@codefast/di 0.7.0 → 0.8.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 +33 -0
- package/README.md +1 -1
- package/dist/ambient/active-container.d.ts +4 -0
- package/dist/ambient/active-container.d.ts.map +1 -1
- package/dist/ambient/active-container.js +4 -0
- package/dist/ambient/active-container.js.map +1 -1
- package/dist/container/binding-builders.d.ts.map +1 -1
- package/dist/container/binding-builders.js +4 -5
- package/dist/container/binding-builders.js.map +1 -1
- package/dist/container/container.d.ts +6 -0
- package/dist/container/container.d.ts.map +1 -1
- package/dist/container/container.js +5 -4
- package/dist/container/container.js.map +1 -1
- package/dist/core/binding.d.ts +15 -2
- package/dist/core/binding.d.ts.map +1 -1
- package/dist/core/binding.js +29 -4
- package/dist/core/binding.js.map +1 -1
- package/dist/core/module.d.ts +16 -0
- package/dist/core/module.d.ts.map +1 -1
- package/dist/core/module.js +8 -0
- package/dist/core/module.js.map +1 -1
- package/dist/core/registry.d.ts +2 -3
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +16 -61
- package/dist/core/registry.js.map +1 -1
- package/dist/core/tag.d.ts +26 -0
- package/dist/core/tag.d.ts.map +1 -1
- package/dist/core/tag.js +54 -11
- package/dist/core/tag.js.map +1 -1
- package/dist/core/token.d.ts +6 -0
- package/dist/core/token.d.ts.map +1 -1
- package/dist/core/token.js +4 -0
- package/dist/core/token.js.map +1 -1
- package/dist/core/types.d.ts +20 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/decorators/inject.d.ts +2 -0
- package/dist/decorators/inject.d.ts.map +1 -1
- package/dist/decorators/inject.js +2 -0
- package/dist/decorators/inject.js.map +1 -1
- package/dist/decorators/injectable.d.ts +6 -0
- package/dist/decorators/injectable.d.ts.map +1 -1
- package/dist/decorators/injectable.js +4 -0
- package/dist/decorators/injectable.js.map +1 -1
- package/dist/decorators/lifecycle-decorators.d.ts +4 -0
- package/dist/decorators/lifecycle-decorators.d.ts.map +1 -1
- package/dist/decorators/lifecycle-decorators.js +4 -0
- package/dist/decorators/lifecycle-decorators.js.map +1 -1
- package/dist/errors/errors.d.ts +34 -0
- package/dist/errors/errors.d.ts.map +1 -1
- package/dist/errors/errors.js +60 -3
- package/dist/errors/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/injection/descriptor.d.ts +12 -0
- package/dist/injection/descriptor.d.ts.map +1 -1
- package/dist/injection/descriptor.js +8 -0
- package/dist/injection/descriptor.js.map +1 -1
- package/dist/injection/resolve-options.d.ts +17 -12
- package/dist/injection/resolve-options.d.ts.map +1 -1
- package/dist/injection/resolve-options.js +69 -15
- package/dist/injection/resolve-options.js.map +1 -1
- package/dist/introspection/dependency-graph.d.ts +6 -0
- package/dist/introspection/dependency-graph.d.ts.map +1 -1
- package/dist/introspection/dependency-graph.js +24 -9
- package/dist/introspection/dependency-graph.js.map +1 -1
- package/dist/introspection/graph-adapters/cytoscape.d.ts +8 -0
- package/dist/introspection/graph-adapters/cytoscape.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/cytoscape.js +2 -0
- package/dist/introspection/graph-adapters/cytoscape.js.map +1 -1
- package/dist/introspection/graph-adapters/dot.d.ts +2 -0
- package/dist/introspection/graph-adapters/dot.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/dot.js +2 -0
- package/dist/introspection/graph-adapters/dot.js.map +1 -1
- package/dist/introspection/graph-adapters/reactflow.d.ts +8 -0
- package/dist/introspection/graph-adapters/reactflow.d.ts.map +1 -1
- package/dist/introspection/graph-adapters/reactflow.js +2 -0
- package/dist/introspection/graph-adapters/reactflow.js.map +1 -1
- package/dist/introspection/inspector.d.ts +6 -0
- package/dist/introspection/inspector.d.ts.map +1 -1
- package/dist/introspection/inspector.js +2 -0
- package/dist/introspection/inspector.js.map +1 -1
- package/dist/lifecycle/lifecycle-manager.d.ts +2 -0
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -1
- package/dist/lifecycle/lifecycle-manager.js +2 -0
- package/dist/lifecycle/lifecycle-manager.js.map +1 -1
- package/dist/lifecycle/scope-manager.d.ts +2 -0
- package/dist/lifecycle/scope-manager.d.ts.map +1 -1
- package/dist/lifecycle/scope-manager.js +2 -0
- package/dist/lifecycle/scope-manager.js.map +1 -1
- package/dist/metadata/metadata-keys.d.ts +6 -0
- package/dist/metadata/metadata-keys.d.ts.map +1 -1
- package/dist/metadata/metadata-keys.js +6 -0
- package/dist/metadata/metadata-keys.js.map +1 -1
- package/dist/metadata/metadata-reader-token.d.ts +2 -0
- package/dist/metadata/metadata-reader-token.d.ts.map +1 -1
- package/dist/metadata/metadata-reader-token.js +2 -0
- package/dist/metadata/metadata-reader-token.js.map +1 -1
- package/dist/metadata/metadata-types.d.ts +6 -0
- package/dist/metadata/metadata-types.d.ts.map +1 -1
- package/dist/metadata/symbol-metadata-reader.d.ts +4 -0
- package/dist/metadata/symbol-metadata-reader.d.ts.map +1 -1
- package/dist/metadata/symbol-metadata-reader.js +4 -0
- package/dist/metadata/symbol-metadata-reader.js.map +1 -1
- package/dist/resolution/cache/activation-need.d.ts +2 -0
- package/dist/resolution/cache/activation-need.d.ts.map +1 -1
- package/dist/resolution/cache/activation-need.js +2 -0
- package/dist/resolution/cache/activation-need.js.map +1 -1
- package/dist/resolution/cache/binding-lookup-cache.d.ts +2 -2
- package/dist/resolution/cache/binding-lookup-cache.d.ts.map +1 -1
- package/dist/resolution/cache/binding-lookup-cache.js +2 -37
- package/dist/resolution/cache/binding-lookup-cache.js.map +1 -1
- package/dist/resolution/cache/class-introspector.d.ts +2 -0
- package/dist/resolution/cache/class-introspector.d.ts.map +1 -1
- package/dist/resolution/cache/class-introspector.js +2 -0
- package/dist/resolution/cache/class-introspector.js.map +1 -1
- package/dist/resolution/context.d.ts +6 -0
- package/dist/resolution/context.d.ts.map +1 -1
- package/dist/resolution/context.js +4 -0
- package/dist/resolution/context.js.map +1 -1
- package/dist/resolution/plan/instantiation-plan.d.ts +6 -12
- package/dist/resolution/plan/instantiation-plan.d.ts.map +1 -1
- package/dist/resolution/plan/instantiation-plan.js +11 -27
- package/dist/resolution/plan/instantiation-plan.js.map +1 -1
- package/dist/resolution/resolver.d.ts +14 -5
- package/dist/resolution/resolver.d.ts.map +1 -1
- package/dist/resolution/resolver.js +108 -141
- package/dist/resolution/resolver.js.map +1 -1
- package/dist/resolution/select/binding-select.d.ts +4 -5
- package/dist/resolution/select/binding-select.d.ts.map +1 -1
- package/dist/resolution/select/binding-select.js +18 -20
- package/dist/resolution/select/binding-select.js.map +1 -1
- package/dist/resolution/select/constraints.d.ts +16 -0
- package/dist/resolution/select/constraints.d.ts.map +1 -1
- package/dist/resolution/select/constraints.js +16 -0
- package/dist/resolution/select/constraints.js.map +1 -1
- package/package.json +13 -6
- package/src/ambient/active-container.ts +4 -0
- package/src/container/binding-builders.ts +10 -5
- package/src/container/container.ts +9 -4
- package/src/core/binding.ts +33 -5
- package/src/core/module.ts +16 -0
- package/src/core/registry.ts +16 -66
- package/src/core/tag.ts +71 -17
- package/src/core/token.ts +6 -0
- package/src/core/types.ts +20 -0
- package/src/decorators/inject.ts +2 -0
- package/src/decorators/injectable.ts +8 -0
- package/src/decorators/lifecycle-decorators.ts +4 -0
- package/src/errors/errors.ts +62 -5
- package/src/index.ts +1 -1
- package/src/injection/descriptor.ts +12 -0
- package/src/injection/resolve-options.ts +75 -18
- package/src/introspection/dependency-graph.ts +29 -9
- package/src/introspection/graph-adapters/cytoscape.ts +8 -0
- package/src/introspection/graph-adapters/dot.ts +2 -0
- package/src/introspection/graph-adapters/reactflow.ts +8 -0
- package/src/introspection/inspector.ts +6 -0
- package/src/lifecycle/lifecycle-manager.ts +2 -0
- package/src/lifecycle/scope-manager.ts +2 -0
- package/src/metadata/metadata-keys.ts +6 -0
- package/src/metadata/metadata-reader-token.ts +2 -0
- package/src/metadata/metadata-types.ts +6 -0
- package/src/metadata/symbol-metadata-reader.ts +4 -0
- package/src/resolution/cache/activation-need.ts +2 -0
- package/src/resolution/cache/binding-lookup-cache.ts +2 -40
- package/src/resolution/cache/class-introspector.ts +2 -0
- package/src/resolution/context.ts +6 -0
- package/src/resolution/plan/instantiation-plan.ts +15 -38
- package/src/resolution/resolver.ts +122 -141
- package/src/resolution/select/binding-select.ts +18 -21
- package/src/resolution/select/constraints.ts +16 -0
package/src/errors/errors.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import type { BindingTag } from "#/core/tag";
|
|
2
|
+
import { slotName } from "#/core/tag";
|
|
1
3
|
import type { BindingIdentifier, BindingScope, ResolveOptions } from "#/core/types";
|
|
2
4
|
|
|
3
5
|
/**
|
|
6
|
+
* Base class for every error the library throws, each carrying a machine-readable `code`.
|
|
7
|
+
*
|
|
4
8
|
* @since 0.3.16-canary.0
|
|
5
9
|
*/
|
|
6
10
|
export abstract class DiError extends Error {
|
|
@@ -13,6 +17,8 @@ export abstract class DiError extends Error {
|
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
/**
|
|
20
|
+
* An internal assertion failure — a library bug, never caller misuse.
|
|
21
|
+
*
|
|
16
22
|
* @since 0.3.16-canary.0
|
|
17
23
|
*/
|
|
18
24
|
export class InternalError extends DiError {
|
|
@@ -24,6 +30,8 @@ export class InternalError extends DiError {
|
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
/**
|
|
33
|
+
* A token with no binding at all, even after walking the parent container chain.
|
|
34
|
+
*
|
|
27
35
|
* @since 0.3.16-canary.0
|
|
28
36
|
*/
|
|
29
37
|
export class TokenNotBoundError extends DiError {
|
|
@@ -37,19 +45,42 @@ export class TokenNotBoundError extends DiError {
|
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
// Options carry caller values a tag may hold — a bigint or a circular object must not make the
|
|
40
|
-
// diagnostic itself throw and mask the real error.
|
|
48
|
+
// diagnostic itself throw and mask the real error. The reserved criterion renders as `name`.
|
|
41
49
|
function describeResolveOptions(options: ResolveOptions): string {
|
|
42
50
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
let name = options.name;
|
|
52
|
+
const criteria: Array<string> = [];
|
|
53
|
+
const add = (criterion: BindingTag): void => {
|
|
54
|
+
if (criterion.key === slotName) {
|
|
55
|
+
name ??= String(criterion.value);
|
|
56
|
+
} else {
|
|
57
|
+
criteria.push(`${criterion.key.name}=${String(criterion.value)}`);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
if (options.tag !== undefined) {
|
|
61
|
+
add(options.tag);
|
|
62
|
+
}
|
|
63
|
+
if (options.tags !== undefined) {
|
|
64
|
+
for (const criterion of options.tags) {
|
|
65
|
+
add(criterion);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const display: { name?: string; tags?: Array<string> } = {};
|
|
69
|
+
if (name !== undefined) {
|
|
70
|
+
display.name = name;
|
|
71
|
+
}
|
|
72
|
+
if (criteria.length > 0) {
|
|
73
|
+
display.tags = criteria;
|
|
74
|
+
}
|
|
75
|
+
return JSON.stringify(display) ?? "undefined";
|
|
47
76
|
} catch {
|
|
48
77
|
return "[unserializable options]";
|
|
49
78
|
}
|
|
50
79
|
}
|
|
51
80
|
|
|
52
81
|
/**
|
|
82
|
+
* A token that has bindings, none of whose slots match the given `ResolveOptions`.
|
|
83
|
+
*
|
|
53
84
|
* @since 0.3.16-canary.0
|
|
54
85
|
*/
|
|
55
86
|
export class NoMatchingBindingError extends DiError {
|
|
@@ -69,6 +100,8 @@ export class NoMatchingBindingError extends DiError {
|
|
|
69
100
|
}
|
|
70
101
|
|
|
71
102
|
/**
|
|
103
|
+
* Two or more candidate bindings that matched without the more-specific rule deciding a winner.
|
|
104
|
+
*
|
|
72
105
|
* @since 0.3.16-canary.0
|
|
73
106
|
*/
|
|
74
107
|
export class AmbiguousBindingError extends DiError {
|
|
@@ -86,6 +119,8 @@ export class AmbiguousBindingError extends DiError {
|
|
|
86
119
|
}
|
|
87
120
|
|
|
88
121
|
/**
|
|
122
|
+
* A dependency cycle detected along the resolution path, alias chains included.
|
|
123
|
+
*
|
|
89
124
|
* @since 0.3.16-canary.0
|
|
90
125
|
*/
|
|
91
126
|
export class CircularDependencyError extends DiError {
|
|
@@ -99,6 +134,8 @@ export class CircularDependencyError extends DiError {
|
|
|
99
134
|
}
|
|
100
135
|
|
|
101
136
|
/**
|
|
137
|
+
* A sync `resolve()` of a token whose factory — or a dependency's factory — is async.
|
|
138
|
+
*
|
|
102
139
|
* @since 0.3.16-canary.0
|
|
103
140
|
*/
|
|
104
141
|
export class AsyncResolutionError extends DiError {
|
|
@@ -120,6 +157,8 @@ export class AsyncResolutionError extends DiError {
|
|
|
120
157
|
}
|
|
121
158
|
|
|
122
159
|
/**
|
|
160
|
+
* A sync `unbind()` of a binding whose `onDeactivation` handler is async.
|
|
161
|
+
*
|
|
123
162
|
* @since 0.3.16-canary.0
|
|
124
163
|
*/
|
|
125
164
|
export class AsyncDeactivationError extends DiError {
|
|
@@ -133,6 +172,8 @@ export class AsyncDeactivationError extends DiError {
|
|
|
133
172
|
}
|
|
134
173
|
|
|
135
174
|
/**
|
|
175
|
+
* The tokens, scopes, and resolution path describing a captive-dependency violation.
|
|
176
|
+
*
|
|
136
177
|
* @since 0.3.16-canary.0
|
|
137
178
|
*/
|
|
138
179
|
export interface ScopeViolationDetails {
|
|
@@ -144,6 +185,8 @@ export interface ScopeViolationDetails {
|
|
|
144
185
|
}
|
|
145
186
|
|
|
146
187
|
/**
|
|
188
|
+
* A captive dependency — a longer-lived binding depending on a shorter-lived one.
|
|
189
|
+
*
|
|
147
190
|
* @since 0.3.16-canary.0
|
|
148
191
|
*/
|
|
149
192
|
export class ScopeViolationError extends DiError {
|
|
@@ -251,6 +294,8 @@ export class InvalidMetadataError extends DiError {
|
|
|
251
294
|
}
|
|
252
295
|
|
|
253
296
|
/**
|
|
297
|
+
* A class the container must construct but that carries no `@injectable()` metadata.
|
|
298
|
+
*
|
|
254
299
|
* @since 0.3.16-canary.0
|
|
255
300
|
*/
|
|
256
301
|
export class MissingMetadataError extends DiError {
|
|
@@ -266,6 +311,8 @@ export class MissingMetadataError extends DiError {
|
|
|
266
311
|
}
|
|
267
312
|
|
|
268
313
|
/**
|
|
314
|
+
* A sync `load()` given a module that needs async setup.
|
|
315
|
+
*
|
|
269
316
|
* @since 0.3.16-canary.0
|
|
270
317
|
*/
|
|
271
318
|
export class AsyncModuleLoadError extends DiError {
|
|
@@ -279,6 +326,8 @@ export class AsyncModuleLoadError extends DiError {
|
|
|
279
326
|
}
|
|
280
327
|
|
|
281
328
|
/**
|
|
329
|
+
* A synchronous disposal attempt on a container whose `onDeactivation` handlers may be async.
|
|
330
|
+
*
|
|
282
331
|
* @since 0.3.16-canary.0
|
|
283
332
|
*/
|
|
284
333
|
export class SyncDisposalNotSupportedError extends DiError {
|
|
@@ -292,6 +341,8 @@ export class SyncDisposalNotSupportedError extends DiError {
|
|
|
292
341
|
}
|
|
293
342
|
|
|
294
343
|
/**
|
|
344
|
+
* A `scoped` binding resolved from a container with no child scope context.
|
|
345
|
+
*
|
|
295
346
|
* @since 0.3.16-canary.0
|
|
296
347
|
*/
|
|
297
348
|
export class MissingScopeContextError extends DiError {
|
|
@@ -355,6 +406,8 @@ export class ChainNotRegisteredError extends DiError {
|
|
|
355
406
|
}
|
|
356
407
|
|
|
357
408
|
/**
|
|
409
|
+
* A `rebind()` of a token that has no own binding in this container.
|
|
410
|
+
*
|
|
358
411
|
* @since 0.3.16-canary.0
|
|
359
412
|
*/
|
|
360
413
|
export class RebindUnboundTokenError extends DiError {
|
|
@@ -410,6 +463,8 @@ export class StaticMemberDecoratorError extends DiError {
|
|
|
410
463
|
}
|
|
411
464
|
|
|
412
465
|
/**
|
|
466
|
+
* An operation attempted on a container that has already been disposed.
|
|
467
|
+
*
|
|
413
468
|
* @since 0.3.16-canary.0
|
|
414
469
|
*/
|
|
415
470
|
export class DisposedContainerError extends DiError {
|
|
@@ -421,6 +476,8 @@ export class DisposedContainerError extends DiError {
|
|
|
421
476
|
}
|
|
422
477
|
|
|
423
478
|
/**
|
|
479
|
+
* A `@postConstruct` or `onActivation` hook that returned a `Promise` on a sync resolution path.
|
|
480
|
+
*
|
|
424
481
|
* @since 0.3.16-canary.0
|
|
425
482
|
*/
|
|
426
483
|
export class AsyncActivationError extends DiError {
|
package/src/index.ts
CHANGED
|
@@ -21,7 +21,7 @@ export { token, tokenName } from "#/core/token";
|
|
|
21
21
|
export type { Token } from "#/core/token";
|
|
22
22
|
|
|
23
23
|
// Tag — the interned slot criteria a `whenTagged` and a resolve both take
|
|
24
|
-
export { coversTagKeys, NO_TAG_KEYS, tag, tagKeyMaskOf } from "#/core/tag";
|
|
24
|
+
export { coversTagKeys, NO_TAG_KEYS, slotName, tag, tagKeyMaskOf } from "#/core/tag";
|
|
25
25
|
export type { TagKey, TagKeyMask } from "#/core/tag";
|
|
26
26
|
|
|
27
27
|
// Binding builders — types only
|
|
@@ -4,6 +4,8 @@ import type { BindingTag, Constructor, TokenValue } from "#/core/types";
|
|
|
4
4
|
import type { DependencySlot } from "#/injection/resolve-options";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
+
* Slot-selection options — a name and tags — a declared dependency narrows its binding with.
|
|
8
|
+
*
|
|
7
9
|
* @since 0.3.16-canary.0
|
|
8
10
|
*/
|
|
9
11
|
export interface InjectOptions {
|
|
@@ -31,6 +33,8 @@ export interface InjectionDescriptor<out Value = unknown> extends DependencySlot
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
/**
|
|
36
|
+
* A dependency as declared: a bare token, a class, or a full injection descriptor.
|
|
37
|
+
*
|
|
34
38
|
* @since 0.3.16-canary.0
|
|
35
39
|
*/
|
|
36
40
|
export type InjectableDependency<Value = unknown> = Token<Value> | Constructor<Value> | InjectionDescriptor<Value>;
|
|
@@ -60,6 +64,8 @@ export type ResolvedDependencyValue<Dependency> = Dependency extends { readonly
|
|
|
60
64
|
type DescribedValue<Dependency> = Dependency extends InjectionDescriptor<infer Value> ? Value : TokenValue<Dependency>;
|
|
61
65
|
|
|
62
66
|
/**
|
|
67
|
+
* Returns whether the value is an `InjectionDescriptor`, dual-role `inject()` functions included.
|
|
68
|
+
*
|
|
63
69
|
* @since 0.3.16-canary.0
|
|
64
70
|
*/
|
|
65
71
|
export function isInjectionDescriptor(value: unknown): value is InjectionDescriptor {
|
|
@@ -81,6 +87,8 @@ export function isInjectionDescriptor(value: unknown): value is InjectionDescrip
|
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
/**
|
|
90
|
+
* Normalises a declared dependency to a plain `InjectionDescriptor`.
|
|
91
|
+
*
|
|
84
92
|
* @since 0.3.16-canary.0
|
|
85
93
|
*/
|
|
86
94
|
export function normalizeToDescriptor(dependency: InjectableDependency): InjectionDescriptor {
|
|
@@ -172,6 +180,8 @@ export function buildInjectionDescriptor<Value>(
|
|
|
172
180
|
}
|
|
173
181
|
|
|
174
182
|
/**
|
|
183
|
+
* Creates a descriptor that resolves to `undefined` instead of throwing when no binding matches.
|
|
184
|
+
*
|
|
175
185
|
* @since 0.3.16-canary.0
|
|
176
186
|
*/
|
|
177
187
|
export function optional<Value>(
|
|
@@ -189,6 +199,8 @@ export function optional<Value>(
|
|
|
189
199
|
}
|
|
190
200
|
|
|
191
201
|
/**
|
|
202
|
+
* Creates a descriptor that resolves every matching binding for the token into an array.
|
|
203
|
+
*
|
|
192
204
|
* @since 0.3.16-canary.0
|
|
193
205
|
*/
|
|
194
206
|
export function injectAll<Value>(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BindingTag } from "#/core/tag";
|
|
2
|
+
import { slotName, slotNameCriterionOf } from "#/core/tag";
|
|
2
3
|
import type { Token } from "#/core/token";
|
|
3
4
|
import type { Constructor, ResolveOptions } from "#/core/types";
|
|
4
5
|
|
|
@@ -19,37 +20,39 @@ export interface DependencySlot {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
23
|
+
* The lone criterion of a request that carries exactly one, whatever its spelling — the shape the
|
|
24
|
+
* registry has a direct index for.
|
|
23
25
|
*
|
|
24
|
-
* @
|
|
25
|
-
|
|
26
|
-
export function isNameOnlyOptions(options: ResolveOptions): options is ResolveOptions & { name: string } {
|
|
27
|
-
return (
|
|
28
|
-
options.name !== undefined && options.tag === undefined && (options.tags === undefined || options.tags.length === 0)
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The lone tag of a request that asks for exactly one, written either way — the shape the registry
|
|
34
|
-
* has a direct tag index for.
|
|
35
|
-
*
|
|
36
|
-
* @remarks Both spellings answer here, so the index is not something one of them silently misses.
|
|
26
|
+
* @remarks Every spelling answers here, so the index is not something one of them silently misses;
|
|
27
|
+
* a name folds to the reserved `slotName` criterion, making the name lane the tag lane.
|
|
37
28
|
*
|
|
38
29
|
* @since 0.5.0-canary.9
|
|
39
30
|
*/
|
|
40
|
-
export function
|
|
41
|
-
if (options
|
|
31
|
+
export function singleCriterionOnlyOf(options: ResolveOptions | undefined): BindingTag | undefined {
|
|
32
|
+
if (options === undefined) {
|
|
42
33
|
return undefined;
|
|
43
34
|
}
|
|
35
|
+
if (options.name !== undefined) {
|
|
36
|
+
return loneNameCriterionOf(options);
|
|
37
|
+
}
|
|
44
38
|
const listed = options.tags;
|
|
45
39
|
const shorthand = options.tag;
|
|
46
40
|
if (shorthand !== undefined) {
|
|
47
|
-
// Both sources present means the request carries two tags, which no single-tag index can answer.
|
|
48
41
|
return listed === undefined || listed.length === 0 ? shorthand : undefined;
|
|
49
42
|
}
|
|
50
43
|
return listed !== undefined && listed.length === 1 ? listed[0] : undefined;
|
|
51
44
|
}
|
|
52
45
|
|
|
46
|
+
/** The name spelling's half of the fold, kept apart so the common body stays small enough to inline. */
|
|
47
|
+
function loneNameCriterionOf(options: ResolveOptions): BindingTag | undefined {
|
|
48
|
+
// A name next to any tag means the request carries two criteria, which no single index answers.
|
|
49
|
+
if (options.tag !== undefined || (options.tags !== undefined && options.tags.length > 0)) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
// Read, not minted: a request must not retain a name no binding ever declared.
|
|
53
|
+
return slotNameCriterionOf(options.name as string);
|
|
54
|
+
}
|
|
55
|
+
|
|
53
56
|
/** Shared core: build a ResolveOptions from already-normalised name + tags. */
|
|
54
57
|
function buildOptions(
|
|
55
58
|
name: string | undefined,
|
|
@@ -83,8 +86,12 @@ export function injectionSlotToResolveOptions(
|
|
|
83
86
|
/** Where a slot's derived options are memoized, so the same object is handed out every resolve. */
|
|
84
87
|
const MEMOIZED_RESOLVE_OPTIONS: unique symbol = Symbol("di:resolve-options");
|
|
85
88
|
|
|
89
|
+
/** Where a slot's folded lone criterion is memoized — `null` records "computed: none". */
|
|
90
|
+
const MEMOIZED_SINGLE_CRITERION: unique symbol = Symbol("di:single-criterion");
|
|
91
|
+
|
|
86
92
|
interface SlotWithMemoizedOptions {
|
|
87
93
|
[MEMOIZED_RESOLVE_OPTIONS]?: ResolveOptions;
|
|
94
|
+
[MEMOIZED_SINGLE_CRITERION]?: BindingTag | null;
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
/**
|
|
@@ -126,6 +133,44 @@ function memoizeResolveOptions(
|
|
|
126
133
|
return built;
|
|
127
134
|
}
|
|
128
135
|
|
|
136
|
+
/**
|
|
137
|
+
* The lone criterion a dependency's fixed criteria fold to, memoized on the slot like its options —
|
|
138
|
+
* `null` means the fold answered "none".
|
|
139
|
+
*
|
|
140
|
+
* @remarks A slot's criteria never change after declaration, so the fold happens once per slot
|
|
141
|
+
* rather than per hop — which is what keeps a named dependency's resolve off the intern map. A
|
|
142
|
+
* lone name whose criterion is not interned yet is left unmemoized: a later `whenNamed` binding
|
|
143
|
+
* mints it, and the next fold must see that.
|
|
144
|
+
*
|
|
145
|
+
* @since 0.8.0
|
|
146
|
+
*/
|
|
147
|
+
export function singleCriterionForSlot(injectionSlot: DependencySlot): BindingTag | null {
|
|
148
|
+
const slot = injectionSlot as SlotWithMemoizedOptions;
|
|
149
|
+
const memoized = slot[MEMOIZED_SINGLE_CRITERION];
|
|
150
|
+
if (memoized !== undefined) {
|
|
151
|
+
return memoized;
|
|
152
|
+
}
|
|
153
|
+
const options = resolveOptionsForSlot(injectionSlot);
|
|
154
|
+
const criterion = singleCriterionOnlyOf(options);
|
|
155
|
+
const folded = criterion ?? null;
|
|
156
|
+
if (criterion !== undefined || !isLoneNameOptions(options)) {
|
|
157
|
+
try {
|
|
158
|
+
slot[MEMOIZED_SINGLE_CRITERION] = folded;
|
|
159
|
+
} catch {
|
|
160
|
+
// A frozen slot re-folds on every hop rather than throwing.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return folded;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** A slot request whose only criterion is a name — the one shape whose fold can change after a bind. */
|
|
167
|
+
function isLoneNameOptions(options: ResolveOptions | undefined): boolean {
|
|
168
|
+
// Slot-derived options never carry the `tag` shorthand — `buildOptions` folds it into `tags`.
|
|
169
|
+
return (
|
|
170
|
+
options !== undefined && options.name !== undefined && (options.tags === undefined || options.tags.length === 0)
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
129
174
|
/**
|
|
130
175
|
* Resolve options derived from a binding slot (tags may be empty; omits when nothing to match).
|
|
131
176
|
*
|
|
@@ -140,5 +185,17 @@ export function bindingSlotToResolveOptions(bindingSlot: {
|
|
|
140
185
|
readonly tags?: ReadonlyArray<BindingTag> | undefined;
|
|
141
186
|
}): ResolveOptions | undefined {
|
|
142
187
|
const tags = bindingSlot.tags;
|
|
143
|
-
|
|
188
|
+
let name = bindingSlot.name;
|
|
189
|
+
let criteria: ReadonlyArray<BindingTag> | undefined = tags !== undefined && tags.length > 0 ? tags : undefined;
|
|
190
|
+
if (criteria !== undefined) {
|
|
191
|
+
// A reserved criterion folds into `name` — carried in `tags` it would be said twice or, on a
|
|
192
|
+
// descriptor that spells its name as a tag, not at all.
|
|
193
|
+
const reserved = criteria.find((criterion) => criterion.key === slotName);
|
|
194
|
+
if (reserved !== undefined) {
|
|
195
|
+
name ??= reserved.value as string;
|
|
196
|
+
const rest = criteria.filter((criterion) => criterion.key !== slotName);
|
|
197
|
+
criteria = rest.length > 0 ? rest : undefined;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return buildOptions(name, criteria);
|
|
144
201
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Binding } from "#/core/binding";
|
|
2
2
|
import { effectiveBindingScope } from "#/core/binding-scope";
|
|
3
3
|
import type { BindingRegistry } from "#/core/registry";
|
|
4
|
+
import { slotName } from "#/core/tag";
|
|
4
5
|
import type { Token } from "#/core/token";
|
|
5
6
|
import { tokenName } from "#/core/token";
|
|
6
7
|
import type { BindingKind, BindingScope, Constructor } from "#/core/types";
|
|
@@ -43,6 +44,8 @@ export interface GraphEdge {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
/**
|
|
47
|
+
* A container's dependency graph as plain JSON — nodes, edges, and whether the parent is included.
|
|
48
|
+
*
|
|
46
49
|
* @since 0.3.16-canary.0
|
|
47
50
|
*/
|
|
48
51
|
export interface ContainerGraphJson {
|
|
@@ -52,6 +55,8 @@ export interface ContainerGraphJson {
|
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
/**
|
|
58
|
+
* Options controlling what a dependency graph includes.
|
|
59
|
+
*
|
|
55
60
|
* @since 0.3.16-canary.0
|
|
56
61
|
*/
|
|
57
62
|
export interface GraphOptions {
|
|
@@ -92,12 +97,23 @@ function matchingTargets(candidates: ReadonlyArray<Binding>, ref: DependencySlot
|
|
|
92
97
|
return candidates.filter((candidate) => matchesSlot(candidate.slot, criterion));
|
|
93
98
|
}
|
|
94
99
|
|
|
100
|
+
/** The name a dependency asks for, whichever spelling carries it. */
|
|
101
|
+
function refSlotName(ref: DependencySlot): string | undefined {
|
|
102
|
+
if (ref.name !== undefined) {
|
|
103
|
+
return ref.name;
|
|
104
|
+
}
|
|
105
|
+
const reserved = ref.tags?.find((criterion) => criterion.key === slotName);
|
|
106
|
+
return reserved === undefined ? undefined : String(reserved.value);
|
|
107
|
+
}
|
|
108
|
+
|
|
95
109
|
function edgeLabel(ref: DependencySlot, index: number): string {
|
|
110
|
+
const name = refSlotName(ref);
|
|
111
|
+
const firstPlainTag = ref.tags?.find((criterion) => criterion.key !== slotName);
|
|
96
112
|
const criterion =
|
|
97
|
-
|
|
98
|
-
? `name:${
|
|
99
|
-
:
|
|
100
|
-
? `tag:${
|
|
113
|
+
name !== undefined
|
|
114
|
+
? `name:${name}`
|
|
115
|
+
: firstPlainTag !== undefined
|
|
116
|
+
? `tag:${firstPlainTag.key.name}=${String(firstPlainTag.value)}`
|
|
101
117
|
: `[${index}]`;
|
|
102
118
|
|
|
103
119
|
return ref.optional ? `${criterion} optional` : criterion;
|
|
@@ -166,23 +182,25 @@ function addDependencyEdges(
|
|
|
166
182
|
return;
|
|
167
183
|
}
|
|
168
184
|
|
|
185
|
+
const unboundSlotName = refSlotName(ref);
|
|
169
186
|
accumulator.edges.push({
|
|
170
187
|
from,
|
|
171
188
|
to: unboundNodeIdFor(accumulator, ref.token),
|
|
172
189
|
label,
|
|
173
190
|
optional: true,
|
|
174
|
-
...(
|
|
191
|
+
...(unboundSlotName !== undefined ? { slotName: unboundSlotName } : {}),
|
|
175
192
|
});
|
|
176
193
|
|
|
177
194
|
return;
|
|
178
195
|
}
|
|
179
196
|
|
|
197
|
+
const requestedName = refSlotName(ref);
|
|
180
198
|
for (const target of targets) {
|
|
181
199
|
// A multi dep with no criterion of its own fans out — each edge names the slot it hits.
|
|
182
|
-
const
|
|
200
|
+
const edgeSlotName = target.slot.name ?? requestedName;
|
|
183
201
|
const perTargetLabel =
|
|
184
|
-
ref.multi &&
|
|
185
|
-
? edgeLabel({ ...ref, name:
|
|
202
|
+
ref.multi && requestedName === undefined && edgeSlotName !== undefined
|
|
203
|
+
? edgeLabel({ ...ref, name: edgeSlotName }, index)
|
|
186
204
|
: label;
|
|
187
205
|
|
|
188
206
|
accumulator.edges.push({
|
|
@@ -190,7 +208,7 @@ function addDependencyEdges(
|
|
|
190
208
|
to: target.id,
|
|
191
209
|
label: perTargetLabel,
|
|
192
210
|
optional: ref.optional,
|
|
193
|
-
...(
|
|
211
|
+
...(edgeSlotName !== undefined ? { slotName: edgeSlotName } : {}),
|
|
194
212
|
});
|
|
195
213
|
}
|
|
196
214
|
}
|
|
@@ -255,6 +273,8 @@ function addRegistryBindings(
|
|
|
255
273
|
}
|
|
256
274
|
|
|
257
275
|
/**
|
|
276
|
+
* Builds the JSON dependency graph of a registry's bindings, optionally including the parent's.
|
|
277
|
+
*
|
|
258
278
|
* @since 0.3.16-canary.0
|
|
259
279
|
*/
|
|
260
280
|
export function buildDependencyGraph(
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ContainerGraphJson, GraphNode } from "#/introspection/dependency-graph";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
+
* A dependency-graph node in Cytoscape's element format.
|
|
5
|
+
*
|
|
4
6
|
* @since 0.3.16-canary.0
|
|
5
7
|
*/
|
|
6
8
|
export interface CytoscapeNode {
|
|
@@ -15,6 +17,8 @@ export interface CytoscapeNode {
|
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/**
|
|
20
|
+
* A dependency-graph edge in Cytoscape's element format.
|
|
21
|
+
*
|
|
18
22
|
* @since 0.3.16-canary.0
|
|
19
23
|
*/
|
|
20
24
|
export interface CytoscapeEdge {
|
|
@@ -29,11 +33,15 @@ export interface CytoscapeEdge {
|
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
/**
|
|
36
|
+
* The flat node-and-edge list Cytoscape takes as its `elements` input.
|
|
37
|
+
*
|
|
32
38
|
* @since 0.3.16-canary.0
|
|
33
39
|
*/
|
|
34
40
|
export type CytoscapeElements = ReadonlyArray<CytoscapeNode | CytoscapeEdge>;
|
|
35
41
|
|
|
36
42
|
/**
|
|
43
|
+
* Converts a container's dependency graph into Cytoscape's element list.
|
|
44
|
+
*
|
|
37
45
|
* @since 0.3.16-canary.0
|
|
38
46
|
*/
|
|
39
47
|
export function toCytoscapeGraph(graph: ContainerGraphJson): CytoscapeElements {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ContainerGraphJson, GraphNode } from "#/introspection/dependency-graph";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
+
* A dependency-graph node in React Flow's node format.
|
|
5
|
+
*
|
|
4
6
|
* @since 0.3.16-canary.0
|
|
5
7
|
*/
|
|
6
8
|
export interface ReactFlowNode {
|
|
@@ -16,6 +18,8 @@ export interface ReactFlowNode {
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
/**
|
|
21
|
+
* A dependency-graph edge in React Flow's edge format.
|
|
22
|
+
*
|
|
19
23
|
* @since 0.3.16-canary.0
|
|
20
24
|
*/
|
|
21
25
|
export interface ReactFlowEdge {
|
|
@@ -28,6 +32,8 @@ export interface ReactFlowEdge {
|
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
/**
|
|
35
|
+
* The nodes-and-edges pair a React Flow viewer renders.
|
|
36
|
+
*
|
|
31
37
|
* @since 0.3.16-canary.0
|
|
32
38
|
*/
|
|
33
39
|
export interface ReactFlowGraph {
|
|
@@ -45,6 +51,8 @@ const GRID_CELL_WIDTH_PX = 200;
|
|
|
45
51
|
const GRID_CELL_HEIGHT_PX = 100;
|
|
46
52
|
|
|
47
53
|
/**
|
|
54
|
+
* Converts a container's dependency graph into React Flow nodes and edges on an initial grid.
|
|
55
|
+
*
|
|
48
56
|
* @since 0.5.0-canary.7
|
|
49
57
|
*/
|
|
50
58
|
export function toReactFlowGraph(graph: ContainerGraphJson): ReactFlowGraph {
|
|
@@ -18,6 +18,8 @@ import { selectAllBindings } from "#/resolution/select/binding-select";
|
|
|
18
18
|
// ── Public types ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
+
* A read-only view of one binding: token, kind, scope, slot, and id.
|
|
22
|
+
*
|
|
21
23
|
* @since 0.3.16-canary.0
|
|
22
24
|
*/
|
|
23
25
|
export interface BindingSnapshot {
|
|
@@ -32,6 +34,8 @@ export interface BindingSnapshot {
|
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/**
|
|
37
|
+
* A read-only view of one container's own bindings and state.
|
|
38
|
+
*
|
|
35
39
|
* @since 0.3.16-canary.0
|
|
36
40
|
*/
|
|
37
41
|
export interface ContainerSnapshot {
|
|
@@ -44,6 +48,8 @@ export interface ContainerSnapshot {
|
|
|
44
48
|
// ── Inspector ────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
45
49
|
|
|
46
50
|
/**
|
|
51
|
+
* The read-only introspection surface behind a container's `inspect()`.
|
|
52
|
+
*
|
|
47
53
|
* @since 0.3.16-canary.0
|
|
48
54
|
*/
|
|
49
55
|
export class Inspector {
|
|
@@ -13,6 +13,8 @@ import { AsyncActivationError, AsyncDeactivationError, InvalidMetadataError } fr
|
|
|
13
13
|
import type { MetadataReader } from "#/metadata/metadata-types";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
+
* One container's registry of container-level activation and deactivation hooks, keyed by token.
|
|
17
|
+
*
|
|
16
18
|
* @since 0.3.16-canary.0
|
|
17
19
|
*/
|
|
18
20
|
export class LifecycleManager {
|
|
@@ -5,6 +5,8 @@ import type { BindingIdentifier } from "#/core/types";
|
|
|
5
5
|
import { MissingScopeContextError } from "#/errors/errors";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
+
* One container's instance caches — singletons, in-flight async creations, and the scoped cache.
|
|
9
|
+
*
|
|
8
10
|
* @since 0.3.16-canary.0
|
|
9
11
|
*/
|
|
10
12
|
export class ScopeManager {
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* The metadata key `@injectable` writes a class's constructor dependencies under.
|
|
3
|
+
*
|
|
2
4
|
* @since 0.3.16-canary.0
|
|
3
5
|
*/
|
|
4
6
|
export const INJECTABLE_KEY: unique symbol = Symbol("di:injectable");
|
|
5
7
|
/**
|
|
8
|
+
* The metadata key `@postConstruct` and `@preDestroy` record method names under.
|
|
9
|
+
*
|
|
6
10
|
* @since 0.3.16-canary.0
|
|
7
11
|
*/
|
|
8
12
|
export const LIFECYCLE_KEY: unique symbol = Symbol("di:lifecycle");
|
|
9
13
|
/**
|
|
14
|
+
* The metadata key `@inject` accessors record their injection descriptors under.
|
|
15
|
+
*
|
|
10
16
|
* @since 0.3.16-canary.0
|
|
11
17
|
*/
|
|
12
18
|
export const INJECT_ACCESSOR_KEY: unique symbol = Symbol("di:inject-accessor");
|
|
@@ -3,6 +3,8 @@ import type { Token } from "#/core/token";
|
|
|
3
3
|
import type { MetadataReader } from "#/metadata/metadata-types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
+
* The token a custom `MetadataReader` is bound to for a container to pick up.
|
|
7
|
+
*
|
|
6
8
|
* @since 0.3.16-canary.0
|
|
7
9
|
*/
|
|
8
10
|
export const MetadataReaderToken: Token<MetadataReader> = token<MetadataReader>("MetadataReader");
|
|
@@ -15,6 +15,8 @@ export interface ParamMetadata extends DependencySlot {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
+
* A class's declared constructor dependencies, as a `MetadataReader` reports them.
|
|
19
|
+
*
|
|
18
20
|
* @since 0.3.16-canary.0
|
|
19
21
|
*/
|
|
20
22
|
export interface ConstructorMetadata {
|
|
@@ -22,6 +24,8 @@ export interface ConstructorMetadata {
|
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/**
|
|
27
|
+
* The `postConstruct` and `preDestroy` method names a class declares.
|
|
28
|
+
*
|
|
25
29
|
* @since 0.3.16-canary.0
|
|
26
30
|
*/
|
|
27
31
|
export interface LifecycleMetadata {
|
|
@@ -40,6 +44,8 @@ export interface MutableLifecycleMetadata {
|
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
/**
|
|
47
|
+
* The port a container reads class metadata through — constructor deps, lifecycle, and accessors.
|
|
48
|
+
*
|
|
43
49
|
* @since 0.3.16-canary.0
|
|
44
50
|
*/
|
|
45
51
|
export interface MetadataReader {
|