@codefast/di 0.3.14-canary.0 → 0.3.14-canary.2
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 +28 -0
- package/README.md +54 -29
- package/dist/binding-scope.d.mts +11 -0
- package/dist/binding-scope.mjs +19 -0
- package/dist/binding-select.d.mts +8 -26
- package/dist/binding-select.mjs +56 -49
- package/dist/binding.d.mts +107 -327
- package/dist/binding.mjs +19 -324
- package/dist/constraints.d.mts +11 -29
- package/dist/constraints.mjs +32 -36
- package/dist/constructor-type.d.mts +17 -0
- package/dist/constructor-type.mjs +1 -0
- package/dist/container.d.mts +44 -128
- package/dist/container.mjs +664 -433
- package/dist/decorators/inject.d.mts +19 -50
- package/dist/decorators/inject.mjs +131 -93
- package/dist/decorators/injectable.d.mts +16 -37
- package/dist/decorators/injectable.mjs +47 -66
- package/dist/decorators/lifecycle-decorators.d.mts +2 -22
- package/dist/decorators/lifecycle-decorators.mjs +81 -37
- package/dist/dependency-graph.d.mts +24 -54
- package/dist/dependency-graph.mjs +51 -153
- package/dist/environment.d.mts +38 -12
- package/dist/environment.mjs +82 -16
- package/dist/errors.d.mts +69 -188
- package/dist/errors.mjs +92 -219
- package/dist/graph-adapters/cytoscape.d.mts +24 -0
- package/dist/graph-adapters/cytoscape.mjs +23 -0
- package/dist/graph-adapters/dot.d.mts +6 -0
- package/dist/graph-adapters/dot.mjs +17 -0
- package/dist/graph-adapters/reactflow.d.mts +29 -0
- package/dist/graph-adapters/reactflow.mjs +29 -0
- package/dist/graph-adapters/types.d.mts +2 -0
- package/dist/graph-adapters/types.mjs +1 -0
- package/dist/index.d.mts +16 -9
- package/dist/index.mjs +8 -5
- package/dist/inspector.d.mts +34 -95
- package/dist/inspector.mjs +57 -256
- package/dist/lifecycle.d.mts +20 -53
- package/dist/lifecycle.mjs +129 -99
- package/dist/metadata/metadata-keys.d.mts +9 -26
- package/dist/metadata/metadata-keys.mjs +7 -28
- package/dist/metadata/metadata-reader-token.d.mts +7 -0
- package/dist/metadata/metadata-reader-token.mjs +5 -0
- package/dist/metadata/metadata-types.d.mts +22 -71
- package/dist/metadata/symbol-metadata-reader.d.mts +10 -26
- package/dist/metadata/symbol-metadata-reader.mjs +32 -45
- package/dist/module.d.mts +30 -84
- package/dist/module.mjs +26 -72
- package/dist/registry.d.mts +32 -63
- package/dist/registry.mjs +131 -82
- package/dist/resolve-options.d.mts +18 -0
- package/dist/resolve-options.mjs +22 -0
- package/dist/resolver.d.mts +67 -190
- package/dist/resolver.mjs +715 -424
- package/dist/scope.d.mts +19 -102
- package/dist/scope.mjs +37 -192
- package/dist/token.d.mts +8 -22
- package/dist/token.mjs +9 -11
- package/dist/types.d.mts +48 -0
- package/dist/types.mjs +1 -0
- package/package.json +52 -14
- package/dist/metadata/param-registry.d.mts +0 -16
- package/dist/metadata/param-registry.mjs +0 -31
- package/dist/scope-validation.d.mts +0 -21
- package/dist/scope-validation.mjs +0 -35
package/dist/module.d.mts
CHANGED
|
@@ -1,92 +1,38 @@
|
|
|
1
|
+
import { Constructor } from "./constructor-type.mjs";
|
|
1
2
|
import { Token } from "./token.mjs";
|
|
2
|
-
import {
|
|
3
|
+
import { BindToBuilder } from "./binding.mjs";
|
|
3
4
|
|
|
4
5
|
//#region src/module.d.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* passing an {@link AsyncModule} throws {@link InternalError}) and `bind()` to register tokens.
|
|
9
|
-
*
|
|
10
|
-
* **Single slot (last-wins)** — `bind(key).to*(...)` with no `whenNamed` / `whenTagged` / `when`
|
|
11
|
-
* *before* the `to*()` call replaces all prior bindings for `key` from this module pass.
|
|
12
|
-
*
|
|
13
|
-
* **Multi-binding** — put at least one disambiguator *before* `to*()` (e.g.
|
|
14
|
-
* `bind(key).whenNamed("a").to*(...)`, `whenTagged` before `to*()`, or `when` before `to*()`).
|
|
15
|
-
* Each such line **appends** another binding so {@link Container.resolveAll} can return every
|
|
16
|
-
* implementation. Use this order in modules; chaining `.to*(...).whenNamed()` only updates that
|
|
17
|
-
* binding in place and does not stack multiple registrations across lines.
|
|
18
|
-
*/
|
|
19
|
-
type ModuleBuilder = {
|
|
20
|
-
readonly import: (...modules: Module[]) => void;
|
|
21
|
-
readonly bind: <Value>(key: Token<Value> | Constructor<Value>) => BindingBuilder<Value>;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Builder passed to the setup callback of an {@link AsyncModule}.
|
|
25
|
-
* Unlike {@link ModuleBuilder}, `import()` accepts both sync and async modules.
|
|
26
|
-
* Async sub-imports are collected and awaited **after** the setup callback returns.
|
|
27
|
-
*/
|
|
28
|
-
type AsyncModuleBuilder = {
|
|
29
|
-
readonly import: (...modules: (Module | AsyncModule)[]) => void;
|
|
30
|
-
readonly bind: <Value>(key: Token<Value> | Constructor<Value>) => BindingBuilder<Value>;
|
|
31
|
-
};
|
|
32
|
-
/**
|
|
33
|
-
* Immutable description of a bundle of bindings. A {@link Module} holds no runtime state and the
|
|
34
|
-
* same instance may be loaded into any number of containers independently (spec §7.3).
|
|
35
|
-
*
|
|
36
|
-
* The owning container is responsible for tracking which modules have been loaded and which
|
|
37
|
-
* binding ids each module produced; the module itself never sees a container reference.
|
|
38
|
-
*/
|
|
39
|
-
declare class Module {
|
|
40
|
-
/**
|
|
41
|
-
* Human-readable label used in error messages, graph output, and module-cycle diagnostics.
|
|
42
|
-
*/
|
|
6
|
+
declare const SYNC_MODULE_BRAND: unique symbol;
|
|
7
|
+
declare const ASYNC_MODULE_BRAND: unique symbol;
|
|
8
|
+
interface SyncModule {
|
|
43
9
|
readonly name: string;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*/
|
|
47
|
-
private readonly syncSetup;
|
|
48
|
-
/**
|
|
49
|
-
* @internal Use {@link Module.create} instead.
|
|
50
|
-
*/
|
|
51
|
-
private constructor();
|
|
52
|
-
/**
|
|
53
|
-
* Defines a synchronous module.
|
|
54
|
-
* @param name - Human-readable label used in error messages and debug output.
|
|
55
|
-
* @param setup - Callback that registers bindings via the {@link ModuleBuilder}.
|
|
56
|
-
*/
|
|
57
|
-
static create(name: string, setup: (builder: ModuleBuilder) => void): Module;
|
|
58
|
-
/**
|
|
59
|
-
* Defines an async module — use when setup requires awaiting (e.g. reading config, dynamic imports).
|
|
60
|
-
* Load with {@link Container.loadAsync} or {@link Container.fromModulesAsync}.
|
|
61
|
-
*/
|
|
62
|
-
static createAsync(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
|
|
63
|
-
/**
|
|
64
|
-
* @internal Invoked by the container while loading this module.
|
|
65
|
-
*/
|
|
66
|
-
runSyncSetup(builder: ModuleBuilder): void;
|
|
10
|
+
readonly [SYNC_MODULE_BRAND]: true;
|
|
11
|
+
readonly _setup: (builder: ModuleBuilder) => void;
|
|
67
12
|
}
|
|
68
|
-
|
|
69
|
-
* An async module whose setup callback may `await` before registering bindings.
|
|
70
|
-
* Prefer {@link Module.createAsync} over constructing this class directly.
|
|
71
|
-
*
|
|
72
|
-
* Load via `Container.loadAsync()` or `Container.fromModulesAsync()`;
|
|
73
|
-
* passing an `AsyncModule` to the synchronous `Container.load()` throws
|
|
74
|
-
* {@link AsyncModuleLoadError}.
|
|
75
|
-
*/
|
|
76
|
-
declare class AsyncModule {
|
|
77
|
-
/**
|
|
78
|
-
* Human-readable label used in error messages and graph output.
|
|
79
|
-
*/
|
|
13
|
+
interface AsyncModule {
|
|
80
14
|
readonly name: string;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
15
|
+
readonly [ASYNC_MODULE_BRAND]: true;
|
|
16
|
+
readonly _setup: (builder: AsyncModuleBuilder) => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
interface ModuleBuilder {
|
|
19
|
+
bind<const Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
|
|
20
|
+
import(...modules: SyncModule[]): void;
|
|
21
|
+
}
|
|
22
|
+
interface AsyncModuleBuilder {
|
|
23
|
+
bind<const Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
|
|
24
|
+
import(...modules: Array<SyncModule | AsyncModule>): void;
|
|
90
25
|
}
|
|
26
|
+
declare const SyncModule: {
|
|
27
|
+
create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule;
|
|
28
|
+
};
|
|
29
|
+
declare const AsyncModule: {
|
|
30
|
+
create(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
|
|
31
|
+
};
|
|
32
|
+
declare const Module: {
|
|
33
|
+
create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule;
|
|
34
|
+
createAsync(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
|
|
35
|
+
};
|
|
36
|
+
declare function isSyncModule(m: SyncModule | AsyncModule): m is SyncModule;
|
|
91
37
|
//#endregion
|
|
92
|
-
export { AsyncModule, AsyncModuleBuilder, Module, ModuleBuilder };
|
|
38
|
+
export { AsyncModule, AsyncModuleBuilder, Module, ModuleBuilder, SyncModule, isSyncModule };
|
package/dist/module.mjs
CHANGED
|
@@ -1,76 +1,30 @@
|
|
|
1
1
|
//#region src/module.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Defines a synchronous module.
|
|
27
|
-
* @param name - Human-readable label used in error messages and debug output.
|
|
28
|
-
* @param setup - Callback that registers bindings via the {@link ModuleBuilder}.
|
|
29
|
-
*/
|
|
30
|
-
static create(name, setup) {
|
|
31
|
-
return new Module(name, setup);
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Defines an async module — use when setup requires awaiting (e.g. reading config, dynamic imports).
|
|
35
|
-
* Load with {@link Container.loadAsync} or {@link Container.fromModulesAsync}.
|
|
36
|
-
*/
|
|
37
|
-
static createAsync(name, setup) {
|
|
38
|
-
return new AsyncModule(name, setup);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* @internal Invoked by the container while loading this module.
|
|
42
|
-
*/
|
|
43
|
-
runSyncSetup(builder) {
|
|
44
|
-
this.syncSetup(builder);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* An async module whose setup callback may `await` before registering bindings.
|
|
49
|
-
* Prefer {@link Module.createAsync} over constructing this class directly.
|
|
50
|
-
*
|
|
51
|
-
* Load via `Container.loadAsync()` or `Container.fromModulesAsync()`;
|
|
52
|
-
* passing an `AsyncModule` to the synchronous `Container.load()` throws
|
|
53
|
-
* {@link AsyncModuleLoadError}.
|
|
54
|
-
*/
|
|
55
|
-
var AsyncModule = class {
|
|
56
|
-
/**
|
|
57
|
-
* Human-readable label used in error messages and graph output.
|
|
58
|
-
*/
|
|
59
|
-
name;
|
|
60
|
-
/**
|
|
61
|
-
* The user-supplied async setup callback; invoked exactly once per `loadAsync()` call.
|
|
62
|
-
*/
|
|
63
|
-
asyncSetup;
|
|
64
|
-
constructor(name, asyncSetup) {
|
|
65
|
-
this.name = name;
|
|
66
|
-
this.asyncSetup = asyncSetup;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* @internal Invoked by the container while loading this module.
|
|
70
|
-
*/
|
|
71
|
-
async runAsyncSetup(builder) {
|
|
72
|
-
await this.asyncSetup(builder);
|
|
2
|
+
const SYNC_MODULE_BRAND = Symbol("di:sync-module");
|
|
3
|
+
const ASYNC_MODULE_BRAND = Symbol("di:async-module");
|
|
4
|
+
const SyncModule = { create(name, setup) {
|
|
5
|
+
return {
|
|
6
|
+
name,
|
|
7
|
+
[SYNC_MODULE_BRAND]: true,
|
|
8
|
+
_setup: setup
|
|
9
|
+
};
|
|
10
|
+
} };
|
|
11
|
+
const AsyncModule = { create(name, setup) {
|
|
12
|
+
return {
|
|
13
|
+
name,
|
|
14
|
+
[ASYNC_MODULE_BRAND]: true,
|
|
15
|
+
_setup: setup
|
|
16
|
+
};
|
|
17
|
+
} };
|
|
18
|
+
const Module = {
|
|
19
|
+
create(name, setup) {
|
|
20
|
+
return SyncModule.create(name, setup);
|
|
21
|
+
},
|
|
22
|
+
createAsync(name, setup) {
|
|
23
|
+
return AsyncModule.create(name, setup);
|
|
73
24
|
}
|
|
74
25
|
};
|
|
26
|
+
function isSyncModule(m) {
|
|
27
|
+
return m[SYNC_MODULE_BRAND] === true;
|
|
28
|
+
}
|
|
75
29
|
//#endregion
|
|
76
|
-
export { AsyncModule, Module };
|
|
30
|
+
export { AsyncModule, Module, SyncModule, isSyncModule };
|
package/dist/registry.d.mts
CHANGED
|
@@ -1,69 +1,38 @@
|
|
|
1
|
+
import { Constructor } from "./constructor-type.mjs";
|
|
1
2
|
import { Token } from "./token.mjs";
|
|
2
|
-
import {
|
|
3
|
+
import { BindingIdentifier } from "./types.mjs";
|
|
4
|
+
import { Binding } from "./binding.mjs";
|
|
3
5
|
|
|
4
6
|
//#region src/registry.d.ts
|
|
5
|
-
/**
|
|
6
|
-
* Key used to group {@link Binding} instances in the registry (reference equality for tokens).
|
|
7
|
-
*/
|
|
8
|
-
type RegistryKey = Token<unknown> | Constructor<unknown>;
|
|
9
|
-
/**
|
|
10
|
-
* Flat, in-memory storage for {@link Binding} entries keyed by {@link RegistryKey}.
|
|
11
|
-
* Each key maps to an ordered list of bindings (multi-binding support).
|
|
12
|
-
*
|
|
13
|
-
* The registry is a "dumb" store — it does not perform selection, scope caching, or
|
|
14
|
-
* lifecycle management. Those concerns live in `DependencyResolver` and `ScopeManager`.
|
|
15
|
-
*
|
|
16
|
-
* Mutation styles:
|
|
17
|
-
* - `add` — append-only; never removes existing entries.
|
|
18
|
-
* - `replaceById` — swaps a single binding in place by ID; no removal callback.
|
|
19
|
-
* - `remove` / `removeById` — delete entries **without** notifying callers; the caller
|
|
20
|
-
* is responsible for draining the scope cache before calling these.
|
|
21
|
-
* - `replaceKeyLastWins` — replaces all bindings for a key with a single new one **and**
|
|
22
|
-
* invokes the `onReplaced` callback for each evicted binding, giving the caller
|
|
23
|
-
* (typically the container or scope manager) a chance to run deactivation.
|
|
24
|
-
*/
|
|
25
7
|
declare class BindingRegistry {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
*/
|
|
55
|
-
removeById(id: BindingIdentifier): void;
|
|
56
|
-
/**
|
|
57
|
-
* Swaps the binding with the given `id` in place, preserving its position in the list.
|
|
58
|
-
*/
|
|
59
|
-
replaceById(id: BindingIdentifier, next: Binding<unknown>): void;
|
|
60
|
-
/**
|
|
61
|
-
* Replaces all bindings for `key` with a single new binding (module "last-wins" semantics).
|
|
62
|
-
* Unlike {@link remove} / {@link removeById}, this method invokes `onReplaced` for every
|
|
63
|
-
* evicted binding **before** inserting the replacement, giving the caller (e.g. the
|
|
64
|
-
* container's scope manager) a chance to release cached instances and run deactivation hooks.
|
|
65
|
-
*/
|
|
66
|
-
replaceKeyLastWins<Value>(key: Token<Value> | Constructor<Value>, binding: Binding<Value>, onReplaced: (removed: Binding<unknown>) => void): void;
|
|
8
|
+
private readonly _bindings;
|
|
9
|
+
private readonly _byId;
|
|
10
|
+
private readonly _simpleNamed;
|
|
11
|
+
private readonly _fastDefault;
|
|
12
|
+
/** Add or replace binding using slot-aware last-wins. */
|
|
13
|
+
add(binding: Binding): void;
|
|
14
|
+
/** Remove all bindings for a token. Returns removed bindings. */
|
|
15
|
+
removeByToken(t: Token<unknown> | Constructor): Binding[];
|
|
16
|
+
/** Remove a specific binding by ID. Returns the removed binding or undefined. */
|
|
17
|
+
removeById(id: BindingIdentifier): Binding | undefined;
|
|
18
|
+
/** Get all bindings for a token. */
|
|
19
|
+
getAll(t: Token<unknown> | Constructor): readonly Binding[];
|
|
20
|
+
/** Get binding by ID. */
|
|
21
|
+
getById(id: BindingIdentifier): Binding | undefined;
|
|
22
|
+
/** Check if any binding exists for token. */
|
|
23
|
+
has(t: Token<unknown> | Constructor): boolean;
|
|
24
|
+
/** All bindings in the registry. */
|
|
25
|
+
allBindings(): readonly Binding[];
|
|
26
|
+
/** Remove all bindings. Returns all removed. */
|
|
27
|
+
clear(): readonly Binding[];
|
|
28
|
+
getSimpleNamed(token: Token<unknown> | Constructor, name: string): Binding | undefined;
|
|
29
|
+
getFastDefault(token: Token<unknown> | Constructor): Binding | undefined;
|
|
30
|
+
/** Summarize available slot strings for a token (for error messages). */
|
|
31
|
+
availableSlotStrings(t: Token<unknown> | Constructor): string[];
|
|
32
|
+
private _isPurePredicateBinding;
|
|
33
|
+
private _indexSimpleNamedBinding;
|
|
34
|
+
private _deindexSimpleNamedBinding;
|
|
35
|
+
private _refreshFastDefaultForToken;
|
|
67
36
|
}
|
|
68
37
|
//#endregion
|
|
69
|
-
export { BindingRegistry
|
|
38
|
+
export { BindingRegistry };
|
package/dist/registry.mjs
CHANGED
|
@@ -1,95 +1,144 @@
|
|
|
1
|
+
import { slotKeyEquals } from "./binding.mjs";
|
|
1
2
|
//#region src/registry.ts
|
|
2
|
-
/**
|
|
3
|
-
* Flat, in-memory storage for {@link Binding} entries keyed by {@link RegistryKey}.
|
|
4
|
-
* Each key maps to an ordered list of bindings (multi-binding support).
|
|
5
|
-
*
|
|
6
|
-
* The registry is a "dumb" store — it does not perform selection, scope caching, or
|
|
7
|
-
* lifecycle management. Those concerns live in `DependencyResolver` and `ScopeManager`.
|
|
8
|
-
*
|
|
9
|
-
* Mutation styles:
|
|
10
|
-
* - `add` — append-only; never removes existing entries.
|
|
11
|
-
* - `replaceById` — swaps a single binding in place by ID; no removal callback.
|
|
12
|
-
* - `remove` / `removeById` — delete entries **without** notifying callers; the caller
|
|
13
|
-
* is responsible for draining the scope cache before calling these.
|
|
14
|
-
* - `replaceKeyLastWins` — replaces all bindings for a key with a single new one **and**
|
|
15
|
-
* invokes the `onReplaced` callback for each evicted binding, giving the caller
|
|
16
|
-
* (typically the container or scope manager) a chance to run deactivation.
|
|
17
|
-
*/
|
|
18
3
|
var BindingRegistry = class {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
4
|
+
_bindings = /* @__PURE__ */ new Map();
|
|
5
|
+
_byId = /* @__PURE__ */ new Map();
|
|
6
|
+
_simpleNamed = /* @__PURE__ */ new Map();
|
|
7
|
+
_fastDefault = /* @__PURE__ */ new Map();
|
|
8
|
+
/** Add or replace binding using slot-aware last-wins. */
|
|
9
|
+
add(binding) {
|
|
10
|
+
const key = binding.token;
|
|
11
|
+
let list = this._bindings.get(key);
|
|
12
|
+
if (list === void 0) {
|
|
13
|
+
list = [];
|
|
14
|
+
this._bindings.set(key, list);
|
|
15
|
+
}
|
|
16
|
+
if (!this._isPurePredicateBinding(binding)) {
|
|
17
|
+
const existingIndex = list.findIndex((b) => !this._isPurePredicateBinding(b) && slotKeyEquals(b.slot, binding.slot));
|
|
18
|
+
if (existingIndex !== -1) {
|
|
19
|
+
const old = list[existingIndex];
|
|
20
|
+
this._byId.delete(old.id);
|
|
21
|
+
list.splice(existingIndex, 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
list.push(binding);
|
|
25
|
+
this._byId.set(binding.id, binding);
|
|
26
|
+
this._indexSimpleNamedBinding(key, binding);
|
|
27
|
+
this._refreshFastDefaultForToken(key);
|
|
33
28
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
/** Remove all bindings for a token. Returns removed bindings. */
|
|
30
|
+
removeByToken(t) {
|
|
31
|
+
const key = t;
|
|
32
|
+
const list = this._bindings.get(key) ?? [];
|
|
33
|
+
this._bindings.delete(key);
|
|
34
|
+
this._simpleNamed.delete(key);
|
|
35
|
+
this._fastDefault.delete(key);
|
|
36
|
+
for (const b of list) this._byId.delete(b.id);
|
|
37
|
+
return list;
|
|
39
38
|
}
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
/** Remove a specific binding by ID. Returns the removed binding or undefined. */
|
|
40
|
+
removeById(id) {
|
|
41
|
+
const binding = this._byId.get(id);
|
|
42
|
+
if (binding === void 0) return;
|
|
43
|
+
this._byId.delete(id);
|
|
44
|
+
const key = binding.token;
|
|
45
|
+
const list = this._bindings.get(key);
|
|
46
|
+
if (list !== void 0) {
|
|
47
|
+
const idx = list.findIndex((b) => b.id === id);
|
|
48
|
+
if (idx !== -1) list.splice(idx, 1);
|
|
49
|
+
this._deindexSimpleNamedBinding(key, binding);
|
|
50
|
+
if (list.length === 0) {
|
|
51
|
+
this._bindings.delete(key);
|
|
52
|
+
this._simpleNamed.delete(key);
|
|
53
|
+
this._fastDefault.delete(key);
|
|
54
|
+
} else this._refreshFastDefaultForToken(key);
|
|
55
|
+
}
|
|
56
|
+
return binding;
|
|
46
57
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
listEntries() {
|
|
51
|
-
return [...this.bindingsByKey.entries()].map(([key, bindings]) => ({
|
|
52
|
-
key,
|
|
53
|
-
bindings
|
|
54
|
-
}));
|
|
58
|
+
/** Get all bindings for a token. */
|
|
59
|
+
getAll(t) {
|
|
60
|
+
return this._bindings.get(t) ?? [];
|
|
55
61
|
}
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
/** Get binding by ID. */
|
|
63
|
+
getById(id) {
|
|
64
|
+
return this._byId.get(id);
|
|
65
|
+
}
|
|
66
|
+
/** Check if any binding exists for token. */
|
|
67
|
+
has(t) {
|
|
68
|
+
const key = t;
|
|
69
|
+
const list = this._bindings.get(key);
|
|
70
|
+
return list !== void 0 && list.length > 0;
|
|
71
|
+
}
|
|
72
|
+
/** All bindings in the registry. */
|
|
73
|
+
allBindings() {
|
|
74
|
+
const result = [];
|
|
75
|
+
for (const list of this._bindings.values()) result.push(...list);
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/** Remove all bindings. Returns all removed. */
|
|
79
|
+
clear() {
|
|
80
|
+
const all = this.allBindings();
|
|
81
|
+
this._bindings.clear();
|
|
82
|
+
this._byId.clear();
|
|
83
|
+
this._simpleNamed.clear();
|
|
84
|
+
this._fastDefault.clear();
|
|
85
|
+
return all;
|
|
86
|
+
}
|
|
87
|
+
getSimpleNamed(token, name) {
|
|
88
|
+
return this._simpleNamed.get(token)?.get(name);
|
|
89
|
+
}
|
|
90
|
+
getFastDefault(token) {
|
|
91
|
+
return this._fastDefault.get(token);
|
|
92
|
+
}
|
|
93
|
+
/** Summarize available slot strings for a token (for error messages). */
|
|
94
|
+
availableSlotStrings(t) {
|
|
95
|
+
return (this._bindings.get(t) ?? []).map((b) => {
|
|
96
|
+
const s = b.slot;
|
|
97
|
+
if (s.name === void 0 && s.tags.length === 0) return "default";
|
|
98
|
+
const parts = [];
|
|
99
|
+
if (s.name !== void 0) parts.push(`name:${s.name}`);
|
|
100
|
+
for (const [k, v] of s.tags) parts.push(`tag:${k}=${String(v)}`);
|
|
101
|
+
return parts.join(",");
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
_isPurePredicateBinding(binding) {
|
|
105
|
+
const slot = binding.slot;
|
|
106
|
+
const hasPredicate = binding.predicate !== void 0;
|
|
107
|
+
const hasConstraint = slot.name !== void 0 || slot.tags.length > 0;
|
|
108
|
+
return hasPredicate && !hasConstraint;
|
|
109
|
+
}
|
|
110
|
+
_indexSimpleNamedBinding(tokenKeyValue, binding) {
|
|
111
|
+
const slot = binding.slot;
|
|
112
|
+
if (slot.name === void 0 || slot.tags.length > 0) return;
|
|
113
|
+
let byName = this._simpleNamed.get(tokenKeyValue);
|
|
114
|
+
if (byName === void 0) {
|
|
115
|
+
byName = /* @__PURE__ */ new Map();
|
|
116
|
+
this._simpleNamed.set(tokenKeyValue, byName);
|
|
66
117
|
}
|
|
118
|
+
byName.set(slot.name, binding);
|
|
67
119
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
updated[index] = next;
|
|
77
|
-
this.bindingsByKey.set(registryKey, updated);
|
|
78
|
-
return;
|
|
120
|
+
_deindexSimpleNamedBinding(tokenKeyValue, binding) {
|
|
121
|
+
const slot = binding.slot;
|
|
122
|
+
if (slot.name === void 0 || slot.tags.length > 0) return;
|
|
123
|
+
const byName = this._simpleNamed.get(tokenKeyValue);
|
|
124
|
+
if (byName === void 0) return;
|
|
125
|
+
if (byName.get(slot.name)?.id === binding.id) {
|
|
126
|
+
byName.delete(slot.name);
|
|
127
|
+
if (byName.size === 0) this._simpleNamed.delete(tokenKeyValue);
|
|
79
128
|
}
|
|
80
129
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.
|
|
130
|
+
_refreshFastDefaultForToken(tokenKeyValue) {
|
|
131
|
+
const list = this._bindings.get(tokenKeyValue);
|
|
132
|
+
if (list === void 0 || list.length !== 1) {
|
|
133
|
+
this._fastDefault.delete(tokenKeyValue);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const onlyBinding = list[0];
|
|
137
|
+
if (!(onlyBinding.slot.name === void 0 && onlyBinding.slot.tags.length === 0) || onlyBinding.predicate !== void 0) {
|
|
138
|
+
this._fastDefault.delete(tokenKeyValue);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
this._fastDefault.set(tokenKeyValue, onlyBinding);
|
|
93
142
|
}
|
|
94
143
|
};
|
|
95
144
|
//#endregion
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ResolveOptions } from "./types.mjs";
|
|
2
|
+
import { SlotKey } from "./binding.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/resolve-options.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Builds a {@link ResolveOptions} safe for `exactOptionalPropertyTypes`:
|
|
7
|
+
* omits keys instead of assigning `undefined`.
|
|
8
|
+
*/
|
|
9
|
+
declare function injectableSlotToResolveOptions(slot: {
|
|
10
|
+
readonly name?: string;
|
|
11
|
+
readonly tags?: ReadonlyArray<readonly [string, unknown]>;
|
|
12
|
+
}): ResolveOptions | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Hint from a binding {@link SlotKey} (tags may be empty; omits when nothing to match).
|
|
15
|
+
*/
|
|
16
|
+
declare function slotKeyToResolveOptions(slot: SlotKey): ResolveOptions | undefined;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { injectableSlotToResolveOptions, slotKeyToResolveOptions };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/resolve-options.ts
|
|
2
|
+
/**
|
|
3
|
+
* Builds a {@link ResolveOptions} safe for `exactOptionalPropertyTypes`:
|
|
4
|
+
* omits keys instead of assigning `undefined`.
|
|
5
|
+
*/
|
|
6
|
+
function injectableSlotToResolveOptions(slot) {
|
|
7
|
+
const options = {};
|
|
8
|
+
if (slot.name !== void 0) options.name = slot.name;
|
|
9
|
+
if (slot.tags !== void 0) options.tags = slot.tags;
|
|
10
|
+
return options.name !== void 0 || options.tags !== void 0 ? options : void 0;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Hint from a binding {@link SlotKey} (tags may be empty; omits when nothing to match).
|
|
14
|
+
*/
|
|
15
|
+
function slotKeyToResolveOptions(slot) {
|
|
16
|
+
const options = {};
|
|
17
|
+
if (slot.name !== void 0) options.name = slot.name;
|
|
18
|
+
if (slot.tags.length > 0) options.tags = slot.tags;
|
|
19
|
+
return options.name !== void 0 || options.tags !== void 0 ? options : void 0;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { injectableSlotToResolveOptions, slotKeyToResolveOptions };
|