@codefast/di 0.8.0 → 0.8.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 +13 -0
- package/README.md +242 -159
- package/dist/ambient/active-container.d.ts +1 -2
- package/dist/ambient/active-container.js +1 -2
- package/dist/container/binding-builders.d.ts +1 -2
- package/dist/container/binding-builders.js +1 -2
- package/dist/container/container.d.ts +1 -2
- package/dist/container/container.js +1 -2
- package/dist/core/binding-scope.d.ts +1 -2
- package/dist/core/binding-scope.js +1 -2
- package/dist/core/binding.d.ts +1 -2
- package/dist/core/binding.js +1 -2
- package/dist/core/constraint-requirement.d.ts +1 -2
- package/dist/core/constraint-requirement.js +1 -2
- package/dist/core/constructor-type.d.ts +1 -2
- package/dist/core/constructor-type.js +1 -2
- package/dist/core/map-upsert.d.ts +1 -2
- package/dist/core/map-upsert.js +1 -2
- package/dist/core/module.d.ts +1 -2
- package/dist/core/module.js +1 -2
- package/dist/core/registry.d.ts +1 -2
- package/dist/core/registry.js +1 -2
- package/dist/core/tag.d.ts +1 -2
- package/dist/core/tag.js +1 -2
- package/dist/core/token.d.ts +1 -2
- package/dist/core/token.js +1 -2
- package/dist/core/types.d.ts +1 -2
- package/dist/core/types.js +1 -2
- package/dist/decorators/inject.d.ts +1 -2
- package/dist/decorators/inject.js +1 -2
- package/dist/decorators/injectable.d.ts +1 -2
- package/dist/decorators/injectable.js +1 -2
- package/dist/decorators/lifecycle-decorators.d.ts +1 -2
- package/dist/decorators/lifecycle-decorators.js +1 -2
- package/dist/errors/diagnostics.d.ts +1 -2
- package/dist/errors/diagnostics.js +1 -2
- package/dist/errors/errors.d.ts +1 -2
- package/dist/errors/errors.js +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/injection/descriptor.d.ts +1 -2
- package/dist/injection/descriptor.js +1 -2
- package/dist/injection/resolve-options.d.ts +1 -2
- package/dist/injection/resolve-options.js +1 -2
- package/dist/introspection/dependency-graph.d.ts +1 -2
- package/dist/introspection/dependency-graph.js +1 -2
- package/dist/introspection/graph-adapters/cytoscape.d.ts +1 -2
- package/dist/introspection/graph-adapters/cytoscape.js +1 -2
- package/dist/introspection/graph-adapters/dot.d.ts +1 -2
- package/dist/introspection/graph-adapters/dot.js +1 -2
- package/dist/introspection/graph-adapters/mermaid.d.ts +1 -2
- package/dist/introspection/graph-adapters/mermaid.js +1 -2
- package/dist/introspection/graph-adapters/reactflow.d.ts +1 -2
- package/dist/introspection/graph-adapters/reactflow.js +1 -2
- package/dist/introspection/inspector.d.ts +1 -2
- package/dist/introspection/inspector.js +1 -2
- package/dist/lifecycle/lifecycle-manager.d.ts +1 -2
- package/dist/lifecycle/lifecycle-manager.js +1 -2
- package/dist/lifecycle/scope-manager.d.ts +1 -2
- package/dist/lifecycle/scope-manager.js +1 -2
- package/dist/metadata/metadata-keys.d.ts +1 -2
- package/dist/metadata/metadata-keys.js +1 -2
- package/dist/metadata/metadata-reader-token.d.ts +1 -2
- package/dist/metadata/metadata-reader-token.js +1 -2
- package/dist/metadata/metadata-types.d.ts +1 -2
- package/dist/metadata/metadata-types.js +1 -2
- package/dist/metadata/symbol-metadata-reader.d.ts +1 -2
- package/dist/metadata/symbol-metadata-reader.js +1 -2
- package/dist/metadata/verifying-metadata-reader.d.ts +1 -2
- package/dist/metadata/verifying-metadata-reader.js +1 -2
- package/dist/resolution/cache/activation-need.d.ts +1 -2
- package/dist/resolution/cache/activation-need.js +1 -2
- package/dist/resolution/cache/binding-lookup-cache.d.ts +1 -2
- package/dist/resolution/cache/binding-lookup-cache.js +1 -2
- package/dist/resolution/cache/class-introspector.d.ts +1 -2
- package/dist/resolution/cache/class-introspector.js +1 -2
- package/dist/resolution/context.d.ts +1 -2
- package/dist/resolution/context.js +1 -2
- package/dist/resolution/path/resolution-path.d.ts +1 -2
- package/dist/resolution/path/resolution-path.js +1 -2
- package/dist/resolution/plan/instantiation-plan.d.ts +1 -2
- package/dist/resolution/plan/instantiation-plan.js +1 -2
- package/dist/resolution/resolver.d.ts +1 -2
- package/dist/resolution/resolver.js +1 -2
- package/dist/resolution/select/binding-select.d.ts +1 -2
- package/dist/resolution/select/binding-select.js +1 -2
- package/dist/resolution/select/constraints.d.ts +1 -2
- package/dist/resolution/select/constraints.js +1 -2
- package/package.json +6 -58
- package/dist/ambient/active-container.d.ts.map +0 -1
- package/dist/ambient/active-container.js.map +0 -1
- package/dist/container/binding-builders.d.ts.map +0 -1
- package/dist/container/binding-builders.js.map +0 -1
- package/dist/container/container.d.ts.map +0 -1
- package/dist/container/container.js.map +0 -1
- package/dist/core/binding-scope.d.ts.map +0 -1
- package/dist/core/binding-scope.js.map +0 -1
- package/dist/core/binding.d.ts.map +0 -1
- package/dist/core/binding.js.map +0 -1
- package/dist/core/constraint-requirement.d.ts.map +0 -1
- package/dist/core/constraint-requirement.js.map +0 -1
- package/dist/core/constructor-type.d.ts.map +0 -1
- package/dist/core/constructor-type.js.map +0 -1
- package/dist/core/map-upsert.d.ts.map +0 -1
- package/dist/core/map-upsert.js.map +0 -1
- package/dist/core/module.d.ts.map +0 -1
- package/dist/core/module.js.map +0 -1
- package/dist/core/registry.d.ts.map +0 -1
- package/dist/core/registry.js.map +0 -1
- package/dist/core/tag.d.ts.map +0 -1
- package/dist/core/tag.js.map +0 -1
- package/dist/core/token.d.ts.map +0 -1
- package/dist/core/token.js.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/decorators/inject.d.ts.map +0 -1
- package/dist/decorators/inject.js.map +0 -1
- package/dist/decorators/injectable.d.ts.map +0 -1
- package/dist/decorators/injectable.js.map +0 -1
- package/dist/decorators/lifecycle-decorators.d.ts.map +0 -1
- package/dist/decorators/lifecycle-decorators.js.map +0 -1
- package/dist/errors/diagnostics.d.ts.map +0 -1
- package/dist/errors/diagnostics.js.map +0 -1
- package/dist/errors/errors.d.ts.map +0 -1
- package/dist/errors/errors.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/injection/descriptor.d.ts.map +0 -1
- package/dist/injection/descriptor.js.map +0 -1
- package/dist/injection/resolve-options.d.ts.map +0 -1
- package/dist/injection/resolve-options.js.map +0 -1
- package/dist/introspection/dependency-graph.d.ts.map +0 -1
- package/dist/introspection/dependency-graph.js.map +0 -1
- package/dist/introspection/graph-adapters/cytoscape.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/cytoscape.js.map +0 -1
- package/dist/introspection/graph-adapters/dot.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/dot.js.map +0 -1
- package/dist/introspection/graph-adapters/mermaid.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/mermaid.js.map +0 -1
- package/dist/introspection/graph-adapters/reactflow.d.ts.map +0 -1
- package/dist/introspection/graph-adapters/reactflow.js.map +0 -1
- package/dist/introspection/inspector.d.ts.map +0 -1
- package/dist/introspection/inspector.js.map +0 -1
- package/dist/lifecycle/lifecycle-manager.d.ts.map +0 -1
- package/dist/lifecycle/lifecycle-manager.js.map +0 -1
- package/dist/lifecycle/scope-manager.d.ts.map +0 -1
- package/dist/lifecycle/scope-manager.js.map +0 -1
- package/dist/metadata/metadata-keys.d.ts.map +0 -1
- package/dist/metadata/metadata-keys.js.map +0 -1
- package/dist/metadata/metadata-reader-token.d.ts.map +0 -1
- package/dist/metadata/metadata-reader-token.js.map +0 -1
- package/dist/metadata/metadata-types.d.ts.map +0 -1
- package/dist/metadata/metadata-types.js.map +0 -1
- package/dist/metadata/symbol-metadata-reader.d.ts.map +0 -1
- package/dist/metadata/symbol-metadata-reader.js.map +0 -1
- package/dist/metadata/verifying-metadata-reader.d.ts.map +0 -1
- package/dist/metadata/verifying-metadata-reader.js.map +0 -1
- package/dist/resolution/cache/activation-need.d.ts.map +0 -1
- package/dist/resolution/cache/activation-need.js.map +0 -1
- package/dist/resolution/cache/binding-lookup-cache.d.ts.map +0 -1
- package/dist/resolution/cache/binding-lookup-cache.js.map +0 -1
- package/dist/resolution/cache/class-introspector.d.ts.map +0 -1
- package/dist/resolution/cache/class-introspector.js.map +0 -1
- package/dist/resolution/context.d.ts.map +0 -1
- package/dist/resolution/context.js.map +0 -1
- package/dist/resolution/path/resolution-path.d.ts.map +0 -1
- package/dist/resolution/path/resolution-path.js.map +0 -1
- package/dist/resolution/plan/instantiation-plan.d.ts.map +0 -1
- package/dist/resolution/plan/instantiation-plan.js.map +0 -1
- package/dist/resolution/resolver.d.ts.map +0 -1
- package/dist/resolution/resolver.js.map +0 -1
- package/dist/resolution/select/binding-select.d.ts.map +0 -1
- package/dist/resolution/select/binding-select.js.map +0 -1
- package/dist/resolution/select/constraints.d.ts.map +0 -1
- package/dist/resolution/select/constraints.js.map +0 -1
- package/src/ambient/active-container.ts +0 -69
- package/src/container/binding-builders.ts +0 -337
- package/src/container/container.ts +0 -972
- package/src/core/binding-scope.ts +0 -15
- package/src/core/binding.ts +0 -505
- package/src/core/constraint-requirement.ts +0 -94
- package/src/core/constructor-type.ts +0 -18
- package/src/core/map-upsert.ts +0 -53
- package/src/core/module.ts +0 -117
- package/src/core/registry.ts +0 -319
- package/src/core/tag.ts +0 -212
- package/src/core/token.ts +0 -31
- package/src/core/types.ts +0 -149
- package/src/decorators/inject.ts +0 -101
- package/src/decorators/injectable.ts +0 -134
- package/src/decorators/lifecycle-decorators.ts +0 -43
- package/src/errors/diagnostics.ts +0 -45
- package/src/errors/errors.ts +0 -499
- package/src/index.ts +0 -139
- package/src/injection/descriptor.ts +0 -218
- package/src/injection/resolve-options.ts +0 -201
- package/src/introspection/dependency-graph.ts +0 -296
- package/src/introspection/graph-adapters/cytoscape.ts +0 -78
- package/src/introspection/graph-adapters/dot.ts +0 -32
- package/src/introspection/graph-adapters/mermaid.ts +0 -61
- package/src/introspection/graph-adapters/reactflow.ts +0 -84
- package/src/introspection/inspector.ts +0 -134
- package/src/lifecycle/lifecycle-manager.ts +0 -272
- package/src/lifecycle/scope-manager.ts +0 -158
- package/src/metadata/metadata-keys.ts +0 -28
- package/src/metadata/metadata-reader-token.ts +0 -10
- package/src/metadata/metadata-types.ts +0 -57
- package/src/metadata/symbol-metadata-reader.ts +0 -126
- package/src/metadata/verifying-metadata-reader.ts +0 -41
- package/src/resolution/cache/activation-need.ts +0 -87
- package/src/resolution/cache/binding-lookup-cache.ts +0 -169
- package/src/resolution/cache/class-introspector.ts +0 -256
- package/src/resolution/context.ts +0 -331
- package/src/resolution/path/resolution-path.ts +0 -167
- package/src/resolution/plan/instantiation-plan.ts +0 -592
- package/src/resolution/resolver.ts +0 -1654
- package/src/resolution/select/binding-select.ts +0 -207
- package/src/resolution/select/constraints.ts +0 -146
package/src/core/module.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import type { BindToBuilder } from "#/core/binding";
|
|
2
|
-
import type { Token } from "#/core/token";
|
|
3
|
-
import type { Constructor } from "#/core/types";
|
|
4
|
-
|
|
5
|
-
// ── Branded types (runtime symbols for branding) ─────────────────────────────────────────────────────────────────────
|
|
6
|
-
|
|
7
|
-
const SYNC_MODULE_BRAND: unique symbol = Symbol("di:sync-module");
|
|
8
|
-
const ASYNC_MODULE_BRAND: unique symbol = Symbol("di:async-module");
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Key for the module's setup callback. A symbol (not exported from the package root)
|
|
12
|
-
* keeps the container-only member out of consumer-facing autocomplete entirely.
|
|
13
|
-
*
|
|
14
|
-
* @since 0.5.0-canary.7
|
|
15
|
-
*/
|
|
16
|
-
export const MODULE_SETUP: unique symbol = Symbol("di:module-setup");
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* A named, reusable group of bindings a container applies synchronously via `load()`.
|
|
20
|
-
*
|
|
21
|
-
* @since 0.3.16-canary.0
|
|
22
|
-
*/
|
|
23
|
-
export interface SyncModule {
|
|
24
|
-
readonly name: string;
|
|
25
|
-
readonly [SYNC_MODULE_BRAND]: true;
|
|
26
|
-
readonly [MODULE_SETUP]: (builder: ModuleBuilder) => void;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* A named group of bindings whose setup is async, applied via `loadAsync()`.
|
|
31
|
-
*
|
|
32
|
-
* @since 0.3.16-canary.0
|
|
33
|
-
*/
|
|
34
|
-
export interface AsyncModule {
|
|
35
|
-
readonly name: string;
|
|
36
|
-
readonly [ASYNC_MODULE_BRAND]: true;
|
|
37
|
-
readonly [MODULE_SETUP]: (builder: AsyncModuleBuilder) => Promise<void>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ── Builder interfaces ───────────────────────────────────────────────────────────────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* The binding surface a sync module's setup callback receives.
|
|
44
|
-
*
|
|
45
|
-
* @since 0.3.16-canary.0
|
|
46
|
-
*/
|
|
47
|
-
export interface ModuleBuilder {
|
|
48
|
-
bind<Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
|
|
49
|
-
import(...modules: Array<SyncModule>): void;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* The binding surface an async module's setup callback receives; its `import` accepts async modules too.
|
|
54
|
-
*
|
|
55
|
-
* @since 0.3.16-canary.0
|
|
56
|
-
*/
|
|
57
|
-
export interface AsyncModuleBuilder {
|
|
58
|
-
bind<Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
|
|
59
|
-
import(...modules: Array<SyncModule | AsyncModule>): void;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ── Static factories ─────────────────────────────────────────────────────────────────────────────────────────────────
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* The companion factory that creates `SyncModule` values.
|
|
66
|
-
*
|
|
67
|
-
* @since 0.3.16-canary.0
|
|
68
|
-
*/
|
|
69
|
-
export const SyncModule = {
|
|
70
|
-
create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule {
|
|
71
|
-
return {
|
|
72
|
-
name,
|
|
73
|
-
[SYNC_MODULE_BRAND]: true as const,
|
|
74
|
-
[MODULE_SETUP]: setup,
|
|
75
|
-
};
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* The companion factory that creates `AsyncModule` values.
|
|
81
|
-
*
|
|
82
|
-
* @since 0.3.16-canary.0
|
|
83
|
-
*/
|
|
84
|
-
export const AsyncModule = {
|
|
85
|
-
create(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule {
|
|
86
|
-
return {
|
|
87
|
-
name,
|
|
88
|
-
[ASYNC_MODULE_BRAND]: true as const,
|
|
89
|
-
[MODULE_SETUP]: setup,
|
|
90
|
-
};
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// ── Module — unified API ─────────────────────────────────────────────────────────────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* The unified module factory — `create` for sync modules, `createAsync` for async ones.
|
|
98
|
-
*
|
|
99
|
-
* @since 0.3.16-canary.0
|
|
100
|
-
*/
|
|
101
|
-
export const Module = {
|
|
102
|
-
create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule {
|
|
103
|
-
return SyncModule.create(name, setup);
|
|
104
|
-
},
|
|
105
|
-
createAsync(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule {
|
|
106
|
-
return AsyncModule.create(name, setup);
|
|
107
|
-
},
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Narrows a module union to `SyncModule` by checking its brand.
|
|
112
|
-
*
|
|
113
|
-
* @since 0.3.16-canary.0
|
|
114
|
-
*/
|
|
115
|
-
export function isSyncModule(module: SyncModule | AsyncModule): module is SyncModule {
|
|
116
|
-
return (module as Partial<SyncModule>)[SYNC_MODULE_BRAND] === true;
|
|
117
|
-
}
|
package/src/core/registry.ts
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
import type { Binding } from "#/core/binding";
|
|
2
|
-
import { bindingSlotEquals, bindingSlotToString } from "#/core/binding";
|
|
3
|
-
import { getOrInsert } from "#/core/map-upsert";
|
|
4
|
-
import type { BindingTag } from "#/core/tag";
|
|
5
|
-
import type { Token } from "#/core/token";
|
|
6
|
-
import type { BindingIdentifier, Constructor, DependencyKey } from "#/core/types";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* One container's binding store, indexed by token, binding id, and slot for fast lookup.
|
|
10
|
-
*
|
|
11
|
-
* @since 0.3.16-canary.0
|
|
12
|
-
*/
|
|
13
|
-
export class BindingRegistry {
|
|
14
|
-
// Monotonic mutation counter — lets resolvers version-stamp lookup caches across a container chain.
|
|
15
|
-
#version = 0;
|
|
16
|
-
// Map from token key -> array of bindings (order matters for last-wins)
|
|
17
|
-
readonly #bindings = new Map<DependencyKey, Array<Binding>>();
|
|
18
|
-
// Fast lookup by binding ID
|
|
19
|
-
readonly #byId = new Map<BindingIdentifier, Binding>();
|
|
20
|
-
// Fast path for one default slot binding with no predicate
|
|
21
|
-
readonly #fastDefault = new Map<DependencyKey, Binding>();
|
|
22
|
-
// Fast lookup for a slot carrying exactly one criterion (a lone name folds here too) — keyed by
|
|
23
|
-
// the interned criterion itself, so the pair is one hash. Unallocated until such a slot lands.
|
|
24
|
-
#simpleTagged: Map<DependencyKey, Map<BindingTag, Binding>> | undefined;
|
|
25
|
-
// Slots with two or more criteria, bucketed by their FIRST criterion. A matching slot's every
|
|
26
|
-
// criterion is in the request, so walking the request's buckets finds each candidate exactly once.
|
|
27
|
-
#multiTagged: Map<DependencyKey, Map<BindingTag, Array<Binding>>> | undefined;
|
|
28
|
-
|
|
29
|
-
// Set on the first constant registered and never cleared. Teardown only needs the negative answer
|
|
30
|
-
// to be exact, and that is what lets a container holding no constant skip its sweep entirely.
|
|
31
|
-
#heldConstantBinding = false;
|
|
32
|
-
|
|
33
|
-
/** Monotonic version — increments on every mutation. */
|
|
34
|
-
get version(): number {
|
|
35
|
-
return this.#version;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Whether a constant has ever been registered here, and so whether teardown has anything to sweep. */
|
|
39
|
-
get hasHeldConstantBinding(): boolean {
|
|
40
|
-
return this.#heldConstantBinding;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Registers a mutation the indexes don't care about (a fluent chain refining scope or an
|
|
45
|
-
* activation hook in place), so version-stamped resolver caches still invalidate.
|
|
46
|
-
*/
|
|
47
|
-
touch(): void {
|
|
48
|
-
this.#version += 1;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Adds or replaces a binding using slot-aware last-wins. Returns the displaced binding, if any.
|
|
53
|
-
*
|
|
54
|
-
* @remarks The binding is stored by reference — it must come from `createBinding`, which is
|
|
55
|
-
* what guarantees the single hidden class the resolver's hot reads depend on.
|
|
56
|
-
*/
|
|
57
|
-
add(binding: Binding): Binding | undefined {
|
|
58
|
-
this.#version += 1;
|
|
59
|
-
if (binding.kind === "constant") {
|
|
60
|
-
this.#heldConstantBinding = true;
|
|
61
|
-
}
|
|
62
|
-
const key: DependencyKey = binding.token;
|
|
63
|
-
// Copy-on-write: a selection may be walking the current list inside a `when()` predicate, so
|
|
64
|
-
// mutation replaces the array and never splices one that has been handed out.
|
|
65
|
-
const bindingsForToken = this.#bindings.get(key);
|
|
66
|
-
|
|
67
|
-
// Only apply last-wins for slot-based bindings (not predicate-only)
|
|
68
|
-
let displacedBinding: Binding | undefined;
|
|
69
|
-
let nextBindings: Array<Binding>;
|
|
70
|
-
if (bindingsForToken === undefined) {
|
|
71
|
-
nextBindings = [binding];
|
|
72
|
-
} else {
|
|
73
|
-
if (!isPurePredicateBinding(binding)) {
|
|
74
|
-
const existingIndex = bindingsForToken.findIndex(
|
|
75
|
-
(candidate) => !isPurePredicateBinding(candidate) && bindingSlotEquals(candidate.slot, binding.slot),
|
|
76
|
-
);
|
|
77
|
-
if (existingIndex !== -1) {
|
|
78
|
-
displacedBinding = bindingsForToken[existingIndex]!;
|
|
79
|
-
this.#byId.delete(displacedBinding.id);
|
|
80
|
-
this.#deindexSimpleTaggedBinding(key, displacedBinding);
|
|
81
|
-
this.#deindexMultiTaggedBinding(key, displacedBinding);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
nextBindings =
|
|
85
|
-
displacedBinding === undefined
|
|
86
|
-
? [...bindingsForToken, binding]
|
|
87
|
-
: [...bindingsForToken.filter((candidate) => candidate !== displacedBinding), binding];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
this.#bindings.set(key, nextBindings);
|
|
91
|
-
this.#byId.set(binding.id, binding);
|
|
92
|
-
this.#indexSimpleTaggedBinding(key, binding);
|
|
93
|
-
this.#indexMultiTaggedBinding(key, binding);
|
|
94
|
-
this.#refreshFastDefaultForToken(key);
|
|
95
|
-
return displacedBinding;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Remove all bindings for a token. Returns removed bindings. */
|
|
99
|
-
removeByToken(token: Token<unknown> | Constructor): Array<Binding> {
|
|
100
|
-
this.#version += 1;
|
|
101
|
-
const key: DependencyKey = token;
|
|
102
|
-
const bindingsForToken = this.#bindings.get(key) ?? [];
|
|
103
|
-
this.#bindings.delete(key);
|
|
104
|
-
this.#simpleTagged?.delete(key);
|
|
105
|
-
this.#multiTagged?.delete(key);
|
|
106
|
-
this.#fastDefault.delete(key);
|
|
107
|
-
for (const binding of bindingsForToken) {
|
|
108
|
-
this.#byId.delete(binding.id);
|
|
109
|
-
}
|
|
110
|
-
return bindingsForToken;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** Remove a specific binding by ID. Returns the removed binding or undefined. */
|
|
114
|
-
removeById(id: BindingIdentifier): Binding | undefined {
|
|
115
|
-
const binding = this.#byId.get(id);
|
|
116
|
-
if (binding === undefined) {
|
|
117
|
-
return undefined;
|
|
118
|
-
}
|
|
119
|
-
this.#version += 1;
|
|
120
|
-
this.#byId.delete(id);
|
|
121
|
-
const key: DependencyKey = binding.token;
|
|
122
|
-
const bindingsForToken = this.#bindings.get(key);
|
|
123
|
-
if (bindingsForToken !== undefined) {
|
|
124
|
-
const bindingIndex = bindingsForToken.findIndex((candidate) => candidate.id === id);
|
|
125
|
-
// Copy-on-write, like `add`: a walk holding the current array must not lose its place.
|
|
126
|
-
const remaining = bindingIndex === -1 ? bindingsForToken : bindingsForToken.toSpliced(bindingIndex, 1);
|
|
127
|
-
this.#deindexSimpleTaggedBinding(key, binding);
|
|
128
|
-
this.#deindexMultiTaggedBinding(key, binding);
|
|
129
|
-
if (remaining.length === 0) {
|
|
130
|
-
this.#bindings.delete(key);
|
|
131
|
-
this.#simpleTagged?.delete(key);
|
|
132
|
-
this.#multiTagged?.delete(key);
|
|
133
|
-
this.#fastDefault.delete(key);
|
|
134
|
-
} else {
|
|
135
|
-
this.#bindings.set(key, remaining);
|
|
136
|
-
this.#refreshFastDefaultForToken(key);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return binding;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** Get all bindings for a token. */
|
|
143
|
-
getAll(token: Token<unknown> | Constructor): ReadonlyArray<Binding> {
|
|
144
|
-
return this.#bindings.get(token) ?? [];
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Get binding by ID. */
|
|
148
|
-
getById(id: BindingIdentifier): Binding | undefined {
|
|
149
|
-
return this.#byId.get(id);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/** Check if any binding exists for token. */
|
|
153
|
-
has(token: Token<unknown> | Constructor): boolean {
|
|
154
|
-
const key: DependencyKey = token;
|
|
155
|
-
const list = this.#bindings.get(key);
|
|
156
|
-
return list !== undefined && list.length > 0;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** All bindings in the registry. */
|
|
160
|
-
allBindings(): ReadonlyArray<Binding> {
|
|
161
|
-
const allBindings: Array<Binding> = [];
|
|
162
|
-
for (const bindingsForToken of this.#bindings.values()) {
|
|
163
|
-
allBindings.push(...bindingsForToken);
|
|
164
|
-
}
|
|
165
|
-
return allBindings;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/** Remove all bindings. Returns all removed. */
|
|
169
|
-
clear(): ReadonlyArray<Binding> {
|
|
170
|
-
this.#version += 1;
|
|
171
|
-
const all = this.allBindings();
|
|
172
|
-
this.#bindings.clear();
|
|
173
|
-
this.#byId.clear();
|
|
174
|
-
this.#simpleTagged?.clear();
|
|
175
|
-
this.#multiTagged?.clear();
|
|
176
|
-
this.#fastDefault.clear();
|
|
177
|
-
return all;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Whether a slot-based binding currently occupies `binding`'s slot, so adding it would displace. */
|
|
181
|
-
hasSlotOccupant(binding: Binding): boolean {
|
|
182
|
-
if (isPurePredicateBinding(binding)) {
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
const candidates = this.#bindings.get(binding.token);
|
|
186
|
-
if (candidates === undefined) {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
return candidates.some(
|
|
190
|
-
(candidate) => !isPurePredicateBinding(candidate) && bindingSlotEquals(candidate.slot, binding.slot),
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* The binding indexed under one criterion.
|
|
196
|
-
*
|
|
197
|
-
* @remarks Exact, with no re-check: criteria are interned, so a `Map` keyed by the pair answers by
|
|
198
|
-
* identity — where a value-keyed map answered by SameValueZero and parted from `Object.is` on ±0.
|
|
199
|
-
*/
|
|
200
|
-
getSimpleTagged(token: Token<unknown> | Constructor, criterion: BindingTag): Binding | undefined {
|
|
201
|
-
return this.#simpleTagged?.get(token)?.get(criterion);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* The multi-tag bindings whose slot's first criterion is `criterion`.
|
|
206
|
-
*
|
|
207
|
-
* @remarks A prefilter, not an answer: a bucket member's remaining tags still have to be matched
|
|
208
|
-
* against the request — first-criterion bucketing only guarantees each candidate appears once.
|
|
209
|
-
*/
|
|
210
|
-
getMultiTagged(token: Token<unknown> | Constructor, criterion: BindingTag): ReadonlyArray<Binding> | undefined {
|
|
211
|
-
return this.#multiTagged?.get(token)?.get(criterion);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
getFastDefault(token: Token<unknown> | Constructor): Binding | undefined {
|
|
215
|
-
return this.#fastDefault.get(token);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/** Summarize available slot strings for a token (for error messages). */
|
|
219
|
-
availableSlotStrings(token: Token<unknown> | Constructor): Array<string> {
|
|
220
|
-
const bindingsForToken = this.#bindings.get(token) ?? [];
|
|
221
|
-
return bindingsForToken.map((binding) => bindingSlotToString(binding.slot));
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
#indexSimpleTaggedBinding(tokenKey: DependencyKey, binding: Binding): void {
|
|
225
|
-
const criterion = simpleTagOf(binding);
|
|
226
|
-
if (criterion === undefined) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
this.#simpleTagged ??= new Map();
|
|
230
|
-
const byCriterion = getOrInsert(this.#simpleTagged, tokenKey, new Map<BindingTag, Binding>());
|
|
231
|
-
byCriterion.set(criterion, binding);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
#deindexSimpleTaggedBinding(tokenKey: DependencyKey, binding: Binding): void {
|
|
235
|
-
const criterion = simpleTagOf(binding);
|
|
236
|
-
if (criterion === undefined) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
const byCriterion = this.#simpleTagged?.get(tokenKey);
|
|
240
|
-
if (byCriterion === undefined) {
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
if (byCriterion.get(criterion)?.id === binding.id) {
|
|
244
|
-
byCriterion.delete(criterion);
|
|
245
|
-
if (byCriterion.size === 0) {
|
|
246
|
-
this.#simpleTagged!.delete(tokenKey);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
#indexMultiTaggedBinding(tokenKey: DependencyKey, binding: Binding): void {
|
|
252
|
-
const firstCriterion = multiTagFirstOf(binding);
|
|
253
|
-
if (firstCriterion === undefined) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
this.#multiTagged ??= new Map();
|
|
257
|
-
const buckets = getOrInsert(this.#multiTagged, tokenKey, new Map<BindingTag, Array<Binding>>());
|
|
258
|
-
getOrInsert(buckets, firstCriterion, []).push(binding);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
#deindexMultiTaggedBinding(tokenKey: DependencyKey, binding: Binding): void {
|
|
262
|
-
const firstCriterion = multiTagFirstOf(binding);
|
|
263
|
-
if (firstCriterion === undefined) {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
const bucket = this.#multiTagged?.get(tokenKey)?.get(firstCriterion);
|
|
267
|
-
if (bucket === undefined) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
const bindingIndex = bucket.findIndex((candidate) => candidate.id === binding.id);
|
|
271
|
-
// Spliced in place: nothing walks a bucket while user code runs — candidates are gathered
|
|
272
|
-
// into their own array before any predicate is evaluated.
|
|
273
|
-
if (bindingIndex !== -1) {
|
|
274
|
-
bucket.splice(bindingIndex, 1);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
#refreshFastDefaultForToken(tokenKey: DependencyKey): void {
|
|
279
|
-
const bindingsForToken = this.#bindings.get(tokenKey);
|
|
280
|
-
const onlyBinding = bindingsForToken?.length === 1 ? bindingsForToken[0]! : undefined;
|
|
281
|
-
if (onlyBinding !== undefined && isDefaultSlotBinding(onlyBinding)) {
|
|
282
|
-
this.#fastDefault.set(tokenKey, onlyBinding);
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
this.#fastDefault.delete(tokenKey);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/** Whether the deferred tagged-slot index has had to be built. */
|
|
289
|
-
get isTaggedIndexBuilt(): boolean {
|
|
290
|
-
return this.#simpleTagged !== undefined;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* The criterion a binding is indexed under, or `undefined` when its slot carries more than one.
|
|
296
|
-
*
|
|
297
|
-
* @remarks Carries predicate-bearing bindings too: every lane that reads this index already
|
|
298
|
-
* re-checks what it finds, so an indexed hit was never unconditional.
|
|
299
|
-
*/
|
|
300
|
-
function simpleTagOf(binding: Binding): BindingTag | undefined {
|
|
301
|
-
const { tags } = binding.slot;
|
|
302
|
-
return tags.length === 1 ? tags[0] : undefined;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/** The first criterion a multi-criterion slot is bucketed under, or `undefined` for any other shape. */
|
|
306
|
-
function multiTagFirstOf(binding: Binding): BindingTag | undefined {
|
|
307
|
-
const { tags } = binding.slot;
|
|
308
|
-
return tags.length >= 2 ? tags[0] : undefined;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/** A binding nothing has to be matched against: the default slot, no predicate. */
|
|
312
|
-
function isDefaultSlotBinding(binding: Binding): boolean {
|
|
313
|
-
return binding.slot.tags.length === 0 && binding.predicate === undefined;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/** A predicate with no slot constraint: last-wins does not apply to it. */
|
|
317
|
-
function isPurePredicateBinding(binding: Binding): boolean {
|
|
318
|
-
return binding.predicate !== undefined && binding.slot.tags.length === 0;
|
|
319
|
-
}
|
package/src/core/tag.ts
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
/** Tag keys and the interned pairs they mint — the one way a slot criterion is built. */
|
|
2
|
-
|
|
3
|
-
declare const TAG_KEY_BRAND: unique symbol;
|
|
4
|
-
declare const BINDING_TAG_BRAND: unique symbol;
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* How many distinct tag keys one mask bit is shared by.
|
|
8
|
-
*
|
|
9
|
-
* @remarks Ids past this wrap, so two keys can share a bit. The mask is a prefilter only — a shared
|
|
10
|
-
* bit costs a false positive that the identity comparison then rejects, never a false negative.
|
|
11
|
-
*/
|
|
12
|
-
const MASK_WIDTH = 32;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* A slot's or a request's tag key set, as one word.
|
|
16
|
-
*
|
|
17
|
-
* @remarks A slot matches only if the request carries every key the slot declares, which is
|
|
18
|
-
* `(requestMask & slotMask) === slotMask` — the whole subset test, before any value is read. Keys
|
|
19
|
-
* are prefiltered rather than values indexed because a key set fits in a word and a value set does not.
|
|
20
|
-
*
|
|
21
|
-
* @since 0.6.0
|
|
22
|
-
*/
|
|
23
|
-
export type TagKeyMask = number & { readonly [TAG_KEY_BRAND]: "mask" };
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The empty key set: what an untagged slot and an untagged request both carry.
|
|
27
|
-
*
|
|
28
|
-
* @since 0.6.0
|
|
29
|
-
*/
|
|
30
|
-
export const NO_TAG_KEYS = 0 as TagKeyMask;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* One `[key, value]` criterion, interned so equal criteria are the same object.
|
|
34
|
-
*
|
|
35
|
-
* @remarks Only {@link TagKey.of} mints one, which is what makes identity a sound stand-in for the
|
|
36
|
-
* `Object.is` comparison the slot contract requires. Never build one by hand — the matcher and the
|
|
37
|
-
* registry index both read identity, so a hand-built criterion matches nothing.
|
|
38
|
-
*
|
|
39
|
-
* @since 0.6.0
|
|
40
|
-
*/
|
|
41
|
-
export interface BindingTag<Value = unknown> {
|
|
42
|
-
readonly key: TagKey<Value>;
|
|
43
|
-
readonly value: Value;
|
|
44
|
-
/** This pair's key mask, copied off the key so the matcher reads one field. */
|
|
45
|
-
readonly mask: TagKeyMask;
|
|
46
|
-
readonly [BINDING_TAG_BRAND]: true;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* A named tag key, and the factory for its criteria.
|
|
51
|
-
*
|
|
52
|
-
* @since 0.6.0
|
|
53
|
-
*/
|
|
54
|
-
export interface TagKey<Value = unknown> {
|
|
55
|
-
readonly name: string;
|
|
56
|
-
/** Process-monotonic, so a key set is a mask and the registry can index on a number. */
|
|
57
|
-
readonly id: number;
|
|
58
|
-
readonly mask: TagKeyMask;
|
|
59
|
-
/** The criterion for one value, interned: the same value always yields the same object. */
|
|
60
|
-
of(value: Value): BindingTag<Value>;
|
|
61
|
-
/**
|
|
62
|
-
* The interned criterion for a value, or `undefined` when none was ever minted.
|
|
63
|
-
*
|
|
64
|
-
* @remarks Reading without minting is what keeps a request-side value from being retained for
|
|
65
|
-
* the process lifetime: a value no binding ever declared has no criterion, so a lookup can
|
|
66
|
-
* answer "no match" without inserting one.
|
|
67
|
-
*/
|
|
68
|
-
peek(value: Value): BindingTag<Value> | undefined;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
let tagKeyCounter = -1;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Distinguishes `-0` from `+0` in the intern cache.
|
|
75
|
-
*
|
|
76
|
-
* @remarks A `Map` key compares by SameValueZero, which holds `+0` and `-0` equal, while tag values
|
|
77
|
-
* compare by `Object.is`, which does not. Interning them to one object would make the
|
|
78
|
-
* two indistinguishable everywhere downstream, so the negative one is cached under this instead.
|
|
79
|
-
*/
|
|
80
|
-
const NEGATIVE_ZERO_KEY: unique symbol = Symbol("di:tag-negative-zero");
|
|
81
|
-
|
|
82
|
-
function internKeyFor(value: unknown): unknown {
|
|
83
|
-
return value === 0 && Object.is(value, -0) ? NEGATIVE_ZERO_KEY : value;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Declares a tag key, whose `of()` builds the criteria a `whenTagged` and a resolve both take.
|
|
88
|
-
*
|
|
89
|
-
* @remarks The value type is checked at both ends: a key declared `tag<Region>("region")` refuses a
|
|
90
|
-
* value that is not a `Region`, so a bind site and a resolve site cannot drift apart silently.
|
|
91
|
-
*
|
|
92
|
-
* @example
|
|
93
|
-
* ```ts
|
|
94
|
-
* const Region = tag<"eu" | "us">("region");
|
|
95
|
-
* container.bind(Storage).to(S3).whenTagged(Region.of("eu"));
|
|
96
|
-
* container.resolve(Storage, { tag: Region.of("eu") });
|
|
97
|
-
* ```
|
|
98
|
-
*
|
|
99
|
-
* @since 0.6.0
|
|
100
|
-
*/
|
|
101
|
-
export function tag<Value = unknown>(name: string): TagKey<Value> {
|
|
102
|
-
tagKeyCounter += 1;
|
|
103
|
-
const id = tagKeyCounter;
|
|
104
|
-
const mask = (1 << (id % MASK_WIDTH)) as TagKeyMask;
|
|
105
|
-
const interned = new Map<unknown, BindingTag<Value>>();
|
|
106
|
-
// One-entry cache in front of the intern map: an inline `.of()` at a call site usually repeats
|
|
107
|
-
// one value, and `Object.is` is the slot contract's own comparison, so a hit is exact — ±0 stay
|
|
108
|
-
// split and `NaN` hits itself, with no `internKeyFor` detour.
|
|
109
|
-
let lastValue: Value | undefined;
|
|
110
|
-
let lastPair: BindingTag<Value> | undefined;
|
|
111
|
-
|
|
112
|
-
// The miss paths live outside `of()`/`peek()` so the hot wrappers stay small enough to inline.
|
|
113
|
-
const internPair = (value: Value): BindingTag<Value> => {
|
|
114
|
-
const cacheKey = internKeyFor(value);
|
|
115
|
-
const existing = interned.get(cacheKey);
|
|
116
|
-
|
|
117
|
-
if (existing !== undefined) {
|
|
118
|
-
lastValue = value;
|
|
119
|
-
lastPair = existing;
|
|
120
|
-
|
|
121
|
-
return existing;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const pair = { key, value, mask } as BindingTag<Value>;
|
|
125
|
-
|
|
126
|
-
interned.set(cacheKey, pair);
|
|
127
|
-
lastValue = value;
|
|
128
|
-
lastPair = pair;
|
|
129
|
-
|
|
130
|
-
return pair;
|
|
131
|
-
};
|
|
132
|
-
const peekInterned = (value: Value): BindingTag<Value> | undefined => interned.get(internKeyFor(value));
|
|
133
|
-
|
|
134
|
-
const key: TagKey<Value> = {
|
|
135
|
-
name,
|
|
136
|
-
id,
|
|
137
|
-
mask,
|
|
138
|
-
of(value: Value): BindingTag<Value> {
|
|
139
|
-
if (lastPair !== undefined && Object.is(value, lastValue)) {
|
|
140
|
-
return lastPair;
|
|
141
|
-
}
|
|
142
|
-
return internPair(value);
|
|
143
|
-
},
|
|
144
|
-
peek(value: Value): BindingTag<Value> | undefined {
|
|
145
|
-
if (lastPair !== undefined && Object.is(value, lastValue)) {
|
|
146
|
-
return lastPair;
|
|
147
|
-
}
|
|
148
|
-
return peekInterned(value);
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
return key;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* The reserved key a slot's name is a criterion of.
|
|
157
|
-
*
|
|
158
|
-
* @remarks `whenNamed(n)` and a request's `name` are sugar for `slotName.of(n)`, so one selection
|
|
159
|
-
* model serves both spellings — a name takes part in key masks, indexes and specificity like any
|
|
160
|
-
* criterion. What reserves the key is its identity; diagnostics render its criteria as `name:<value>`.
|
|
161
|
-
*
|
|
162
|
-
* @since 0.8.0
|
|
163
|
-
*/
|
|
164
|
-
export const slotName: TagKey<string> = tag<string>("di:name");
|
|
165
|
-
|
|
166
|
-
// One-entry front for the reserved key's read: a name's criterion never changes once minted, so a
|
|
167
|
-
// hit is sound forever, and a miss is never cached so a later `whenNamed` bind is still seen.
|
|
168
|
-
let lastPeekedName: string | undefined;
|
|
169
|
-
let lastPeekedCriterion: BindingTag<string> | undefined;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* The reserved criterion for a name, or `undefined` while no binding has declared it.
|
|
173
|
-
*
|
|
174
|
-
* @remarks Reads without minting, so a request-side name no binding declared is never retained.
|
|
175
|
-
*
|
|
176
|
-
* @since 0.8.0
|
|
177
|
-
*/
|
|
178
|
-
export function slotNameCriterionOf(name: string): BindingTag<string> | undefined {
|
|
179
|
-
if (name === lastPeekedName) {
|
|
180
|
-
return lastPeekedCriterion;
|
|
181
|
-
}
|
|
182
|
-
const criterion = slotName.peek(name);
|
|
183
|
-
if (criterion !== undefined) {
|
|
184
|
-
lastPeekedName = name;
|
|
185
|
-
lastPeekedCriterion = criterion;
|
|
186
|
-
}
|
|
187
|
-
return criterion;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* The key set a list of criteria covers.
|
|
192
|
-
*
|
|
193
|
-
* @since 0.6.0
|
|
194
|
-
*/
|
|
195
|
-
export function tagKeyMaskOf(tags: ReadonlyArray<BindingTag>): TagKeyMask {
|
|
196
|
-
let mask = NO_TAG_KEYS;
|
|
197
|
-
|
|
198
|
-
for (let index = 0; index < tags.length; index += 1) {
|
|
199
|
-
mask = (mask | tags[index]!.mask) as TagKeyMask;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return mask;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Whether a request carrying `requestMask` covers every key in `slotMask`.
|
|
207
|
-
*
|
|
208
|
-
* @since 0.6.0
|
|
209
|
-
*/
|
|
210
|
-
export function coversTagKeys(requestMask: TagKeyMask, slotMask: TagKeyMask): boolean {
|
|
211
|
-
return (requestMask & slotMask) === slotMask;
|
|
212
|
-
}
|
package/src/core/token.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { Constructor } from "#/core/constructor-type";
|
|
2
|
-
|
|
3
|
-
declare const TOKEN_BRAND: unique symbol;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* A branded identifier carrying the value type its bindings resolve to.
|
|
7
|
-
*
|
|
8
|
-
* @since 0.3.16-canary.0
|
|
9
|
-
*/
|
|
10
|
-
export interface Token<out Value> {
|
|
11
|
-
readonly name: string;
|
|
12
|
-
readonly [TOKEN_BRAND]: Value;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Creates a named `Token` for the given value type.
|
|
17
|
-
*
|
|
18
|
-
* @since 0.3.16-canary.0
|
|
19
|
-
*/
|
|
20
|
-
export function token<Value>(name: string): Token<Value> {
|
|
21
|
-
return { name } as Token<Value>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Returns the display name of a token or class used as a dependency key.
|
|
26
|
-
*
|
|
27
|
-
* @since 0.3.16-canary.0
|
|
28
|
-
*/
|
|
29
|
-
export function tokenName(dependency: Token<unknown> | Constructor): string {
|
|
30
|
-
return dependency.name;
|
|
31
|
-
}
|