@codefast/di 0.5.0-canary.4 → 0.5.0-canary.6
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 +12 -0
- package/README.md +67 -499
- package/dist/binding-scope.d.ts +10 -0
- package/dist/binding-scope.d.ts.map +1 -0
- package/dist/binding-scope.js +24 -0
- package/dist/binding-scope.js.map +1 -0
- package/dist/binding-select.d.ts +16 -0
- package/dist/binding-select.d.ts.map +1 -0
- package/dist/binding-select.js +130 -0
- package/dist/binding-select.js.map +1 -0
- package/dist/binding.d.ts +194 -0
- package/dist/binding.d.ts.map +1 -0
- package/dist/binding.js +46 -0
- package/dist/binding.js.map +1 -0
- package/dist/constraints.d.ts +51 -0
- package/dist/constraints.d.ts.map +1 -0
- package/dist/constraints.js +88 -0
- package/dist/constraints.js.map +1 -0
- package/dist/{constructor-type.d.mts → constructor-type.d.ts} +3 -5
- package/dist/constructor-type.d.ts.map +1 -0
- package/dist/constructor-type.js +2 -0
- package/dist/constructor-type.js.map +1 -0
- package/dist/container.d.ts +58 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +853 -0
- package/dist/container.js.map +1 -0
- package/dist/decorators/inject.d.ts +46 -0
- package/dist/decorators/inject.d.ts.map +1 -0
- package/dist/decorators/inject.js +145 -0
- package/dist/decorators/inject.js.map +1 -0
- package/dist/decorators/injectable.d.ts +29 -0
- package/dist/decorators/injectable.d.ts.map +1 -0
- package/dist/decorators/injectable.js +53 -0
- package/dist/decorators/injectable.js.map +1 -0
- package/dist/decorators/lifecycle-decorators.d.ts +9 -0
- package/dist/decorators/lifecycle-decorators.d.ts.map +1 -0
- package/dist/decorators/lifecycle-decorators.js +40 -0
- package/dist/decorators/lifecycle-decorators.js.map +1 -0
- package/dist/dependency-graph.d.ts +40 -0
- package/dist/dependency-graph.d.ts.map +1 -0
- package/dist/dependency-graph.js +66 -0
- package/dist/dependency-graph.js.map +1 -0
- package/dist/environment.d.ts +51 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +100 -0
- package/dist/environment.js.map +1 -0
- package/dist/errors.d.ts +150 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +197 -0
- package/dist/errors.js.map +1 -0
- package/dist/graph-adapters/cytoscape.d.ts +33 -0
- package/dist/graph-adapters/cytoscape.d.ts.map +1 -0
- package/dist/graph-adapters/cytoscape.js +30 -0
- package/dist/graph-adapters/cytoscape.js.map +1 -0
- package/dist/graph-adapters/dot.d.ts +6 -0
- package/dist/graph-adapters/dot.d.ts.map +1 -0
- package/dist/graph-adapters/dot.js +18 -0
- package/dist/graph-adapters/dot.js.map +1 -0
- package/dist/graph-adapters/reactflow.d.ts +38 -0
- package/dist/graph-adapters/reactflow.d.ts.map +1 -0
- package/dist/graph-adapters/reactflow.js +23 -0
- package/dist/graph-adapters/reactflow.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/inspector.d.ts +44 -0
- package/dist/inspector.d.ts.map +1 -0
- package/dist/inspector.js +82 -0
- package/dist/inspector.js.map +1 -0
- package/dist/lifecycle.d.ts +21 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +174 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/metadata/{metadata-keys.d.mts → metadata-keys.d.ts} +5 -7
- package/dist/metadata/metadata-keys.d.ts.map +1 -0
- package/dist/metadata/metadata-keys.js +25 -0
- package/dist/metadata/metadata-keys.js.map +1 -0
- package/dist/metadata/metadata-reader-token.d.ts +7 -0
- package/dist/metadata/metadata-reader-token.d.ts.map +1 -0
- package/dist/metadata/metadata-reader-token.js +6 -0
- package/dist/metadata/metadata-reader-token.js.map +1 -0
- package/dist/metadata/metadata-types.d.ts +48 -0
- package/dist/metadata/metadata-types.d.ts.map +1 -0
- package/dist/metadata/metadata-types.js +2 -0
- package/dist/metadata/metadata-types.js.map +1 -0
- package/dist/metadata/symbol-metadata-reader.d.ts +20 -0
- package/dist/metadata/symbol-metadata-reader.d.ts.map +1 -0
- package/dist/metadata/symbol-metadata-reader.js +34 -0
- package/dist/metadata/symbol-metadata-reader.js.map +1 -0
- package/dist/module.d.ts +60 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +47 -0
- package/dist/module.js.map +1 -0
- package/dist/registry.d.ts +41 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +208 -0
- package/dist/registry.js.map +1 -0
- package/dist/resolve-options.d.ts +19 -0
- package/dist/resolve-options.d.ts.map +1 -0
- package/dist/resolve-options.js +32 -0
- package/dist/resolve-options.js.map +1 -0
- package/dist/resolver.d.ts +90 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +1208 -0
- package/dist/resolver.js.map +1 -0
- package/dist/scope.d.ts +25 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +61 -0
- package/dist/scope.js.map +1 -0
- package/dist/token.d.ts +23 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +25 -0
- package/dist/token.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +87 -79
- package/dist/binding-scope.d.mts +0 -13
- package/dist/binding-scope.mjs +0 -21
- package/dist/binding-select.d.mts +0 -19
- package/dist/binding-select.mjs +0 -72
- package/dist/binding.d.mts +0 -191
- package/dist/binding.mjs +0 -36
- package/dist/constraints.d.mts +0 -55
- package/dist/constraints.mjs +0 -87
- package/dist/constructor-type.mjs +0 -1
- package/dist/container.d.mts +0 -62
- package/dist/container.mjs +0 -741
- package/dist/decorators/inject.d.mts +0 -49
- package/dist/decorators/inject.mjs +0 -136
- package/dist/decorators/injectable.d.mts +0 -32
- package/dist/decorators/injectable.mjs +0 -57
- package/dist/decorators/lifecycle-decorators.d.mts +0 -11
- package/dist/decorators/lifecycle-decorators.mjs +0 -38
- package/dist/dependency-graph.d.mts +0 -43
- package/dist/dependency-graph.mjs +0 -63
- package/dist/environment.d.mts +0 -55
- package/dist/environment.mjs +0 -98
- package/dist/errors.d.mts +0 -153
- package/dist/errors.mjs +0 -197
- package/dist/graph-adapters/cytoscape.d.mts +0 -36
- package/dist/graph-adapters/cytoscape.mjs +0 -26
- package/dist/graph-adapters/dot.d.mts +0 -9
- package/dist/graph-adapters/dot.mjs +0 -20
- package/dist/graph-adapters/reactflow.d.mts +0 -41
- package/dist/graph-adapters/reactflow.mjs +0 -29
- package/dist/index.d.mts +0 -18
- package/dist/index.mjs +0 -12
- package/dist/inspector.d.mts +0 -48
- package/dist/inspector.mjs +0 -71
- package/dist/lifecycle.d.mts +0 -25
- package/dist/lifecycle.mjs +0 -118
- package/dist/metadata/metadata-keys.mjs +0 -27
- package/dist/metadata/metadata-reader-token.d.mts +0 -10
- package/dist/metadata/metadata-reader-token.mjs +0 -8
- package/dist/metadata/metadata-types.d.mts +0 -51
- package/dist/metadata/metadata-types.mjs +0 -1
- package/dist/metadata/symbol-metadata-reader.d.mts +0 -23
- package/dist/metadata/symbol-metadata-reader.mjs +0 -29
- package/dist/module.d.mts +0 -62
- package/dist/module.mjs +0 -42
- package/dist/registry.d.mts +0 -45
- package/dist/registry.mjs +0 -163
- package/dist/resolve-options.d.mts +0 -22
- package/dist/resolve-options.mjs +0 -28
- package/dist/resolver.d.mts +0 -94
- package/dist/resolver.mjs +0 -767
- package/dist/scope.d.mts +0 -28
- package/dist/scope.mjs +0 -58
- package/dist/token.d.mts +0 -25
- package/dist/token.mjs +0 -22
- package/dist/types.d.mts +0 -92
- package/dist/types.mjs +0 -1
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
import { DEFAULT_BINDING_SLOT, generateBindingId } from "#/binding";
|
|
2
|
+
import { effectiveBindingScope } from "#/binding-scope";
|
|
3
|
+
import { normalizeToDescriptor } from "#/decorators/inject";
|
|
4
|
+
import { buildDependencyGraph } from "#/dependency-graph";
|
|
5
|
+
import { AsyncModuleLoadError, CircularDependencyError, DisposedContainerError, InternalError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError, } from "#/errors";
|
|
6
|
+
import { Inspector } from "#/inspector";
|
|
7
|
+
import { LifecycleManager } from "#/lifecycle";
|
|
8
|
+
import { MetadataReaderToken } from "#/metadata/metadata-reader-token";
|
|
9
|
+
import { defaultMetadataReader } from "#/metadata/symbol-metadata-reader";
|
|
10
|
+
import { isSyncModule } from "#/module";
|
|
11
|
+
import { BindingRegistry } from "#/registry";
|
|
12
|
+
import { injectionSlotToResolveOptions, bindingSlotToResolveOptions } from "#/resolve-options";
|
|
13
|
+
import { DependencyResolver } from "#/resolver";
|
|
14
|
+
import { ScopeManager } from "#/scope";
|
|
15
|
+
import { tokenName } from "#/token";
|
|
16
|
+
// ── DefaultContainer ──────────────────────────────────────────────────────────
|
|
17
|
+
class DefaultContainer {
|
|
18
|
+
_disposed = false;
|
|
19
|
+
_registry;
|
|
20
|
+
_scope;
|
|
21
|
+
_lifecycle;
|
|
22
|
+
_resolver;
|
|
23
|
+
_inspector;
|
|
24
|
+
_parent;
|
|
25
|
+
// Module tracking: module -> ref count
|
|
26
|
+
_moduleRefs = new Map();
|
|
27
|
+
// Module bindings: module -> array of binding IDs registered by it
|
|
28
|
+
_moduleBindingIds = new Map();
|
|
29
|
+
constructor(parent) {
|
|
30
|
+
this._parent = parent;
|
|
31
|
+
this._registry = new BindingRegistry();
|
|
32
|
+
this._scope = new ScopeManager(parent !== undefined);
|
|
33
|
+
this._lifecycle = new LifecycleManager();
|
|
34
|
+
this._inspector = new Inspector(this._registry, this._scope, parent !== undefined, () => this._disposed);
|
|
35
|
+
this._initResolver();
|
|
36
|
+
}
|
|
37
|
+
_initResolver() {
|
|
38
|
+
const metadataReader = this._getMetadataReader();
|
|
39
|
+
const parentResolver = this._parent?._resolver;
|
|
40
|
+
this._resolver = new DependencyResolver(this._registry, this._scope, this._lifecycle, metadataReader, this, parentResolver);
|
|
41
|
+
}
|
|
42
|
+
_getMetadataReader() {
|
|
43
|
+
// Check if a custom MetadataReader has been bound
|
|
44
|
+
const metaBindings = this._registry.getAll(MetadataReaderToken);
|
|
45
|
+
if (metaBindings.length > 0) {
|
|
46
|
+
try {
|
|
47
|
+
return this._resolver.resolve(MetadataReaderToken, undefined, [], []);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// fall through to default
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (this._parent !== undefined) {
|
|
54
|
+
return this._parent._getMetadataReader();
|
|
55
|
+
}
|
|
56
|
+
return defaultMetadataReader;
|
|
57
|
+
}
|
|
58
|
+
get isDisposed() {
|
|
59
|
+
return this._disposed;
|
|
60
|
+
}
|
|
61
|
+
// ── Binding ──────────────────────────────────────────────────────────────
|
|
62
|
+
bind(token) {
|
|
63
|
+
this._assertNotDisposed();
|
|
64
|
+
return this._createBindToBuilder(token);
|
|
65
|
+
}
|
|
66
|
+
_createBindToBuilder(token, moduleRef) {
|
|
67
|
+
const registry = this._registry;
|
|
68
|
+
const commitBinding = (binding, previousId) => {
|
|
69
|
+
if (previousId !== undefined) {
|
|
70
|
+
registry.removeById(previousId);
|
|
71
|
+
if (moduleRef !== undefined) {
|
|
72
|
+
const ids = this._moduleBindingIds.get(moduleRef);
|
|
73
|
+
if (ids !== undefined) {
|
|
74
|
+
const idx = ids.indexOf(previousId);
|
|
75
|
+
if (idx !== -1) {
|
|
76
|
+
ids.splice(idx, 1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
registry.add(binding);
|
|
82
|
+
if (moduleRef !== undefined) {
|
|
83
|
+
let ids = this._moduleBindingIds.get(moduleRef);
|
|
84
|
+
if (ids === undefined) {
|
|
85
|
+
ids = [];
|
|
86
|
+
this._moduleBindingIds.set(moduleRef, ids);
|
|
87
|
+
}
|
|
88
|
+
ids.push(binding.id);
|
|
89
|
+
}
|
|
90
|
+
return binding.id;
|
|
91
|
+
};
|
|
92
|
+
return new BindingEntry(token, commitBinding);
|
|
93
|
+
}
|
|
94
|
+
unbind(tokenOrId) {
|
|
95
|
+
this._assertNotDisposed();
|
|
96
|
+
this._unbindSync(tokenOrId);
|
|
97
|
+
}
|
|
98
|
+
/** Remove bindings from registry + scope and collect [binding, instance] pairs for deactivation. */
|
|
99
|
+
_collectDeactivationPairs(tokenOrId) {
|
|
100
|
+
const pairs = [];
|
|
101
|
+
for (const binding of this._getBindingsForTokenOrId(tokenOrId)) {
|
|
102
|
+
this._registry.removeById(binding.id);
|
|
103
|
+
if (this._scope.hasSingleton(binding.id)) {
|
|
104
|
+
pairs.push([binding, this._scope.getSingleton(binding.id)]);
|
|
105
|
+
this._scope.deleteSingleton(binding.id);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return pairs;
|
|
109
|
+
}
|
|
110
|
+
/** Drain singleton scope entries for a set of already-removed bindings. */
|
|
111
|
+
_drainSingletons(bindings) {
|
|
112
|
+
const pairs = [];
|
|
113
|
+
for (const binding of bindings) {
|
|
114
|
+
if (this._scope.hasSingleton(binding.id)) {
|
|
115
|
+
pairs.push([binding, this._scope.getSingleton(binding.id)]);
|
|
116
|
+
this._scope.deleteSingleton(binding.id);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return pairs;
|
|
120
|
+
}
|
|
121
|
+
_unbindSync(tokenOrId) {
|
|
122
|
+
const reader = this._getMetadataReader();
|
|
123
|
+
for (const [binding, instance] of this._collectDeactivationPairs(tokenOrId)) {
|
|
124
|
+
this._lifecycle.runDeactivationSync(binding, instance, reader);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async unbindAsync(tokenOrId) {
|
|
128
|
+
this._assertNotDisposed();
|
|
129
|
+
const reader = this._getMetadataReader();
|
|
130
|
+
for (const [binding, instance] of this._collectDeactivationPairs(tokenOrId)) {
|
|
131
|
+
await this._lifecycle.runDeactivation(binding, instance, reader);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
unbindAll() {
|
|
135
|
+
this._assertNotDisposed();
|
|
136
|
+
const reader = this._getMetadataReader();
|
|
137
|
+
for (const [binding, instance] of this._drainSingletons(this._registry.clear())) {
|
|
138
|
+
this._lifecycle.runDeactivationSync(binding, instance, reader);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async unbindAllAsync() {
|
|
142
|
+
this._assertNotDisposed();
|
|
143
|
+
const reader = this._getMetadataReader();
|
|
144
|
+
for (const [binding, instance] of this._drainSingletons(this._registry.clear())) {
|
|
145
|
+
await this._lifecycle.runDeactivation(binding, instance, reader);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
rebind(token) {
|
|
149
|
+
this._assertNotDisposed();
|
|
150
|
+
if (!this._registry.has(token)) {
|
|
151
|
+
throw new RebindUnboundTokenError(tokenName(token));
|
|
152
|
+
}
|
|
153
|
+
// Unbind existing (sync — if async deactivation, will throw AsyncDeactivationError)
|
|
154
|
+
this._unbindSync(token);
|
|
155
|
+
return this._createBindToBuilder(token);
|
|
156
|
+
}
|
|
157
|
+
_getBindingsForTokenOrId(tokenOrId) {
|
|
158
|
+
if (typeof tokenOrId === "string") {
|
|
159
|
+
// It's a BindingIdentifier (string)
|
|
160
|
+
const binding = this._registry.getById(tokenOrId);
|
|
161
|
+
return binding !== undefined ? [binding] : [];
|
|
162
|
+
}
|
|
163
|
+
return [...this._registry.getAll(tokenOrId)];
|
|
164
|
+
}
|
|
165
|
+
// ── Module ────────────────────────────────────────────────────────────────
|
|
166
|
+
load(...modules) {
|
|
167
|
+
this._assertNotDisposed();
|
|
168
|
+
this._loadSyncModules(modules);
|
|
169
|
+
}
|
|
170
|
+
_loadSyncModules(modules) {
|
|
171
|
+
// Collect all modules in topological order (dedup by identity)
|
|
172
|
+
const toLoad = this._collectModuleDeps(modules);
|
|
173
|
+
for (const module of toLoad) {
|
|
174
|
+
if (!isSyncModule(module)) {
|
|
175
|
+
throw new AsyncModuleLoadError(module.name);
|
|
176
|
+
}
|
|
177
|
+
const moduleRef = module;
|
|
178
|
+
const existing = this._moduleRefs.get(moduleRef);
|
|
179
|
+
if (existing !== undefined) {
|
|
180
|
+
this._moduleRefs.set(moduleRef, existing + 1);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
this._moduleRefs.set(moduleRef, 1);
|
|
184
|
+
const builder = this._createModuleBuilder(moduleRef);
|
|
185
|
+
module._setup(builder);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
_collectModuleDeps(modules) {
|
|
189
|
+
const seen = new Set();
|
|
190
|
+
const result = [];
|
|
191
|
+
const visit = (module) => {
|
|
192
|
+
const moduleRef = module;
|
|
193
|
+
if (seen.has(moduleRef)) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
seen.add(moduleRef);
|
|
197
|
+
// We'll collect deps during setup via the builder's import()
|
|
198
|
+
result.push(module);
|
|
199
|
+
};
|
|
200
|
+
for (const module of modules) {
|
|
201
|
+
visit(module);
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
async loadAsync(...modules) {
|
|
206
|
+
this._assertNotDisposed();
|
|
207
|
+
for (const module of modules) {
|
|
208
|
+
await this._loadOneModuleAsync(module);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async _loadOneModuleAsync(module) {
|
|
212
|
+
const moduleRef = module;
|
|
213
|
+
const existing = this._moduleRefs.get(moduleRef);
|
|
214
|
+
if (existing !== undefined) {
|
|
215
|
+
this._moduleRefs.set(moduleRef, existing + 1);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
this._moduleRefs.set(moduleRef, 1);
|
|
219
|
+
if (isSyncModule(module)) {
|
|
220
|
+
const builder = this._createModuleBuilder(moduleRef);
|
|
221
|
+
module._setup(builder);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
const importPromises = [];
|
|
225
|
+
const builder = this._createAsyncModuleBuilder(moduleRef, importPromises);
|
|
226
|
+
await module._setup(builder);
|
|
227
|
+
// Await nested async imports triggered inside _setup
|
|
228
|
+
if (importPromises.length > 0) {
|
|
229
|
+
await Promise.all(importPromises);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
_createModuleBuilder(moduleRef) {
|
|
234
|
+
return {
|
|
235
|
+
bind: (token) => this._createBindToBuilder(token, moduleRef),
|
|
236
|
+
import: (...modules) => {
|
|
237
|
+
this._loadSyncModules(modules);
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
_createAsyncModuleBuilder(moduleRef, importPromises) {
|
|
242
|
+
return {
|
|
243
|
+
bind: (token) => this._createBindToBuilder(token, moduleRef),
|
|
244
|
+
import: (...modules) => {
|
|
245
|
+
for (const module of modules) {
|
|
246
|
+
importPromises.push(this._loadOneModuleAsync(module));
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
unload(...modules) {
|
|
252
|
+
this._assertNotDisposed();
|
|
253
|
+
for (const module of modules) {
|
|
254
|
+
this._unloadModuleSync(module);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/** Unregister module bindings and collect [binding, instance] pairs for deactivation. */
|
|
258
|
+
_removeModuleBindings(ref) {
|
|
259
|
+
this._moduleRefs.delete(ref);
|
|
260
|
+
const ids = this._moduleBindingIds.get(ref) ?? [];
|
|
261
|
+
this._moduleBindingIds.delete(ref);
|
|
262
|
+
const pairs = [];
|
|
263
|
+
for (const id of ids) {
|
|
264
|
+
const binding = this._registry.getById(id);
|
|
265
|
+
if (binding !== undefined) {
|
|
266
|
+
this._registry.removeById(id);
|
|
267
|
+
if (this._scope.hasSingleton(id)) {
|
|
268
|
+
pairs.push([binding, this._scope.getSingleton(id)]);
|
|
269
|
+
this._scope.deleteSingleton(id);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return pairs;
|
|
274
|
+
}
|
|
275
|
+
_unloadModuleSync(ref) {
|
|
276
|
+
const count = this._moduleRefs.get(ref) ?? 0;
|
|
277
|
+
if (count <= 1) {
|
|
278
|
+
const reader = this._getMetadataReader();
|
|
279
|
+
for (const [binding, instance] of this._removeModuleBindings(ref)) {
|
|
280
|
+
this._lifecycle.runDeactivationSync(binding, instance, reader);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
this._moduleRefs.set(ref, count - 1);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async unloadAsync(...modules) {
|
|
288
|
+
this._assertNotDisposed();
|
|
289
|
+
for (const module of modules) {
|
|
290
|
+
await this._unloadModuleAsync(module);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async _unloadModuleAsync(ref) {
|
|
294
|
+
const count = this._moduleRefs.get(ref) ?? 0;
|
|
295
|
+
if (count <= 1) {
|
|
296
|
+
const reader = this._getMetadataReader();
|
|
297
|
+
for (const [binding, instance] of this._removeModuleBindings(ref)) {
|
|
298
|
+
await this._lifecycle.runDeactivation(binding, instance, reader);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
this._moduleRefs.set(ref, count - 1);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
loadAutoRegistered(registry) {
|
|
306
|
+
this._assertNotDisposed();
|
|
307
|
+
const entries = registry.entries();
|
|
308
|
+
for (const { target, scope } of entries) {
|
|
309
|
+
const builder = this._createBindToBuilder(target);
|
|
310
|
+
const bindingBuilder = builder.toSelf();
|
|
311
|
+
if (scope === "singleton") {
|
|
312
|
+
bindingBuilder.singleton();
|
|
313
|
+
}
|
|
314
|
+
else if (scope === "scoped") {
|
|
315
|
+
bindingBuilder.scoped();
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
bindingBuilder.transient();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return entries.length;
|
|
322
|
+
}
|
|
323
|
+
// ── Lifecycle hooks ────────────────────────────────────────────────────────
|
|
324
|
+
onActivation(token, handler) {
|
|
325
|
+
this._assertNotDisposed();
|
|
326
|
+
this._lifecycle.registerActivation(token, handler);
|
|
327
|
+
}
|
|
328
|
+
onDeactivation(token, handler) {
|
|
329
|
+
this._assertNotDisposed();
|
|
330
|
+
this._lifecycle.registerDeactivation(token, handler);
|
|
331
|
+
}
|
|
332
|
+
// ── Resolution ────────────────────────────────────────────────────────────
|
|
333
|
+
resolve(token, options) {
|
|
334
|
+
this._assertNotDisposed();
|
|
335
|
+
if (options === undefined) {
|
|
336
|
+
return this._resolver.resolveFromContext(token, [], []);
|
|
337
|
+
}
|
|
338
|
+
return this._resolver.resolve(token, options, [], []);
|
|
339
|
+
}
|
|
340
|
+
resolveAsync(token, options) {
|
|
341
|
+
this._assertNotDisposed();
|
|
342
|
+
if (options === undefined) {
|
|
343
|
+
return this._resolver.resolveAsyncFromContext(token, [], []);
|
|
344
|
+
}
|
|
345
|
+
return this._resolver.resolveAsync(token, options, [], []);
|
|
346
|
+
}
|
|
347
|
+
resolveOptional(token, options) {
|
|
348
|
+
this._assertNotDisposed();
|
|
349
|
+
return this._resolver.resolveOptional(token, options, [], []);
|
|
350
|
+
}
|
|
351
|
+
resolveOptionalAsync(token, options) {
|
|
352
|
+
this._assertNotDisposed();
|
|
353
|
+
return this._resolver.resolveOptionalAsync(token, options, [], []);
|
|
354
|
+
}
|
|
355
|
+
resolveAll(token, options) {
|
|
356
|
+
this._assertNotDisposed();
|
|
357
|
+
return this._resolver.resolveAll(token, options, [], []);
|
|
358
|
+
}
|
|
359
|
+
resolveAllAsync(token, options) {
|
|
360
|
+
this._assertNotDisposed();
|
|
361
|
+
return this._resolver.resolveAllAsync(token, options, [], []);
|
|
362
|
+
}
|
|
363
|
+
// ── Child ─────────────────────────────────────────────────────────────────
|
|
364
|
+
createChild() {
|
|
365
|
+
this._assertNotDisposed();
|
|
366
|
+
return new DefaultContainer(this);
|
|
367
|
+
}
|
|
368
|
+
// ── Dispose ───────────────────────────────────────────────────────────────
|
|
369
|
+
async dispose() {
|
|
370
|
+
if (this._disposed) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
this._disposed = true;
|
|
374
|
+
// Deactivate all singletons in this container (own only)
|
|
375
|
+
const reader = this._getMetadataReader();
|
|
376
|
+
for (const [id, instance] of this._scope.getAllSingletons()) {
|
|
377
|
+
const binding = this._registry.getById(id);
|
|
378
|
+
if (binding !== undefined) {
|
|
379
|
+
await this._lifecycle.runDeactivation(binding, instance, reader);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
this._scope.clearAll();
|
|
383
|
+
}
|
|
384
|
+
[Symbol.asyncDispose]() {
|
|
385
|
+
return this.dispose();
|
|
386
|
+
}
|
|
387
|
+
[Symbol.dispose]() {
|
|
388
|
+
throw new SyncDisposalNotSupportedError();
|
|
389
|
+
}
|
|
390
|
+
// ── Initialization ────────────────────────────────────────────────────────
|
|
391
|
+
async initializeAsync() {
|
|
392
|
+
this._assertNotDisposed();
|
|
393
|
+
const allBindings = this._registry.allBindings();
|
|
394
|
+
for (const binding of allBindings) {
|
|
395
|
+
if (binding.kind === "alias") {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const scope = effectiveBindingScope(binding);
|
|
399
|
+
if (scope === "singleton" && !this._scope.hasSingleton(binding.id)) {
|
|
400
|
+
if (binding.predicate !== undefined) {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
// Has activation — need to resolve
|
|
404
|
+
if (binding.kind === "constant" && binding.onActivation === undefined) {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const slotOptions = bindingSlotToResolveOptions(binding.slot);
|
|
408
|
+
await this.resolveAsync(binding.token, slotOptions);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// ── Validate ──────────────────────────────────────────────────────────────
|
|
413
|
+
validate() {
|
|
414
|
+
this._assertNotDisposed();
|
|
415
|
+
const reader = this._getMetadataReader();
|
|
416
|
+
const allBindings = this._registry.allBindings();
|
|
417
|
+
for (const binding of allBindings) {
|
|
418
|
+
if (!this._isSingletonStaticAnalyzableBinding(binding)) {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
this._validateSingletonBindingGraph(binding, reader);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
_isSingletonStaticAnalyzableBinding(binding) {
|
|
425
|
+
if (effectiveBindingScope(binding) !== "singleton") {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
return binding.kind === "class" || binding.kind === "resolved" || binding.kind === "resolved-async";
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* DFS over explicit constructor / `toResolved*` dependency edges. Follows `toAlias` chains to the
|
|
432
|
+
* terminal binding for scope checks (SPEC §6.9). Skips `toDynamic*` subtrees (opaque).
|
|
433
|
+
*/
|
|
434
|
+
_validateSingletonBindingGraph(root, reader) {
|
|
435
|
+
const rootName = tokenName(root.token);
|
|
436
|
+
const dfs = (current, pathNames, pathBindingIds) => {
|
|
437
|
+
if (pathBindingIds.has(current.id)) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const extendedPathIds = new Set(pathBindingIds);
|
|
441
|
+
extendedPathIds.add(current.id);
|
|
442
|
+
for (const edge of this._collectStaticDependencyEdges(current, reader)) {
|
|
443
|
+
const { terminal, depTokenName } = edge;
|
|
444
|
+
const depScope = this._validationScopeFromTerminal(terminal);
|
|
445
|
+
if (depScope === "opaque") {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (depScope === "scoped" || depScope === "transient") {
|
|
449
|
+
throw new ScopeViolationError({
|
|
450
|
+
consumerToken: rootName,
|
|
451
|
+
consumerScope: "singleton",
|
|
452
|
+
dependencyToken: depTokenName,
|
|
453
|
+
dependencyScope: depScope,
|
|
454
|
+
path: [...pathNames, depTokenName],
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
if (terminal.kind === "class" || terminal.kind === "resolved" || terminal.kind === "resolved-async") {
|
|
458
|
+
dfs(terminal, [...pathNames, depTokenName], extendedPathIds);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
dfs(root, [rootName], new Set());
|
|
463
|
+
}
|
|
464
|
+
_validationScopeFromTerminal(terminal) {
|
|
465
|
+
switch (terminal.kind) {
|
|
466
|
+
case "constant":
|
|
467
|
+
return "singleton";
|
|
468
|
+
case "dynamic":
|
|
469
|
+
case "dynamic-async":
|
|
470
|
+
return "opaque";
|
|
471
|
+
case "class":
|
|
472
|
+
case "resolved":
|
|
473
|
+
case "resolved-async":
|
|
474
|
+
return terminal.scope;
|
|
475
|
+
case "alias":
|
|
476
|
+
throw new InternalError("validate: expected terminal binding after alias resolution");
|
|
477
|
+
default: {
|
|
478
|
+
const exhaustive = terminal;
|
|
479
|
+
return exhaustive;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
_followAliasChainToTerminal(binding, options) {
|
|
484
|
+
const cyclePath = [];
|
|
485
|
+
const seenAliasIds = new Set();
|
|
486
|
+
let current = binding;
|
|
487
|
+
while (current !== undefined && current.kind === "alias") {
|
|
488
|
+
if (seenAliasIds.has(current.id)) {
|
|
489
|
+
throw new CircularDependencyError(cyclePath);
|
|
490
|
+
}
|
|
491
|
+
seenAliasIds.add(current.id);
|
|
492
|
+
cyclePath.push(tokenName(current.token));
|
|
493
|
+
const nextToken = current.target;
|
|
494
|
+
const next = this._resolver.peekBindingForValidate(nextToken, options);
|
|
495
|
+
if (next === undefined) {
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
current = next.binding;
|
|
499
|
+
}
|
|
500
|
+
return current;
|
|
501
|
+
}
|
|
502
|
+
_collectStaticDependencyEdges(binding, reader) {
|
|
503
|
+
const edges = [];
|
|
504
|
+
const pushTerminal = (terminal, displayName) => {
|
|
505
|
+
if (terminal === undefined) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
edges.push({ terminal, depTokenName: displayName });
|
|
509
|
+
};
|
|
510
|
+
if (binding.kind === "class") {
|
|
511
|
+
const meta = reader.getConstructorMetadata(binding.target);
|
|
512
|
+
if (meta === undefined) {
|
|
513
|
+
return edges;
|
|
514
|
+
}
|
|
515
|
+
for (const param of meta.params) {
|
|
516
|
+
const paramOptions = injectionSlotToResolveOptions(param);
|
|
517
|
+
if (param.optional) {
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
const tokenRef = param.token;
|
|
521
|
+
if (param.multi) {
|
|
522
|
+
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, paramOptions);
|
|
523
|
+
for (const cand of candidates) {
|
|
524
|
+
const term = this._followAliasChainToTerminal(cand, paramOptions);
|
|
525
|
+
pushTerminal(term, term !== undefined ? tokenName(term.token) : "");
|
|
526
|
+
}
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const found = paramOptions === undefined
|
|
530
|
+
? this._resolver.peekBindingForValidate(tokenRef, undefined)
|
|
531
|
+
: this._resolver.peekBindingForValidate(tokenRef, paramOptions);
|
|
532
|
+
if (found === undefined) {
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
const term = this._followAliasChainToTerminal(found.binding, paramOptions);
|
|
536
|
+
pushTerminal(term, term !== undefined ? tokenName(term.token) : "");
|
|
537
|
+
}
|
|
538
|
+
return edges;
|
|
539
|
+
}
|
|
540
|
+
if (binding.kind === "resolved" || binding.kind === "resolved-async") {
|
|
541
|
+
for (const dep of binding.deps) {
|
|
542
|
+
const depOptions = injectionSlotToResolveOptions(dep);
|
|
543
|
+
if (dep.optional) {
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
const tokenRef = dep.token;
|
|
547
|
+
if (dep.multi) {
|
|
548
|
+
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, depOptions);
|
|
549
|
+
for (const cand of candidates) {
|
|
550
|
+
const term = this._followAliasChainToTerminal(cand, depOptions);
|
|
551
|
+
pushTerminal(term, term !== undefined ? tokenName(term.token) : "");
|
|
552
|
+
}
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
const found = depOptions === undefined
|
|
556
|
+
? this._resolver.peekBindingForValidate(tokenRef, undefined)
|
|
557
|
+
: this._resolver.peekBindingForValidate(tokenRef, depOptions);
|
|
558
|
+
if (found === undefined) {
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const term = this._followAliasChainToTerminal(found.binding, depOptions);
|
|
562
|
+
pushTerminal(term, term !== undefined ? tokenName(term.token) : "");
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return edges;
|
|
566
|
+
}
|
|
567
|
+
// ── Introspection ─────────────────────────────────────────────────────────
|
|
568
|
+
has(token, options) {
|
|
569
|
+
this._assertNotDisposed();
|
|
570
|
+
return this._inspector.has(token, options, () => this._parent?.has(token, options) ?? false);
|
|
571
|
+
}
|
|
572
|
+
hasOwn(token, options) {
|
|
573
|
+
this._assertNotDisposed();
|
|
574
|
+
return this._inspector.hasOwn(token, options);
|
|
575
|
+
}
|
|
576
|
+
lookupBindings(token) {
|
|
577
|
+
this._assertNotDisposed();
|
|
578
|
+
return this._inspector.lookupBindings(token);
|
|
579
|
+
}
|
|
580
|
+
inspect() {
|
|
581
|
+
this._assertNotDisposed();
|
|
582
|
+
return this._inspector.inspect();
|
|
583
|
+
}
|
|
584
|
+
generateDependencyGraph(options) {
|
|
585
|
+
this._assertNotDisposed();
|
|
586
|
+
return buildDependencyGraph(this._registry, this._getMetadataReader(), options, this._parent?._registry);
|
|
587
|
+
}
|
|
588
|
+
// ── Internal ──────────────────────────────────────────────────────────────
|
|
589
|
+
_assertNotDisposed() {
|
|
590
|
+
if (this._disposed) {
|
|
591
|
+
throw new DisposedContainerError();
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function updateSlotTag(slot, tag, value) {
|
|
596
|
+
const existing = [...slot.tags];
|
|
597
|
+
const idx = existing.findIndex(([k]) => k === tag);
|
|
598
|
+
if (idx !== -1) {
|
|
599
|
+
existing[idx] = [tag, value];
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
existing.push([tag, value]);
|
|
603
|
+
}
|
|
604
|
+
return { ...slot, tags: existing };
|
|
605
|
+
}
|
|
606
|
+
// ── SlotBuilder ───────────────────────────────────────────────────────────────
|
|
607
|
+
class SlotBuilder {
|
|
608
|
+
_slot = DEFAULT_BINDING_SLOT; // ✓ T3-1: no redundant spread
|
|
609
|
+
_predicate;
|
|
610
|
+
_committed;
|
|
611
|
+
when(predicate) {
|
|
612
|
+
this._predicate = predicate;
|
|
613
|
+
this._doCommit();
|
|
614
|
+
return this;
|
|
615
|
+
}
|
|
616
|
+
whenNamed(name) {
|
|
617
|
+
this._slot = { ...this._slot, name };
|
|
618
|
+
this._doCommit();
|
|
619
|
+
return this;
|
|
620
|
+
}
|
|
621
|
+
whenTagged(tag, value) {
|
|
622
|
+
this._slot = updateSlotTag(this._slot, tag, value);
|
|
623
|
+
this._doCommit();
|
|
624
|
+
return this;
|
|
625
|
+
}
|
|
626
|
+
whenDefault() {
|
|
627
|
+
return this;
|
|
628
|
+
}
|
|
629
|
+
id() {
|
|
630
|
+
return this._committed;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
// ── BindingEntry ──────────────────────────────────────────────────────────────
|
|
634
|
+
class BindingEntry {
|
|
635
|
+
_token;
|
|
636
|
+
_commit;
|
|
637
|
+
constructor(_token, _commit) {
|
|
638
|
+
this._token = _token;
|
|
639
|
+
this._commit = _commit;
|
|
640
|
+
}
|
|
641
|
+
to(type) {
|
|
642
|
+
return new ConstraintBuilder(this._token, { kind: "class", target: type, scope: "transient" }, this._commit);
|
|
643
|
+
}
|
|
644
|
+
toSelf() {
|
|
645
|
+
if (typeof this._token !== "function") {
|
|
646
|
+
throw new Error("toSelf() requires token to be a Constructor");
|
|
647
|
+
}
|
|
648
|
+
return new ConstraintBuilder(this._token, { kind: "class", target: this._token, scope: "transient" }, this._commit);
|
|
649
|
+
}
|
|
650
|
+
toConstantValue(value) {
|
|
651
|
+
return new ConstantBuilder(this._token, value, this._commit);
|
|
652
|
+
}
|
|
653
|
+
toDynamic(factory) {
|
|
654
|
+
return new ConstraintBuilder(this._token, { kind: "dynamic", factory, scope: "transient" }, this._commit);
|
|
655
|
+
}
|
|
656
|
+
toDynamicAsync(factory) {
|
|
657
|
+
return new ConstraintBuilder(this._token, { kind: "dynamic-async", factory, scope: "transient" }, this._commit);
|
|
658
|
+
}
|
|
659
|
+
toResolved(factory, deps) {
|
|
660
|
+
const normalizedDeps = deps.map((dependency) => normalizeToDescriptor(dependency));
|
|
661
|
+
return new ConstraintBuilder(this._token, {
|
|
662
|
+
kind: "resolved",
|
|
663
|
+
factory: factory,
|
|
664
|
+
deps: normalizedDeps,
|
|
665
|
+
scope: "transient",
|
|
666
|
+
}, this._commit);
|
|
667
|
+
}
|
|
668
|
+
toResolvedAsync(factory, deps) {
|
|
669
|
+
const normalizedDeps = deps.map((dependency) => normalizeToDescriptor(dependency));
|
|
670
|
+
return new ConstraintBuilder(this._token, {
|
|
671
|
+
kind: "resolved-async",
|
|
672
|
+
factory: factory,
|
|
673
|
+
deps: normalizedDeps,
|
|
674
|
+
scope: "transient",
|
|
675
|
+
}, this._commit);
|
|
676
|
+
}
|
|
677
|
+
toAlias(target) {
|
|
678
|
+
return new AliasBuilder(this._token, target, this._commit);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
// ── ConstraintBuilder ─────────────────────────────────────────────────────────
|
|
682
|
+
class ConstraintBuilder extends SlotBuilder {
|
|
683
|
+
_token;
|
|
684
|
+
_partial;
|
|
685
|
+
_commit;
|
|
686
|
+
constructor(_token, _partial, _commit) {
|
|
687
|
+
super();
|
|
688
|
+
this._token = _token;
|
|
689
|
+
this._partial = _partial;
|
|
690
|
+
this._commit = _commit;
|
|
691
|
+
this._doCommit();
|
|
692
|
+
}
|
|
693
|
+
_doCommit() {
|
|
694
|
+
const previousId = this._committed;
|
|
695
|
+
const binding = {
|
|
696
|
+
...this._partial,
|
|
697
|
+
id: generateBindingId(),
|
|
698
|
+
token: this._token,
|
|
699
|
+
slot: this._slot,
|
|
700
|
+
predicate: this._predicate,
|
|
701
|
+
};
|
|
702
|
+
this._committed = this._commit(binding, previousId);
|
|
703
|
+
}
|
|
704
|
+
singleton() {
|
|
705
|
+
const previousId = this._committed;
|
|
706
|
+
this._committed = undefined;
|
|
707
|
+
return new ScopeBuilder(this._token, { ...this._partial, scope: "singleton" }, this._slot, this._predicate, this._commit, "singleton", previousId);
|
|
708
|
+
}
|
|
709
|
+
transient() {
|
|
710
|
+
const previousId = this._committed;
|
|
711
|
+
this._committed = undefined;
|
|
712
|
+
return new ScopeBuilder(this._token, { ...this._partial, scope: "transient" }, this._slot, this._predicate, this._commit, "transient", previousId);
|
|
713
|
+
}
|
|
714
|
+
scoped() {
|
|
715
|
+
const previousId = this._committed;
|
|
716
|
+
this._committed = undefined;
|
|
717
|
+
return new ScopeBuilder(this._token, { ...this._partial, scope: "scoped" }, this._slot, this._predicate, this._commit, "scoped", previousId);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
// ── ScopeBuilder ─────────────────────────────────────────────────────────────
|
|
721
|
+
class ScopeBuilder {
|
|
722
|
+
_token;
|
|
723
|
+
_partial;
|
|
724
|
+
_slot;
|
|
725
|
+
_predicate;
|
|
726
|
+
_commit;
|
|
727
|
+
_scope;
|
|
728
|
+
_initialPreviousId;
|
|
729
|
+
_onActivation;
|
|
730
|
+
_onDeactivation;
|
|
731
|
+
_committed;
|
|
732
|
+
constructor(_token, _partial, _slot, _predicate, _commit, _scope, _initialPreviousId) {
|
|
733
|
+
this._token = _token;
|
|
734
|
+
this._partial = _partial;
|
|
735
|
+
this._slot = _slot;
|
|
736
|
+
this._predicate = _predicate;
|
|
737
|
+
this._commit = _commit;
|
|
738
|
+
this._scope = _scope;
|
|
739
|
+
this._initialPreviousId = _initialPreviousId;
|
|
740
|
+
this._doCommit();
|
|
741
|
+
}
|
|
742
|
+
_doCommit() {
|
|
743
|
+
const previousId = this._committed ?? this._initialPreviousId;
|
|
744
|
+
const binding = {
|
|
745
|
+
...this._partial,
|
|
746
|
+
id: generateBindingId(),
|
|
747
|
+
token: this._token,
|
|
748
|
+
slot: this._slot,
|
|
749
|
+
predicate: this._predicate,
|
|
750
|
+
onActivation: this._onActivation,
|
|
751
|
+
onDeactivation: this._onDeactivation,
|
|
752
|
+
};
|
|
753
|
+
this._committed = this._commit(binding, previousId);
|
|
754
|
+
}
|
|
755
|
+
onActivation(fn) {
|
|
756
|
+
this._onActivation = fn;
|
|
757
|
+
this._doCommit();
|
|
758
|
+
return this;
|
|
759
|
+
}
|
|
760
|
+
onDeactivation(fn) {
|
|
761
|
+
this._onDeactivation = fn;
|
|
762
|
+
this._doCommit();
|
|
763
|
+
return this;
|
|
764
|
+
}
|
|
765
|
+
id() {
|
|
766
|
+
return this._committed;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
// ── ConstantBuilder ───────────────────────────────────────────────────────────
|
|
770
|
+
class ConstantBuilder extends SlotBuilder {
|
|
771
|
+
_token;
|
|
772
|
+
_value;
|
|
773
|
+
_commit;
|
|
774
|
+
_onActivation;
|
|
775
|
+
_onDeactivation;
|
|
776
|
+
constructor(_token, _value, _commit) {
|
|
777
|
+
super();
|
|
778
|
+
this._token = _token;
|
|
779
|
+
this._value = _value;
|
|
780
|
+
this._commit = _commit;
|
|
781
|
+
this._doCommit();
|
|
782
|
+
}
|
|
783
|
+
_doCommit() {
|
|
784
|
+
const previousId = this._committed;
|
|
785
|
+
const binding = {
|
|
786
|
+
kind: "constant",
|
|
787
|
+
id: generateBindingId(),
|
|
788
|
+
token: this._token,
|
|
789
|
+
slot: this._slot,
|
|
790
|
+
predicate: this._predicate,
|
|
791
|
+
value: this._value,
|
|
792
|
+
scope: "singleton",
|
|
793
|
+
onActivation: this._onActivation,
|
|
794
|
+
onDeactivation: this._onDeactivation,
|
|
795
|
+
};
|
|
796
|
+
this._committed = this._commit(binding, previousId);
|
|
797
|
+
}
|
|
798
|
+
onActivation(fn) {
|
|
799
|
+
this._onActivation = fn;
|
|
800
|
+
this._doCommit();
|
|
801
|
+
return this;
|
|
802
|
+
}
|
|
803
|
+
onDeactivation(fn) {
|
|
804
|
+
this._onDeactivation = fn;
|
|
805
|
+
this._doCommit();
|
|
806
|
+
return this;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
// ── AliasBuilder ──────────────────────────────────────────────────────────────
|
|
810
|
+
class AliasBuilder extends SlotBuilder {
|
|
811
|
+
_token;
|
|
812
|
+
_target;
|
|
813
|
+
_commit;
|
|
814
|
+
constructor(_token, _target, _commit) {
|
|
815
|
+
super();
|
|
816
|
+
this._token = _token;
|
|
817
|
+
this._target = _target;
|
|
818
|
+
this._commit = _commit;
|
|
819
|
+
this._doCommit();
|
|
820
|
+
}
|
|
821
|
+
_doCommit() {
|
|
822
|
+
const previousId = this._committed;
|
|
823
|
+
const binding = {
|
|
824
|
+
kind: "alias",
|
|
825
|
+
id: generateBindingId(),
|
|
826
|
+
token: this._token,
|
|
827
|
+
slot: this._slot,
|
|
828
|
+
predicate: this._predicate,
|
|
829
|
+
target: this._target,
|
|
830
|
+
};
|
|
831
|
+
this._committed = this._commit(binding, previousId);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
// ── Container static ──────────────────────────────────────────────────────────
|
|
835
|
+
/**
|
|
836
|
+
* @since 0.3.16-canary.0
|
|
837
|
+
*/
|
|
838
|
+
export const Container = {
|
|
839
|
+
create() {
|
|
840
|
+
return new DefaultContainer();
|
|
841
|
+
},
|
|
842
|
+
fromModules(...modules) {
|
|
843
|
+
const container = new DefaultContainer();
|
|
844
|
+
container.load(...modules);
|
|
845
|
+
return container;
|
|
846
|
+
},
|
|
847
|
+
async fromModulesAsync(...modules) {
|
|
848
|
+
const container = new DefaultContainer();
|
|
849
|
+
await container.loadAsync(...modules);
|
|
850
|
+
return container;
|
|
851
|
+
},
|
|
852
|
+
};
|
|
853
|
+
//# sourceMappingURL=container.js.map
|