@codefast/di 0.3.16-canary.0 → 0.3.16-canary.1
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 +2 -0
- package/README.md +4 -1
- package/dist/binding-select.d.mts +2 -2
- package/dist/binding.d.mts +6 -6
- package/dist/constraints.d.mts +25 -9
- package/dist/constraints.mjs +47 -19
- package/dist/constructor-type.d.mts +2 -2
- package/dist/container.d.mts +6 -6
- package/dist/container.mjs +136 -55
- package/dist/decorators/inject.d.mts +3 -3
- package/dist/decorators/inject.mjs +13 -11
- package/dist/decorators/injectable.d.mts +1 -1
- package/dist/decorators/injectable.mjs +18 -14
- package/dist/decorators/lifecycle-decorators.mjs +3 -0
- package/dist/dependency-graph.d.mts +2 -2
- package/dist/environment.d.mts +12 -12
- package/dist/errors.d.mts +18 -8
- package/dist/errors.mjs +17 -1
- package/dist/graph-adapters/reactflow.d.mts +2 -2
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +4 -3
- package/dist/inspector.d.mts +2 -3
- package/dist/inspector.mjs +5 -3
- package/dist/lifecycle.d.mts +5 -6
- package/dist/lifecycle.mjs +45 -52
- package/dist/metadata/metadata-keys.d.mts +24 -1
- package/dist/metadata/metadata-keys.mjs +14 -1
- package/dist/metadata/metadata-types.d.mts +5 -5
- package/dist/metadata/symbol-metadata-reader.mjs +31 -24
- package/dist/module.d.mts +1 -1
- package/dist/registry.d.mts +5 -5
- package/dist/registry.mjs +35 -42
- package/dist/resolver.d.mts +20 -8
- package/dist/resolver.mjs +64 -43
- package/dist/types.d.mts +9 -5
- package/package.json +11 -5
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -203,7 +203,10 @@ container.bind(DbToken).toDynamicAsync(async (ctx) => {
|
|
|
203
203
|
|
|
204
204
|
container
|
|
205
205
|
.bind(UserServiceToken)
|
|
206
|
-
.toResolved((
|
|
206
|
+
.toResolved((repository, config) => new UserService(repository, config), [
|
|
207
|
+
UserRepository,
|
|
208
|
+
AppConfigToken,
|
|
209
|
+
] as const);
|
|
207
210
|
|
|
208
211
|
container
|
|
209
212
|
.bind(MetricsToken)
|
|
@@ -8,12 +8,12 @@ import { Binding } from "./binding.mjs";
|
|
|
8
8
|
*
|
|
9
9
|
* @since 0.3.16-canary.0
|
|
10
10
|
*/
|
|
11
|
-
declare function selectBinding(bindings:
|
|
11
|
+
declare function selectBinding(bindings: ReadonlyArray<Binding>, hint: ResolveOptions | undefined, ctx: ConstraintContext, tName: string): Binding | undefined;
|
|
12
12
|
/**
|
|
13
13
|
* Select all candidates matching hint + predicates.
|
|
14
14
|
*
|
|
15
15
|
* @since 0.3.16-canary.0
|
|
16
16
|
*/
|
|
17
|
-
declare function selectAllBindings(bindings:
|
|
17
|
+
declare function selectAllBindings(bindings: ReadonlyArray<Binding>, hint: ResolveOptions | undefined, ctx: ConstraintContext): Array<Binding>;
|
|
18
18
|
//#endregion
|
|
19
19
|
export { selectAllBindings, selectBinding };
|
package/dist/binding.d.mts
CHANGED
|
@@ -64,8 +64,8 @@ interface DynamicAsyncBinding<Value> extends BindingBase<Value> {
|
|
|
64
64
|
*/
|
|
65
65
|
interface ResolvedBinding<Value> extends BindingBase<Value> {
|
|
66
66
|
readonly kind: "resolved";
|
|
67
|
-
readonly factory: (...args: unknown
|
|
68
|
-
readonly deps:
|
|
67
|
+
readonly factory: (...args: Array<unknown>) => Value;
|
|
68
|
+
readonly deps: ReadonlyArray<InjectionDescriptor>;
|
|
69
69
|
readonly scope: BindingScope;
|
|
70
70
|
readonly onActivation?: ActivationHandler<Value>;
|
|
71
71
|
readonly onDeactivation?: DeactivationHandler<Value>;
|
|
@@ -75,8 +75,8 @@ interface ResolvedBinding<Value> extends BindingBase<Value> {
|
|
|
75
75
|
*/
|
|
76
76
|
interface ResolvedAsyncBinding<Value> extends BindingBase<Value> {
|
|
77
77
|
readonly kind: "resolved-async";
|
|
78
|
-
readonly factory: (...args: unknown
|
|
79
|
-
readonly deps:
|
|
78
|
+
readonly factory: (...args: Array<unknown>) => Promise<Value>;
|
|
79
|
+
readonly deps: ReadonlyArray<InjectionDescriptor>;
|
|
80
80
|
readonly scope: BindingScope;
|
|
81
81
|
readonly onActivation?: ActivationHandler<Value>;
|
|
82
82
|
readonly onDeactivation?: DeactivationHandler<Value>;
|
|
@@ -121,8 +121,8 @@ interface BindToBuilder<Value> {
|
|
|
121
121
|
toConstantValue(value: Value): ConstantBindingBuilder<Value>;
|
|
122
122
|
toDynamic(factory: (ctx: ResolutionContext) => Value): BindingBuilder<Value>;
|
|
123
123
|
toDynamicAsync(factory: (ctx: ResolutionContext) => Promise<Value>): BindingBuilder<Value>;
|
|
124
|
-
toResolved<const Deps extends
|
|
125
|
-
toResolvedAsync<const Deps extends
|
|
124
|
+
toResolved<const Deps extends ReadonlyArray<DependencyKey>>(factory: (...args: { [K in keyof Deps]: TokenValue<NoInfer<Deps>[K]> }) => Value, deps: Deps): BindingBuilder<Value>;
|
|
125
|
+
toResolvedAsync<const Deps extends ReadonlyArray<DependencyKey>>(factory: (...args: { [K in keyof Deps]: TokenValue<NoInfer<Deps>[K]> }) => Promise<Value>, deps: Deps): BindingBuilder<Value>;
|
|
126
126
|
toAlias(target: Token<Value> | Constructor<Value>): AliasBindingBuilder;
|
|
127
127
|
}
|
|
128
128
|
/**
|
package/dist/constraints.d.mts
CHANGED
|
@@ -6,34 +6,50 @@ import { ConstraintContext } from "./types.mjs";
|
|
|
6
6
|
/**
|
|
7
7
|
* @since 0.3.16-canary.0
|
|
8
8
|
*/
|
|
9
|
-
declare function whenParentIs(
|
|
9
|
+
declare function whenParentIs(token: Token<unknown> | Constructor): (constraintContext: ConstraintContext) => boolean;
|
|
10
10
|
/**
|
|
11
11
|
* @since 0.3.16-canary.0
|
|
12
12
|
*/
|
|
13
|
-
declare function whenNoParentIs(
|
|
13
|
+
declare function whenNoParentIs(token: Token<unknown> | Constructor): (constraintContext: ConstraintContext) => boolean;
|
|
14
14
|
/**
|
|
15
15
|
* @since 0.3.16-canary.0
|
|
16
16
|
*/
|
|
17
|
-
declare function whenAnyAncestorIs(
|
|
17
|
+
declare function whenAnyAncestorIs(token: Token<unknown> | Constructor): (constraintContext: ConstraintContext) => boolean;
|
|
18
18
|
/**
|
|
19
19
|
* @since 0.3.16-canary.0
|
|
20
20
|
*/
|
|
21
|
-
declare function whenNoAncestorIs(
|
|
21
|
+
declare function whenNoAncestorIs(token: Token<unknown> | Constructor): (constraintContext: ConstraintContext) => boolean;
|
|
22
22
|
/**
|
|
23
23
|
* @since 0.3.16-canary.0
|
|
24
24
|
*/
|
|
25
|
-
declare function whenParentNamed(name: string): (
|
|
25
|
+
declare function whenParentNamed(name: string): (constraintContext: ConstraintContext) => boolean;
|
|
26
26
|
/**
|
|
27
27
|
* @since 0.3.16-canary.0
|
|
28
28
|
*/
|
|
29
|
-
declare function whenAnyAncestorNamed(name: string): (
|
|
29
|
+
declare function whenAnyAncestorNamed(name: string): (constraintContext: ConstraintContext) => boolean;
|
|
30
30
|
/**
|
|
31
31
|
* @since 0.3.16-canary.0
|
|
32
32
|
*/
|
|
33
|
-
declare function whenParentTagged(tag: string, value: unknown): (
|
|
33
|
+
declare function whenParentTagged(tag: string, value: unknown): (constraintContext: ConstraintContext) => boolean;
|
|
34
34
|
/**
|
|
35
35
|
* @since 0.3.16-canary.0
|
|
36
36
|
*/
|
|
37
|
-
declare function whenAnyAncestorTagged(tag: string, value: unknown): (
|
|
37
|
+
declare function whenAnyAncestorTagged(tag: string, value: unknown): (constraintContext: ConstraintContext) => boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Matches when the direct parent slot carries **all** of the given tag pairs.
|
|
40
|
+
* Equivalent to AND-composing multiple `whenParentTagged` calls but evaluates
|
|
41
|
+
* in a single predicate invocation — no intermediate closure allocations.
|
|
42
|
+
*
|
|
43
|
+
* @since 0.3.16-canary.1
|
|
44
|
+
*/
|
|
45
|
+
declare function whenParentTaggedAll(tags: ReadonlyArray<readonly [tag: string, value: unknown]>): (constraintContext: ConstraintContext) => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Matches when at least one ancestor slot carries **all** of the given tag pairs.
|
|
48
|
+
* Equivalent to AND-composing multiple `whenAnyAncestorTagged` calls but evaluates
|
|
49
|
+
* in a single predicate invocation — no intermediate closure allocations.
|
|
50
|
+
*
|
|
51
|
+
* @since 0.3.16-canary.1
|
|
52
|
+
*/
|
|
53
|
+
declare function whenAnyAncestorTaggedAll(tags: ReadonlyArray<readonly [tag: string, value: unknown]>): (constraintContext: ConstraintContext) => boolean;
|
|
38
54
|
//#endregion
|
|
39
|
-
export { whenAnyAncestorIs, whenAnyAncestorNamed, whenAnyAncestorTagged, whenNoAncestorIs, whenNoParentIs, whenParentIs, whenParentNamed, whenParentTagged };
|
|
55
|
+
export { whenAnyAncestorIs, whenAnyAncestorNamed, whenAnyAncestorTagged, whenAnyAncestorTaggedAll, whenNoAncestorIs, whenNoParentIs, whenParentIs, whenParentNamed, whenParentTagged, whenParentTaggedAll };
|
package/dist/constraints.mjs
CHANGED
|
@@ -1,59 +1,87 @@
|
|
|
1
1
|
import { tokenName } from "./token.mjs";
|
|
2
2
|
//#region src/constraints.ts
|
|
3
|
-
function tokenNameOf(
|
|
4
|
-
return tokenName(
|
|
3
|
+
function tokenNameOf(token) {
|
|
4
|
+
return tokenName(token);
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
7
|
* @since 0.3.16-canary.0
|
|
8
8
|
*/
|
|
9
|
-
function whenParentIs(
|
|
10
|
-
const
|
|
11
|
-
return (
|
|
9
|
+
function whenParentIs(token) {
|
|
10
|
+
const tokenDisplayName = tokenNameOf(token);
|
|
11
|
+
return (constraintContext) => constraintContext.parent !== void 0 && constraintContext.parent.tokenName === tokenDisplayName;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* @since 0.3.16-canary.0
|
|
15
15
|
*/
|
|
16
|
-
function whenNoParentIs(
|
|
17
|
-
const
|
|
18
|
-
return (
|
|
16
|
+
function whenNoParentIs(token) {
|
|
17
|
+
const tokenDisplayName = tokenNameOf(token);
|
|
18
|
+
return (constraintContext) => constraintContext.parent === void 0 || constraintContext.parent.tokenName !== tokenDisplayName;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* @since 0.3.16-canary.0
|
|
22
22
|
*/
|
|
23
|
-
function whenAnyAncestorIs(
|
|
24
|
-
const
|
|
25
|
-
return (
|
|
23
|
+
function whenAnyAncestorIs(token) {
|
|
24
|
+
const tokenDisplayName = tokenNameOf(token);
|
|
25
|
+
return (constraintContext) => constraintContext.ancestors.some((ancestorFrame) => ancestorFrame.tokenName === tokenDisplayName);
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* @since 0.3.16-canary.0
|
|
29
29
|
*/
|
|
30
|
-
function whenNoAncestorIs(
|
|
31
|
-
const
|
|
32
|
-
return (
|
|
30
|
+
function whenNoAncestorIs(token) {
|
|
31
|
+
const tokenDisplayName = tokenNameOf(token);
|
|
32
|
+
return (constraintContext) => constraintContext.ancestors.every((ancestorFrame) => ancestorFrame.tokenName !== tokenDisplayName);
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* @since 0.3.16-canary.0
|
|
36
36
|
*/
|
|
37
37
|
function whenParentNamed(name) {
|
|
38
|
-
return (
|
|
38
|
+
return (constraintContext) => constraintContext.parent !== void 0 && constraintContext.parent.slot.name === name;
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* @since 0.3.16-canary.0
|
|
42
42
|
*/
|
|
43
43
|
function whenAnyAncestorNamed(name) {
|
|
44
|
-
return (
|
|
44
|
+
return (constraintContext) => constraintContext.ancestors.some((ancestorFrame) => ancestorFrame.slot.name === name);
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* @since 0.3.16-canary.0
|
|
48
48
|
*/
|
|
49
49
|
function whenParentTagged(tag, value) {
|
|
50
|
-
return (
|
|
50
|
+
return (constraintContext) => constraintContext.parent !== void 0 && constraintContext.parent.slot.tags.some(([tagKey, tagValue]) => tagKey === tag && Object.is(tagValue, value));
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* @since 0.3.16-canary.0
|
|
54
54
|
*/
|
|
55
55
|
function whenAnyAncestorTagged(tag, value) {
|
|
56
|
-
return (
|
|
56
|
+
return (constraintContext) => constraintContext.ancestors.some((ancestorFrame) => ancestorFrame.slot.tags.some(([tagKey, tagValue]) => tagKey === tag && Object.is(tagValue, value)));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Matches when the direct parent slot carries **all** of the given tag pairs.
|
|
60
|
+
* Equivalent to AND-composing multiple `whenParentTagged` calls but evaluates
|
|
61
|
+
* in a single predicate invocation — no intermediate closure allocations.
|
|
62
|
+
*
|
|
63
|
+
* @since 0.3.16-canary.1
|
|
64
|
+
*/
|
|
65
|
+
function whenParentTaggedAll(tags) {
|
|
66
|
+
return (constraintContext) => {
|
|
67
|
+
const { parent } = constraintContext;
|
|
68
|
+
if (parent === void 0) return false;
|
|
69
|
+
const { tags: parentTags } = parent.slot;
|
|
70
|
+
return tags.every(([tagKey, tagValue]) => parentTags.some(([k, v]) => k === tagKey && Object.is(v, tagValue)));
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Matches when at least one ancestor slot carries **all** of the given tag pairs.
|
|
75
|
+
* Equivalent to AND-composing multiple `whenAnyAncestorTagged` calls but evaluates
|
|
76
|
+
* in a single predicate invocation — no intermediate closure allocations.
|
|
77
|
+
*
|
|
78
|
+
* @since 0.3.16-canary.1
|
|
79
|
+
*/
|
|
80
|
+
function whenAnyAncestorTaggedAll(tags) {
|
|
81
|
+
return (constraintContext) => constraintContext.ancestors.some((frame) => {
|
|
82
|
+
const { tags: frameTags } = frame.slot;
|
|
83
|
+
return tags.every(([tagKey, tagValue]) => frameTags.some(([k, v]) => k === tagKey && Object.is(v, tagValue)));
|
|
84
|
+
});
|
|
57
85
|
}
|
|
58
86
|
//#endregion
|
|
59
|
-
export { whenAnyAncestorIs, whenAnyAncestorNamed, whenAnyAncestorTagged, whenNoAncestorIs, whenNoParentIs, whenParentIs, whenParentNamed, whenParentTagged };
|
|
87
|
+
export { whenAnyAncestorIs, whenAnyAncestorNamed, whenAnyAncestorTagged, whenAnyAncestorTaggedAll, whenNoAncestorIs, whenNoParentIs, whenParentIs, whenParentNamed, whenParentTagged, whenParentTaggedAll };
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @since 0.3.16-canary.0
|
|
10
10
|
*/
|
|
11
|
-
type Constructor<Value = unknown> = new (...args: never
|
|
11
|
+
type Constructor<Value = unknown> = new (...args: Array<never>) => Value;
|
|
12
12
|
/**
|
|
13
13
|
* Class constructor as invoked by the resolver after metadata-driven
|
|
14
14
|
* resolution of `unknown[]` dependencies — separate from {@link Constructor},
|
|
@@ -16,6 +16,6 @@ type Constructor<Value = unknown> = new (...args: never[]) => Value;
|
|
|
16
16
|
*
|
|
17
17
|
* @since 0.3.16-canary.0
|
|
18
18
|
*/
|
|
19
|
-
type ConstructorInvocation = new (...args: unknown
|
|
19
|
+
type ConstructorInvocation = new (...args: Array<unknown>) => unknown;
|
|
20
20
|
//#endregion
|
|
21
21
|
export { Constructor, ConstructorInvocation };
|
package/dist/container.d.mts
CHANGED
|
@@ -19,9 +19,9 @@ interface Container {
|
|
|
19
19
|
unbindAll(): void;
|
|
20
20
|
unbindAllAsync(): Promise<void>;
|
|
21
21
|
rebind<const Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
|
|
22
|
-
load(...modules: SyncModule
|
|
22
|
+
load(...modules: Array<SyncModule>): void;
|
|
23
23
|
loadAsync(...modules: Array<SyncModule | AsyncModule>): Promise<void>;
|
|
24
|
-
unload(...modules: SyncModule
|
|
24
|
+
unload(...modules: Array<SyncModule>): void;
|
|
25
25
|
unloadAsync(...modules: Array<SyncModule | AsyncModule>): Promise<void>;
|
|
26
26
|
loadAutoRegistered(registry: AutoRegisterRegistry): number;
|
|
27
27
|
onActivation<const Value>(token: Token<Value> | Constructor<Value>, handler: ActivationHandler<Value>): void;
|
|
@@ -30,8 +30,8 @@ interface Container {
|
|
|
30
30
|
resolveAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Value>;
|
|
31
31
|
resolveOptional<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Value | undefined;
|
|
32
32
|
resolveOptionalAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Value | undefined>;
|
|
33
|
-
resolveAll<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Value
|
|
34
|
-
resolveAllAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Value
|
|
33
|
+
resolveAll<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Array<Value>;
|
|
34
|
+
resolveAllAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Array<Value>>;
|
|
35
35
|
createChild(): Container;
|
|
36
36
|
dispose(): Promise<void>;
|
|
37
37
|
[Symbol.asyncDispose](): Promise<void>;
|
|
@@ -40,7 +40,7 @@ interface Container {
|
|
|
40
40
|
validate(): void;
|
|
41
41
|
has(token: Token<unknown> | Constructor, hint?: ResolveOptions): boolean;
|
|
42
42
|
hasOwn(token: Token<unknown> | Constructor, hint?: ResolveOptions): boolean;
|
|
43
|
-
lookupBindings<const Value>(token: Token<Value> | Constructor<Value>):
|
|
43
|
+
lookupBindings<const Value>(token: Token<Value> | Constructor<Value>): ReadonlyArray<BindingSnapshot>;
|
|
44
44
|
inspect(): ContainerSnapshot;
|
|
45
45
|
generateDependencyGraph(options?: GraphOptions): ContainerGraphJson;
|
|
46
46
|
}
|
|
@@ -49,7 +49,7 @@ interface Container {
|
|
|
49
49
|
*/
|
|
50
50
|
interface ContainerStatic {
|
|
51
51
|
create(): Container;
|
|
52
|
-
fromModules(...modules: SyncModule
|
|
52
|
+
fromModules(...modules: Array<SyncModule>): Container;
|
|
53
53
|
fromModulesAsync(...modules: Array<SyncModule | AsyncModule>): Promise<Container>;
|
|
54
54
|
}
|
|
55
55
|
/**
|
package/dist/container.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { effectiveBindingScope } from "./binding-scope.mjs";
|
|
2
|
-
import { DisposedContainerError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError } from "./errors.mjs";
|
|
2
|
+
import { AsyncModuleLoadError, CircularDependencyError, DisposedContainerError, InternalError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError } from "./errors.mjs";
|
|
3
3
|
import { DEFAULT_SLOT, generateBindingId } from "./binding.mjs";
|
|
4
4
|
import { tokenName } from "./token.mjs";
|
|
5
5
|
import { BindingRegistry } from "./registry.mjs";
|
|
6
6
|
import { ScopeManager } from "./scope.mjs";
|
|
7
7
|
import { LifecycleManager } from "./lifecycle.mjs";
|
|
8
|
-
import { slotKeyToResolveOptions } from "./resolve-options.mjs";
|
|
8
|
+
import { injectableSlotToResolveOptions, slotKeyToResolveOptions } from "./resolve-options.mjs";
|
|
9
9
|
import { DependencyResolver } from "./resolver.mjs";
|
|
10
10
|
import { Inspector } from "./inspector.mjs";
|
|
11
11
|
import { buildDependencyGraph } from "./dependency-graph.mjs";
|
|
@@ -142,6 +142,7 @@ var DefaultContainer = class DefaultContainer {
|
|
|
142
142
|
_loadSyncModules(modules) {
|
|
143
143
|
const toLoad = this._collectModuleDeps(modules);
|
|
144
144
|
for (const mod of toLoad) {
|
|
145
|
+
if (!isSyncModule(mod)) throw new AsyncModuleLoadError(mod.name);
|
|
145
146
|
const ref = mod;
|
|
146
147
|
const existing = this._moduleRefs.get(ref);
|
|
147
148
|
if (existing !== void 0) {
|
|
@@ -332,43 +333,116 @@ var DefaultContainer = class DefaultContainer {
|
|
|
332
333
|
const reader = this._getMetadataReader();
|
|
333
334
|
const allBindings = this._registry.allBindings();
|
|
334
335
|
for (const binding of allBindings) {
|
|
335
|
-
if (
|
|
336
|
-
|
|
337
|
-
if (scope !== "singleton") continue;
|
|
338
|
-
const deps = this._getDepsForBinding(binding, reader);
|
|
339
|
-
for (const dep of deps) this._validateDep(tokenName(binding.token), scope, dep, [tokenName(binding.token)], reader);
|
|
336
|
+
if (!this._isSingletonStaticAnalyzableBinding(binding)) continue;
|
|
337
|
+
this._validateSingletonBindingGraph(binding, reader);
|
|
340
338
|
}
|
|
341
339
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
340
|
+
_isSingletonStaticAnalyzableBinding(binding) {
|
|
341
|
+
if (effectiveBindingScope(binding) !== "singleton") return false;
|
|
342
|
+
return binding.kind === "class" || binding.kind === "resolved" || binding.kind === "resolved-async";
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* DFS over explicit constructor / `toResolved*` dependency edges. Follows `toAlias` chains to the
|
|
346
|
+
* terminal binding for scope checks (SPEC §6.9). Skips `toDynamic*` subtrees (opaque).
|
|
347
|
+
*/
|
|
348
|
+
_validateSingletonBindingGraph(root, reader) {
|
|
349
|
+
const rootName = tokenName(root.token);
|
|
350
|
+
const dfs = (current, pathNames, pathBindingIds) => {
|
|
351
|
+
if (pathBindingIds.has(current.id)) return;
|
|
352
|
+
const extendedPathIds = new Set(pathBindingIds);
|
|
353
|
+
extendedPathIds.add(current.id);
|
|
354
|
+
for (const edge of this._collectStaticDependencyEdges(current, reader)) {
|
|
355
|
+
const { terminal, depTokenName } = edge;
|
|
356
|
+
const depScope = this._validationScopeFromTerminal(terminal);
|
|
357
|
+
if (depScope === "opaque") continue;
|
|
358
|
+
if (depScope === "scoped" || depScope === "transient") throw new ScopeViolationError({
|
|
359
|
+
consumerToken: rootName,
|
|
360
|
+
consumerScope: "singleton",
|
|
361
|
+
dependencyToken: depTokenName,
|
|
362
|
+
dependencyScope: depScope,
|
|
363
|
+
path: [...pathNames, depTokenName]
|
|
351
364
|
});
|
|
365
|
+
if (terminal.kind === "class" || terminal.kind === "resolved" || terminal.kind === "resolved-async") dfs(terminal, [...pathNames, depTokenName], extendedPathIds);
|
|
352
366
|
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
367
|
+
};
|
|
368
|
+
dfs(root, [rootName], /* @__PURE__ */ new Set());
|
|
369
|
+
}
|
|
370
|
+
_validationScopeFromTerminal(terminal) {
|
|
371
|
+
switch (terminal.kind) {
|
|
372
|
+
case "constant": return "singleton";
|
|
373
|
+
case "dynamic":
|
|
374
|
+
case "dynamic-async": return "opaque";
|
|
375
|
+
case "class":
|
|
376
|
+
case "resolved":
|
|
377
|
+
case "resolved-async": return terminal.scope;
|
|
378
|
+
case "alias": throw new InternalError("validate: expected terminal binding after alias resolution");
|
|
379
|
+
default: return terminal;
|
|
359
380
|
}
|
|
360
|
-
return result;
|
|
361
381
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
382
|
+
_followAliasChainToTerminal(binding, hint) {
|
|
383
|
+
const cyclePath = [];
|
|
384
|
+
const seenAliasIds = /* @__PURE__ */ new Set();
|
|
385
|
+
let current = binding;
|
|
386
|
+
while (current !== void 0 && current.kind === "alias") {
|
|
387
|
+
if (seenAliasIds.has(current.id)) throw new CircularDependencyError(cyclePath);
|
|
388
|
+
seenAliasIds.add(current.id);
|
|
389
|
+
cyclePath.push(tokenName(current.token));
|
|
390
|
+
const nextToken = current.target;
|
|
391
|
+
const next = this._resolver.peekBindingForValidate(nextToken, hint);
|
|
392
|
+
if (next === void 0) return;
|
|
393
|
+
current = next.binding;
|
|
394
|
+
}
|
|
395
|
+
return current;
|
|
396
|
+
}
|
|
397
|
+
_collectStaticDependencyEdges(binding, reader) {
|
|
398
|
+
const edges = [];
|
|
399
|
+
const pushTerminal = (terminal, displayName) => {
|
|
400
|
+
if (terminal === void 0) return;
|
|
401
|
+
edges.push({
|
|
402
|
+
terminal,
|
|
403
|
+
depTokenName: displayName
|
|
370
404
|
});
|
|
405
|
+
};
|
|
406
|
+
if (binding.kind === "class") {
|
|
407
|
+
const meta = reader.getConstructorMetadata(binding.target);
|
|
408
|
+
if (meta === void 0) return edges;
|
|
409
|
+
for (const param of meta.params) {
|
|
410
|
+
const paramHint = injectableSlotToResolveOptions(param);
|
|
411
|
+
if (param.optional) continue;
|
|
412
|
+
const tokenRef = param.token;
|
|
413
|
+
if (param.multi) {
|
|
414
|
+
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, paramHint);
|
|
415
|
+
for (const cand of candidates) {
|
|
416
|
+
const term = this._followAliasChainToTerminal(cand, paramHint);
|
|
417
|
+
pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
|
|
418
|
+
}
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
const found = paramHint === void 0 ? this._resolver.peekBindingForValidate(tokenRef, void 0) : this._resolver.peekBindingForValidate(tokenRef, paramHint);
|
|
422
|
+
if (found === void 0) continue;
|
|
423
|
+
const term = this._followAliasChainToTerminal(found.binding, paramHint);
|
|
424
|
+
pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
|
|
425
|
+
}
|
|
426
|
+
return edges;
|
|
371
427
|
}
|
|
428
|
+
if (binding.kind === "resolved" || binding.kind === "resolved-async") for (const dep of binding.deps) {
|
|
429
|
+
const depHint = injectableSlotToResolveOptions(dep);
|
|
430
|
+
if (dep.optional) continue;
|
|
431
|
+
const tokenRef = dep.token;
|
|
432
|
+
if (dep.multi) {
|
|
433
|
+
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, depHint);
|
|
434
|
+
for (const cand of candidates) {
|
|
435
|
+
const term = this._followAliasChainToTerminal(cand, depHint);
|
|
436
|
+
pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
|
|
437
|
+
}
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
const found = depHint === void 0 ? this._resolver.peekBindingForValidate(tokenRef, void 0) : this._resolver.peekBindingForValidate(tokenRef, depHint);
|
|
441
|
+
if (found === void 0) continue;
|
|
442
|
+
const term = this._followAliasChainToTerminal(found.binding, depHint);
|
|
443
|
+
pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
|
|
444
|
+
}
|
|
445
|
+
return edges;
|
|
372
446
|
}
|
|
373
447
|
has(token, hint) {
|
|
374
448
|
this._assertNotDisposed();
|
|
@@ -394,7 +468,19 @@ var DefaultContainer = class DefaultContainer {
|
|
|
394
468
|
if (this._disposed) throw new DisposedContainerError();
|
|
395
469
|
}
|
|
396
470
|
};
|
|
471
|
+
function updateSlotTag(slot, tag, value) {
|
|
472
|
+
const existing = [...slot.tags];
|
|
473
|
+
const idx = existing.findIndex(([k]) => k === tag);
|
|
474
|
+
if (idx !== -1) existing[idx] = [tag, value];
|
|
475
|
+
else existing.push([tag, value]);
|
|
476
|
+
return {
|
|
477
|
+
...slot,
|
|
478
|
+
tags: existing
|
|
479
|
+
};
|
|
480
|
+
}
|
|
397
481
|
var BindToBuilderImpl = class {
|
|
482
|
+
_token;
|
|
483
|
+
_commit;
|
|
398
484
|
constructor(_token, _commit) {
|
|
399
485
|
this._token = _token;
|
|
400
486
|
this._commit = _commit;
|
|
@@ -454,6 +540,9 @@ var BindToBuilderImpl = class {
|
|
|
454
540
|
}
|
|
455
541
|
};
|
|
456
542
|
var BindingBuilderImpl = class {
|
|
543
|
+
_token;
|
|
544
|
+
_partial;
|
|
545
|
+
_commit;
|
|
457
546
|
_slot;
|
|
458
547
|
_predicate;
|
|
459
548
|
_committed;
|
|
@@ -492,14 +581,7 @@ var BindingBuilderImpl = class {
|
|
|
492
581
|
return this;
|
|
493
582
|
}
|
|
494
583
|
whenTagged(tag, value) {
|
|
495
|
-
|
|
496
|
-
const idx = existing.findIndex(([k]) => k === tag);
|
|
497
|
-
if (idx !== -1) existing[idx] = [tag, value];
|
|
498
|
-
else existing.push([tag, value]);
|
|
499
|
-
this._slot = {
|
|
500
|
-
...this._slot,
|
|
501
|
-
tags: existing
|
|
502
|
-
};
|
|
584
|
+
this._slot = updateSlotTag(this._slot, tag, value);
|
|
503
585
|
this._doCommit();
|
|
504
586
|
return this;
|
|
505
587
|
}
|
|
@@ -535,6 +617,13 @@ var BindingBuilderImpl = class {
|
|
|
535
617
|
}
|
|
536
618
|
};
|
|
537
619
|
var ScopeBuilderImpl = class {
|
|
620
|
+
_token;
|
|
621
|
+
_partial;
|
|
622
|
+
_slot;
|
|
623
|
+
_predicate;
|
|
624
|
+
_commit;
|
|
625
|
+
_scope;
|
|
626
|
+
_initialPreviousId;
|
|
538
627
|
_onActivation;
|
|
539
628
|
_onDeactivation;
|
|
540
629
|
_committed;
|
|
@@ -576,6 +665,9 @@ var ScopeBuilderImpl = class {
|
|
|
576
665
|
}
|
|
577
666
|
};
|
|
578
667
|
var ConstantBindingBuilderImpl = class {
|
|
668
|
+
_token;
|
|
669
|
+
_value;
|
|
670
|
+
_commit;
|
|
579
671
|
_slot;
|
|
580
672
|
_predicate;
|
|
581
673
|
_onActivation;
|
|
@@ -620,14 +712,7 @@ var ConstantBindingBuilderImpl = class {
|
|
|
620
712
|
return this;
|
|
621
713
|
}
|
|
622
714
|
whenTagged(tag, value) {
|
|
623
|
-
|
|
624
|
-
const idx = existing.findIndex(([k]) => k === tag);
|
|
625
|
-
if (idx !== -1) existing[idx] = [tag, value];
|
|
626
|
-
else existing.push([tag, value]);
|
|
627
|
-
this._slot = {
|
|
628
|
-
...this._slot,
|
|
629
|
-
tags: existing
|
|
630
|
-
};
|
|
715
|
+
this._slot = updateSlotTag(this._slot, tag, value);
|
|
631
716
|
this._doCommit();
|
|
632
717
|
return this;
|
|
633
718
|
}
|
|
@@ -649,6 +734,9 @@ var ConstantBindingBuilderImpl = class {
|
|
|
649
734
|
}
|
|
650
735
|
};
|
|
651
736
|
var AliasBindingBuilderImpl = class {
|
|
737
|
+
_token;
|
|
738
|
+
_target;
|
|
739
|
+
_commit;
|
|
652
740
|
_slot;
|
|
653
741
|
_predicate;
|
|
654
742
|
_committed;
|
|
@@ -688,14 +776,7 @@ var AliasBindingBuilderImpl = class {
|
|
|
688
776
|
return this;
|
|
689
777
|
}
|
|
690
778
|
whenTagged(tag, value) {
|
|
691
|
-
|
|
692
|
-
const idx = existing.findIndex(([k]) => k === tag);
|
|
693
|
-
if (idx !== -1) existing[idx] = [tag, value];
|
|
694
|
-
else existing.push([tag, value]);
|
|
695
|
-
this._slot = {
|
|
696
|
-
...this._slot,
|
|
697
|
-
tags: existing
|
|
698
|
-
};
|
|
779
|
+
this._slot = updateSlotTag(this._slot, tag, value);
|
|
699
780
|
this._doCommit();
|
|
700
781
|
return this;
|
|
701
782
|
}
|
|
@@ -35,14 +35,14 @@ type ClassAccessorDecorator<This, Value> = (target: ClassAccessorDecoratorTarget
|
|
|
35
35
|
/**
|
|
36
36
|
* @since 0.3.16-canary.0
|
|
37
37
|
*/
|
|
38
|
-
declare function inject<const Value>(
|
|
38
|
+
declare function inject<const Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value> & ClassAccessorDecorator<unknown, Value>;
|
|
39
39
|
/**
|
|
40
40
|
* @since 0.3.16-canary.0
|
|
41
41
|
*/
|
|
42
|
-
declare function optional<const Value>(
|
|
42
|
+
declare function optional<const Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value | undefined>;
|
|
43
43
|
/**
|
|
44
44
|
* @since 0.3.16-canary.0
|
|
45
45
|
*/
|
|
46
|
-
declare function injectAll<const Value>(
|
|
46
|
+
declare function injectAll<const Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Array<Value>>;
|
|
47
47
|
//#endregion
|
|
48
48
|
export { InjectOptions, InjectableDependency, InjectionDescriptor, inject, injectAll, isInjectionDescriptor, normalizeToDescriptor, optional };
|