@codefast/di 0.3.14-canary.1 → 0.3.14

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +42 -26
  3. package/dist/binding-scope.d.mts +11 -0
  4. package/dist/binding-scope.mjs +19 -0
  5. package/dist/binding-select.d.mts +8 -26
  6. package/dist/binding-select.mjs +56 -49
  7. package/dist/binding.d.mts +107 -335
  8. package/dist/binding.mjs +19 -332
  9. package/dist/constraints.d.mts +11 -29
  10. package/dist/constraints.mjs +32 -36
  11. package/dist/constructor-type.d.mts +17 -0
  12. package/dist/constructor-type.mjs +1 -0
  13. package/dist/container.d.mts +43 -122
  14. package/dist/container.mjs +667 -482
  15. package/dist/decorators/inject.d.mts +19 -50
  16. package/dist/decorators/inject.mjs +131 -93
  17. package/dist/decorators/injectable.d.mts +16 -37
  18. package/dist/decorators/injectable.mjs +47 -66
  19. package/dist/decorators/lifecycle-decorators.d.mts +2 -22
  20. package/dist/decorators/lifecycle-decorators.mjs +81 -37
  21. package/dist/dependency-graph.d.mts +24 -54
  22. package/dist/dependency-graph.mjs +51 -153
  23. package/dist/environment.d.mts +38 -12
  24. package/dist/environment.mjs +82 -16
  25. package/dist/errors.d.mts +70 -189
  26. package/dist/errors.mjs +92 -219
  27. package/dist/graph-adapters/cytoscape.d.mts +21 -7
  28. package/dist/graph-adapters/cytoscape.mjs +18 -35
  29. package/dist/graph-adapters/dot.d.mts +1 -4
  30. package/dist/graph-adapters/dot.mjs +6 -86
  31. package/dist/graph-adapters/reactflow.d.mts +26 -7
  32. package/dist/graph-adapters/reactflow.mjs +21 -72
  33. package/dist/graph-adapters/types.d.mts +2 -91
  34. package/dist/index.d.mts +16 -8
  35. package/dist/index.mjs +8 -5
  36. package/dist/inspector.d.mts +35 -74
  37. package/dist/inspector.mjs +61 -88
  38. package/dist/lifecycle.d.mts +20 -53
  39. package/dist/lifecycle.mjs +129 -99
  40. package/dist/metadata/metadata-keys.d.mts +9 -26
  41. package/dist/metadata/metadata-keys.mjs +7 -28
  42. package/dist/metadata/metadata-reader-token.d.mts +7 -0
  43. package/dist/metadata/metadata-reader-token.mjs +5 -0
  44. package/dist/metadata/metadata-types.d.mts +26 -75
  45. package/dist/metadata/symbol-metadata-reader.d.mts +10 -26
  46. package/dist/metadata/symbol-metadata-reader.mjs +32 -45
  47. package/dist/module.d.mts +30 -96
  48. package/dist/module.mjs +26 -72
  49. package/dist/registry.d.mts +32 -63
  50. package/dist/registry.mjs +131 -82
  51. package/dist/resolve-options.d.mts +18 -0
  52. package/dist/resolve-options.mjs +22 -0
  53. package/dist/resolver.d.mts +67 -190
  54. package/dist/resolver.mjs +715 -424
  55. package/dist/scope.d.mts +19 -106
  56. package/dist/scope.mjs +37 -196
  57. package/dist/token.d.mts +8 -22
  58. package/dist/token.mjs +9 -11
  59. package/dist/types.d.mts +48 -0
  60. package/dist/types.mjs +1 -0
  61. package/package.json +36 -14
  62. package/dist/metadata/param-registry.d.mts +0 -16
  63. package/dist/metadata/param-registry.mjs +0 -31
  64. package/dist/scope-validation.d.mts +0 -21
  65. package/dist/scope-validation.mjs +0 -35
@@ -1,540 +1,725 @@
1
- import { AsyncModuleLoadError, CircularDependencyError, InternalError } from "./errors.mjs";
2
- import { BindingBuilder } from "./binding.mjs";
3
- import { getAutoRegistered } from "./decorators/injectable.mjs";
4
- import { SymbolMetadataReader } from "./metadata/symbol-metadata-reader.mjs";
5
- import { ContainerInspector } from "./inspector.mjs";
6
- import { AsyncModule } from "./module.mjs";
1
+ import { effectiveBindingScope } from "./binding-scope.mjs";
2
+ import { DisposedContainerError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError } from "./errors.mjs";
3
+ import { DEFAULT_SLOT, generateBindingId } from "./binding.mjs";
4
+ import { tokenName } from "./token.mjs";
7
5
  import { BindingRegistry } from "./registry.mjs";
8
- import { DependencyResolver } from "./resolver.mjs";
9
- import { validateScopeRules } from "./scope-validation.mjs";
10
6
  import { ScopeManager } from "./scope.mjs";
11
- import { isDevelopmentOrTestEnvironment } from "./environment.mjs";
7
+ import { LifecycleManager } from "./lifecycle.mjs";
8
+ import { slotKeyToResolveOptions } from "./resolve-options.mjs";
9
+ import { DependencyResolver } from "./resolver.mjs";
10
+ import { Inspector } from "./inspector.mjs";
11
+ import { buildDependencyGraph } from "./dependency-graph.mjs";
12
+ import { defaultMetadataReader } from "./metadata/symbol-metadata-reader.mjs";
13
+ import { MetadataReaderToken } from "./metadata/metadata-reader-token.mjs";
14
+ import { normalizeToDescriptor } from "./decorators/inject.mjs";
15
+ import { isSyncModule } from "./module.mjs";
12
16
  //#region src/container.ts
13
- /**
14
- * Derives a {@link ResolveHint} from a binding's name or first tag.
15
- * Used by {@link DefaultContainer.initializeAsync} to re-resolve named/tagged singletons
16
- * through the standard resolution path.
17
- */
18
- function resolveHintForBinding(binding) {
19
- if (binding.bindingName !== void 0) return { name: binding.bindingName };
20
- for (const [tagKey, tagValue] of binding.tags) return { tag: [tagKey, tagValue] };
21
- }
22
- /**
23
- * Module {@link ModuleBuilder.bind}: append when the binding is disambiguated **at first
24
- * registration** (`whenNamed` / `whenTagged` / `when` before `to*()`). Otherwise replace
25
- * all bindings for the token (last-wins). Chaining `.whenNamed()` after `.to*()` only updates
26
- * in place and does not enable multi-binding for subsequent module lines — use hint-before-`to*()`
27
- * in modules (same style as `container.bind(...).whenNamed("x").to*(...)` in the package README).
28
- */
29
- function moduleBindingUsesMultiSlot(built) {
30
- return built.bindingName !== void 0 || built.tags.size > 0 || built.constraint !== void 0;
31
- }
32
- /**
33
- * Default IoC container: registry + scoped caches + synchronous / asynchronous resolution.
34
- * @internal Implementation of {@link Container}; not part of the public package contract.
35
- */
36
17
  var DefaultContainer = class DefaultContainer {
37
- /**
38
- * Stack guard for detecting circular sync module imports during {@link ensureSyncModuleLoaded}.
39
- */
40
- syncModuleStack = [];
41
- /**
42
- * Stack guard for detecting circular async module imports during {@link ensureAsyncModuleLoaded}.
43
- */
44
- asyncModuleStack = [];
45
- /**
46
- * Tracks loaded modules → their binding IDs so {@link unload} / {@link unloadAsync} can
47
- * remove exactly the bindings contributed by each module.
48
- * Also serves as a deduplication set: a module present as a key is considered loaded.
49
- */
50
- loadedModules = /* @__PURE__ */ new Map();
51
- /**
52
- * True after the first dev/test one-shot scope validation has run for the current registry state.
53
- * Reset to `false` by {@link invalidateDevValidationState} on every registry mutation.
54
- */
55
- devValidationRan = false;
56
- /**
57
- * Internal constructor for root/child instances.
58
- * Use {@link Container.create}, {@link Container.fromModules}, or {@link createChild}.
59
- */
60
- constructor(ownRegistry, ownScopeManager, parent, resolver, metadataReader) {
61
- this.ownRegistry = ownRegistry;
62
- this.ownScopeManager = ownScopeManager;
63
- this.parent = parent;
64
- this.resolver = resolver;
65
- this.metadataReader = metadataReader;
66
- }
67
- /**
68
- * Creates a root container with an empty registry and fresh singleton/scoped caches.
69
- * Wires the circular container↔resolver reference via a mutable {@link ContainerRef} holder.
70
- */
71
- static create() {
72
- const ownRegistry = new BindingRegistry();
73
- const ownScopeManager = ScopeManager.createRoot();
74
- const metadataReader = new SymbolMetadataReader();
75
- const holder = { current: void 0 };
76
- const container = new DefaultContainer(ownRegistry, ownScopeManager, void 0, new DependencyResolver({
77
- lookup: (token) => {
78
- const current = holder.current;
79
- if (current === void 0) throw new InternalError("container is not initialized");
80
- return current.lookupBindings(token);
81
- },
82
- scopeManager: ownScopeManager,
83
- metadataReader
84
- }), metadataReader);
85
- holder.current = container;
86
- return container;
18
+ _disposed = false;
19
+ _registry;
20
+ _scope;
21
+ _lifecycle;
22
+ _resolver;
23
+ _inspector;
24
+ _parent;
25
+ _moduleRefs = /* @__PURE__ */ new Map();
26
+ _moduleBindingIds = /* @__PURE__ */ new Map();
27
+ constructor(parent) {
28
+ this._parent = parent;
29
+ this._registry = new BindingRegistry();
30
+ this._scope = new ScopeManager(parent !== void 0);
31
+ this._lifecycle = new LifecycleManager();
32
+ this._inspector = new Inspector(this._registry, this._scope, parent !== void 0, () => this._disposed);
33
+ this._initResolver();
34
+ }
35
+ _initResolver() {
36
+ const metadataReader = this._getMetadataReader();
37
+ const parentResolver = this._parent?._resolver;
38
+ this._resolver = new DependencyResolver(this._registry, this._scope, this._lifecycle, metadataReader, this, parentResolver);
39
+ }
40
+ _getMetadataReader() {
41
+ if (this._registry.getAll(MetadataReaderToken).length > 0) try {
42
+ return this._resolver.resolve(MetadataReaderToken, void 0, [], []);
43
+ } catch {}
44
+ if (this._parent !== void 0) return this._parent._getMetadataReader();
45
+ return defaultMetadataReader;
46
+ }
47
+ get isDisposed() {
48
+ return this._disposed;
87
49
  }
88
- /**
89
- * Starts a fluent {@link BindingBuilder} for the given token or constructor.
90
- * The binding is registered into this container's registry immediately when a
91
- * `to*()` strategy method is called on the returned builder.
92
- */
93
50
  bind(token) {
94
- return new BindingBuilder(token, void 0, {
95
- register: (built) => {
96
- this.invalidateDevValidationState();
97
- this.ownRegistry.add(token, built);
98
- },
99
- update: (built) => {
100
- this.invalidateDevValidationState();
101
- this.ownRegistry.replaceById(built.id, built);
51
+ this._assertNotDisposed();
52
+ return this._createBindToBuilder(token);
53
+ }
54
+ _createBindToBuilder(token, moduleRef) {
55
+ const registry = this._registry;
56
+ const commitBinding = (b, previousId) => {
57
+ if (previousId !== void 0) {
58
+ registry.removeById(previousId);
59
+ if (moduleRef !== void 0) {
60
+ const ids = this._moduleBindingIds.get(moduleRef);
61
+ if (ids !== void 0) {
62
+ const idx = ids.indexOf(previousId);
63
+ if (idx !== -1) ids.splice(idx, 1);
64
+ }
65
+ }
102
66
  }
103
- });
104
- }
105
- /**
106
- * Fast registry presence check without instantiation.
107
- *
108
- * When `hint` is provided, this only verifies that at least one binding matches
109
- * the name/tag discriminator; it does not evaluate runtime `when()` predicates.
110
- */
111
- has(token, hint) {
112
- const list = this.lookupBindings(token);
113
- if (list === void 0 || list.length === 0) return false;
114
- if (hint === void 0) return true;
115
- return list.some((binding) => {
116
- if (hint.name !== void 0 && binding.bindingName !== hint.name) return false;
117
- if (hint.tag !== void 0) {
118
- const tag = hint.tag;
119
- if (binding.tags.get(tag[0]) !== tag[1]) return false;
67
+ registry.add(b);
68
+ if (moduleRef !== void 0) {
69
+ let ids = this._moduleBindingIds.get(moduleRef);
70
+ if (ids === void 0) {
71
+ ids = [];
72
+ this._moduleBindingIds.set(moduleRef, ids);
73
+ }
74
+ ids.push(b.id);
120
75
  }
121
- return true;
122
- });
123
- }
124
- /**
125
- * Removes bindings by token or by binding id and synchronously releases cached instances.
126
- *
127
- * - `binding id` path removes one binding and its cache entry.
128
- * - `token` path removes all owned bindings for that key at once.
129
- */
76
+ return b.id;
77
+ };
78
+ return new BindToBuilderImpl(token, commitBinding);
79
+ }
130
80
  unbind(tokenOrId) {
131
- this.invalidateDevValidationState();
132
- if (typeof tokenOrId === "string") {
133
- this.ownScopeManager.releaseByBindingId(tokenOrId);
134
- this.ownRegistry.removeById(tokenOrId);
135
- return;
81
+ this._assertNotDisposed();
82
+ this._unbindSync(tokenOrId);
83
+ }
84
+ _unbindSync(tokenOrId) {
85
+ const bindings = this._getBindingsForTokenOrId(tokenOrId);
86
+ for (const binding of bindings) {
87
+ this._registry.removeById(binding.id);
88
+ if (this._scope.hasSingleton(binding.id)) {
89
+ const instance = this._scope.getSingleton(binding.id);
90
+ this._scope.deleteSingleton(binding.id);
91
+ this._lifecycle.runDeactivationSync(binding, instance, this._getMetadataReader());
92
+ }
136
93
  }
137
- const owned = this.ownRegistry.get(tokenOrId);
138
- if (owned !== void 0) for (const binding of owned) this.ownScopeManager.releaseBinding(binding);
139
- this.ownRegistry.remove(tokenOrId);
140
94
  }
141
- /**
142
- * Async counterpart of {@link unbind}; awaits deactivation hooks before registry removal.
143
- */
144
95
  async unbindAsync(tokenOrId) {
145
- this.invalidateDevValidationState();
146
- if (typeof tokenOrId === "string") {
147
- await this.ownScopeManager.releaseByBindingIdAsync(tokenOrId);
148
- this.ownRegistry.removeById(tokenOrId);
149
- return;
96
+ this._assertNotDisposed();
97
+ const bindings = this._getBindingsForTokenOrId(tokenOrId);
98
+ for (const binding of bindings) {
99
+ this._registry.removeById(binding.id);
100
+ if (this._scope.hasSingleton(binding.id)) {
101
+ const instance = this._scope.getSingleton(binding.id);
102
+ this._scope.deleteSingleton(binding.id);
103
+ await this._lifecycle.runDeactivation(binding, instance, this._getMetadataReader());
104
+ }
150
105
  }
151
- const owned = this.ownRegistry.get(tokenOrId);
152
- if (owned !== void 0) for (const binding of owned) await this.ownScopeManager.releaseBindingAsync(binding);
153
- this.ownRegistry.remove(tokenOrId);
154
- }
155
- /**
156
- * Replaces all owned bindings for `token` and returns a fresh builder.
157
- *
158
- * Existing cached instances for the removed bindings are synchronously released first.
159
- */
106
+ }
107
+ unbindAll() {
108
+ this._assertNotDisposed();
109
+ const all = this._registry.clear();
110
+ for (const binding of all) if (this._scope.hasSingleton(binding.id)) {
111
+ const instance = this._scope.getSingleton(binding.id);
112
+ this._scope.deleteSingleton(binding.id);
113
+ this._lifecycle.runDeactivationSync(binding, instance, this._getMetadataReader());
114
+ }
115
+ }
116
+ async unbindAllAsync() {
117
+ this._assertNotDisposed();
118
+ const all = this._registry.clear();
119
+ for (const binding of all) if (this._scope.hasSingleton(binding.id)) {
120
+ const instance = this._scope.getSingleton(binding.id);
121
+ this._scope.deleteSingleton(binding.id);
122
+ await this._lifecycle.runDeactivation(binding, instance, this._getMetadataReader());
123
+ }
124
+ }
160
125
  rebind(token) {
161
- this.invalidateDevValidationState();
162
- const owned = this.ownRegistry.get(token);
163
- if (owned !== void 0) for (const binding of owned) this.ownScopeManager.releaseBinding(binding);
164
- this.ownRegistry.remove(token);
165
- return this.bind(token);
166
- }
167
- /**
168
- * Registers bindings from synchronous modules. Re-loading a module already present on this
169
- * container is a no-op (deduplication, spec §7.3 Phase 3). When {@link isDevelopmentOrTestEnvironment}
170
- * is true, runs {@link validate} at most once after the registry changes.
171
- */
126
+ this._assertNotDisposed();
127
+ if (!this._registry.has(token)) throw new RebindUnboundTokenError(tokenName(token));
128
+ this._unbindSync(token);
129
+ return this._createBindToBuilder(token);
130
+ }
131
+ _getBindingsForTokenOrId(tokenOrId) {
132
+ if (typeof tokenOrId === "string") {
133
+ const b = this._registry.getById(tokenOrId);
134
+ return b !== void 0 ? [b] : [];
135
+ }
136
+ return [...this._registry.getAll(tokenOrId)];
137
+ }
172
138
  load(...modules) {
173
- this.invalidateDevValidationState();
174
- for (const syncModule of modules) {
175
- if (syncModule instanceof AsyncModule) throw new AsyncModuleLoadError(syncModule.name);
176
- this.ensureSyncModuleLoaded(syncModule);
139
+ this._assertNotDisposed();
140
+ this._loadSyncModules(modules);
141
+ }
142
+ _loadSyncModules(modules) {
143
+ const toLoad = this._collectModuleDeps(modules);
144
+ for (const mod of toLoad) {
145
+ const ref = mod;
146
+ const existing = this._moduleRefs.get(ref);
147
+ if (existing !== void 0) {
148
+ this._moduleRefs.set(ref, existing + 1);
149
+ continue;
150
+ }
151
+ this._moduleRefs.set(ref, 1);
152
+ const builder = this._createModuleBuilder(ref);
153
+ mod._setup(builder);
177
154
  }
178
- this.maybeRunDevValidationOnce();
179
155
  }
180
- /**
181
- * Like {@link load} but allows async modules. Re-loading is deduplicated the same way.
182
- */
156
+ _collectModuleDeps(modules) {
157
+ const seen = /* @__PURE__ */ new Set();
158
+ const result = [];
159
+ const visit = (mod) => {
160
+ const ref = mod;
161
+ if (seen.has(ref)) return;
162
+ seen.add(ref);
163
+ result.push(mod);
164
+ };
165
+ for (const m of modules) visit(m);
166
+ return result;
167
+ }
183
168
  async loadAsync(...modules) {
184
- this.invalidateDevValidationState();
185
- for (const moduleOrAsync of modules) if (moduleOrAsync instanceof AsyncModule) await this.ensureAsyncModuleLoaded(moduleOrAsync);
186
- else this.ensureSyncModuleLoaded(moduleOrAsync);
187
- this.maybeRunDevValidationOnce();
169
+ this._assertNotDisposed();
170
+ for (const mod of modules) await this._loadOneModuleAsync(mod);
171
+ }
172
+ async _loadOneModuleAsync(mod) {
173
+ const ref = mod;
174
+ const existing = this._moduleRefs.get(ref);
175
+ if (existing !== void 0) {
176
+ this._moduleRefs.set(ref, existing + 1);
177
+ return;
178
+ }
179
+ this._moduleRefs.set(ref, 1);
180
+ if (isSyncModule(mod)) {
181
+ const builder = this._createModuleBuilder(ref);
182
+ mod._setup(builder);
183
+ } else {
184
+ const importPromises = [];
185
+ const builder = this._createAsyncModuleBuilder(ref, importPromises);
186
+ await mod._setup(builder);
187
+ if (importPromises.length > 0) await Promise.all(importPromises);
188
+ }
189
+ }
190
+ _createModuleBuilder(moduleRef) {
191
+ return {
192
+ bind: (t) => this._createBindToBuilder(t, moduleRef),
193
+ import: (...mods) => {
194
+ this._loadSyncModules(mods);
195
+ }
196
+ };
197
+ }
198
+ _createAsyncModuleBuilder(moduleRef, importPromises) {
199
+ return {
200
+ bind: (t) => this._createBindToBuilder(t, moduleRef),
201
+ import: (...mods) => {
202
+ for (const mod of mods) importPromises.push(this._loadOneModuleAsync(mod));
203
+ }
204
+ };
188
205
  }
189
206
  unload(...modules) {
190
- this.invalidateDevValidationState();
191
- for (const module of modules) {
192
- const ownedBindingIds = this.loadedModules.get(module);
193
- if (ownedBindingIds === void 0) throw new InternalError(`Module "${module.name}" is not loaded on this container.`);
194
- for (const bindingId of [...ownedBindingIds].reverse()) {
195
- this.ownScopeManager.releaseByBindingId(bindingId);
196
- this.ownRegistry.removeById(bindingId);
207
+ this._assertNotDisposed();
208
+ for (const mod of modules) this._unloadModuleSync(mod);
209
+ }
210
+ _unloadModuleSync(ref) {
211
+ const count = this._moduleRefs.get(ref) ?? 0;
212
+ if (count <= 1) {
213
+ this._moduleRefs.delete(ref);
214
+ const ids = this._moduleBindingIds.get(ref) ?? [];
215
+ this._moduleBindingIds.delete(ref);
216
+ for (const id of ids) {
217
+ const binding = this._registry.getById(id);
218
+ if (binding !== void 0) {
219
+ this._registry.removeById(id);
220
+ if (this._scope.hasSingleton(id)) {
221
+ const instance = this._scope.getSingleton(id);
222
+ this._scope.deleteSingleton(id);
223
+ this._lifecycle.runDeactivationSync(binding, instance, this._getMetadataReader());
224
+ }
225
+ }
197
226
  }
198
- this.loadedModules.delete(module);
199
- }
227
+ } else this._moduleRefs.set(ref, count - 1);
200
228
  }
201
229
  async unloadAsync(...modules) {
202
- this.invalidateDevValidationState();
203
- for (const module of modules) {
204
- const ownedBindingIds = this.loadedModules.get(module);
205
- if (ownedBindingIds === void 0) throw new InternalError(`Module "${module.name}" is not loaded on this container.`);
206
- for (const bindingId of [...ownedBindingIds].reverse()) {
207
- await this.ownScopeManager.releaseByBindingIdAsync(bindingId);
208
- this.ownRegistry.removeById(bindingId);
230
+ this._assertNotDisposed();
231
+ for (const mod of modules) await this._unloadModuleAsync(mod);
232
+ }
233
+ async _unloadModuleAsync(ref) {
234
+ const count = this._moduleRefs.get(ref) ?? 0;
235
+ if (count <= 1) {
236
+ this._moduleRefs.delete(ref);
237
+ const ids = this._moduleBindingIds.get(ref) ?? [];
238
+ this._moduleBindingIds.delete(ref);
239
+ for (const id of ids) {
240
+ const binding = this._registry.getById(id);
241
+ if (binding !== void 0) {
242
+ this._registry.removeById(id);
243
+ if (this._scope.hasSingleton(id)) {
244
+ const instance = this._scope.getSingleton(id);
245
+ this._scope.deleteSingleton(id);
246
+ await this._lifecycle.runDeactivation(binding, instance, this._getMetadataReader());
247
+ }
248
+ }
209
249
  }
210
- this.loadedModules.delete(module);
250
+ } else this._moduleRefs.set(ref, count - 1);
251
+ }
252
+ loadAutoRegistered(registry) {
253
+ this._assertNotDisposed();
254
+ const entries = registry.entries();
255
+ for (const { target, scope } of entries) {
256
+ const bb = this._createBindToBuilder(target).toSelf();
257
+ if (scope === "singleton") bb.singleton();
258
+ else if (scope === "scoped") bb.scoped();
259
+ else bb.transient();
211
260
  }
261
+ return entries.length;
212
262
  }
213
- /**
214
- * Resolves synchronously. Captive dependency (singleton holding scoped/transient) throws
215
- * {@link ScopeViolationError} with binding ids and full resolution path. In dev/test, the first
216
- * successful resolution also triggers a one-time static {@link validate} when not in production.
217
- */
218
- resolve(key, hint) {
219
- try {
220
- return this.resolver.resolveRoot(key, hint);
221
- } finally {
222
- this.maybeRunDevValidationOnce();
223
- }
263
+ onActivation(token, handler) {
264
+ this._assertNotDisposed();
265
+ this._lifecycle.registerActivation(token, handler);
224
266
  }
225
- /**
226
- * Async variant of {@link resolve}; same dev/test validation and runtime scope enforcement.
227
- */
228
- resolveAsync(key, hint) {
229
- return this.resolver.resolveAsyncRoot(key, hint).finally(() => {
230
- this.maybeRunDevValidationOnce();
231
- });
232
- }
233
- /**
234
- * Optional root resolution: returns `undefined` when the requested key is absent (or filtered out
235
- * without a name/tag hint), while preserving normal errors for nested required dependencies.
236
- */
237
- resolveOptional(key, hint) {
238
- try {
239
- return this.resolver.resolveOptionalRoot(key, hint);
240
- } finally {
241
- this.maybeRunDevValidationOnce();
242
- }
267
+ onDeactivation(token, handler) {
268
+ this._assertNotDisposed();
269
+ this._lifecycle.registerDeactivation(token, handler);
270
+ }
271
+ resolve(token, hint) {
272
+ this._assertNotDisposed();
273
+ if (hint === void 0) return this._resolver.resolveFromContext(token, [], []);
274
+ return this._resolver.resolve(token, hint, [], []);
275
+ }
276
+ resolveAsync(token, hint) {
277
+ this._assertNotDisposed();
278
+ if (hint === void 0) return this._resolver.resolveAsyncFromContext(token, [], []);
279
+ return this._resolver.resolveAsync(token, hint, [], []);
243
280
  }
244
- /**
245
- * Synchronously resolves every matching binding for a key.
246
- * Returns an empty array when no binding exists.
247
- */
248
- resolveAll(key, hint) {
249
- try {
250
- return this.resolver.resolveAllRoot(key, hint);
251
- } finally {
252
- this.maybeRunDevValidationOnce();
281
+ resolveOptional(token, hint) {
282
+ this._assertNotDisposed();
283
+ return this._resolver.resolveOptional(token, hint, [], []);
284
+ }
285
+ resolveOptionalAsync(token, hint) {
286
+ this._assertNotDisposed();
287
+ return this._resolver.resolveOptionalAsync(token, hint, [], []);
288
+ }
289
+ resolveAll(token, hint) {
290
+ this._assertNotDisposed();
291
+ return this._resolver.resolveAll(token, hint, [], []);
292
+ }
293
+ resolveAllAsync(token, hint) {
294
+ this._assertNotDisposed();
295
+ return this._resolver.resolveAllAsync(token, hint, [], []);
296
+ }
297
+ createChild() {
298
+ this._assertNotDisposed();
299
+ return new DefaultContainer(this);
300
+ }
301
+ async dispose() {
302
+ if (this._disposed) return;
303
+ this._disposed = true;
304
+ const reader = this._getMetadataReader();
305
+ for (const [id, instance] of this._scope.getAllSingletons()) {
306
+ const binding = this._registry.getById(id);
307
+ if (binding !== void 0) await this._lifecycle.runDeactivation(binding, instance, reader);
253
308
  }
309
+ this._scope.clearAll();
310
+ }
311
+ [Symbol.asyncDispose]() {
312
+ return this.dispose();
313
+ }
314
+ [Symbol.dispose]() {
315
+ throw new SyncDisposalNotSupportedError();
254
316
  }
255
- /**
256
- * Async variant of {@link resolveAll}: safe for multi-bindings that mix sync and async factories
257
- * (spec §5.2).
258
- */
259
- resolveAllAsync(key, hint) {
260
- return this.resolver.resolveAllAsyncRoot(key, hint).finally(() => {
261
- this.maybeRunDevValidationOnce();
262
- });
263
- }
264
- /**
265
- * Eagerly resolves every registered `singleton` binding, including `async-dynamic` factories.
266
- */
267
317
  async initializeAsync() {
268
- for (const registryKey of this.collectAllRegistryKeysInHierarchy()) {
269
- const list = this.lookupBindings(registryKey);
270
- if (list === void 0) continue;
271
- for (const binding of list) {
272
- if (binding.scope !== "singleton") continue;
273
- const hint = resolveHintForBinding(binding);
274
- await this.resolveAsync(registryKey, hint);
318
+ this._assertNotDisposed();
319
+ const allBindings = this._registry.allBindings();
320
+ for (const binding of allBindings) {
321
+ if (binding.kind === "alias") continue;
322
+ if (effectiveBindingScope(binding) === "singleton" && !this._scope.hasSingleton(binding.id)) {
323
+ if (binding.predicate !== void 0) continue;
324
+ if (binding.kind === "constant" && binding.onActivation === void 0) continue;
325
+ const slotHint = slotKeyToResolveOptions(binding.slot);
326
+ await this.resolveAsync(binding.token, slotHint);
275
327
  }
276
328
  }
277
329
  }
278
- /**
279
- * Marks the dev/test one-shot validation as stale so the next resolve or load triggers it again.
280
- * Called on every registry mutation (bind, unbind, rebind, load, unload).
281
- */
282
- invalidateDevValidationState() {
283
- this.devValidationRan = false;
284
- }
285
- /**
286
- * Runs scope validation at most once per registry epoch when `NODE_ENV` is not `"production"`.
287
- * Guards against repeated validation on successive resolves without intervening mutations.
288
- */
289
- maybeRunDevValidationOnce() {
290
- if (!isDevelopmentOrTestEnvironment()) return;
291
- if (this.devValidationRan) return;
292
- this.devValidationRan = true;
293
- this.validate();
294
- }
295
- /**
296
- * Validates static dependency scope rules: a singleton must not depend on scoped/transient
297
- * class/factory/alias targets. Constant dependencies are always allowed.
298
- */
299
330
  validate() {
300
- validateScopeRules({
301
- collectAllRegistryKeys: () => this.collectAllRegistryKeysInHierarchy(),
302
- lookupBindings: (registryKey) => this.lookupBindings(registryKey),
303
- getMetadataReader: () => this.metadataReader
304
- });
305
- }
306
- /**
307
- * Dev/debug snapshot of registered bindings and activation status (design spec §5.5).
308
- */
309
- inspect() {
310
- return this.createInspector().getSnapshot();
331
+ this._assertNotDisposed();
332
+ const reader = this._getMetadataReader();
333
+ const allBindings = this._registry.allBindings();
334
+ for (const binding of allBindings) {
335
+ if (binding.kind === "alias" || binding.kind === "constant" || binding.kind === "dynamic" || binding.kind === "dynamic-async") continue;
336
+ const scope = effectiveBindingScope(binding);
337
+ if (scope !== "singleton") continue;
338
+ const deps = this._getDepsForBinding(binding, reader);
339
+ for (const dep of deps) this._validateDep(tokenName(binding.token), scope, dep, [tokenName(binding.token)], reader);
340
+ }
311
341
  }
312
- /**
313
- * Delegates canonical dependency-graph generation to {@link ContainerInspector}.
314
- */
315
- generateDependencyGraph(options) {
316
- return this.createInspector().generateDependencyGraph(options);
317
- }
318
- /**
319
- * Registers every class collected by `@injectable({ autoRegister: true })`.
320
- * Returns how many entries were processed.
321
- */
322
- loadAutoRegistered() {
323
- const entries = getAutoRegistered();
324
- let count = 0;
325
- for (const { implementationClass, scope } of entries) {
326
- const builder = this.bind(implementationClass).toSelf();
327
- switch (scope) {
328
- case "singleton":
329
- builder.singleton();
330
- break;
331
- case "scoped":
332
- builder.scoped();
333
- break;
334
- default: builder.transient();
342
+ _getDepsForBinding(binding, reader) {
343
+ const result = [];
344
+ if (binding.kind === "class") {
345
+ const meta = reader.getConstructorMetadata(binding.target);
346
+ if (meta !== void 0) for (const param of meta.params) {
347
+ const depBindings = this._registry.getAll(param.token);
348
+ for (const db of depBindings) if (db.kind !== "alias") result.push({
349
+ token: param.token,
350
+ scope: effectiveBindingScope(db)
351
+ });
335
352
  }
336
- count++;
353
+ } else if (binding.kind === "resolved" || binding.kind === "resolved-async") for (const dep of binding.deps) {
354
+ const depBindings = this._registry.getAll(dep.token);
355
+ for (const db of depBindings) if (db.kind !== "alias") result.push({
356
+ token: dep.token,
357
+ scope: effectiveBindingScope(db)
358
+ });
359
+ }
360
+ return result;
361
+ }
362
+ _validateDep(consumerToken, consumerScope, dep, path, _reader) {
363
+ if (consumerScope === "singleton") {
364
+ if (dep.scope === "scoped" || dep.scope === "transient") throw new ScopeViolationError({
365
+ consumerToken,
366
+ consumerScope,
367
+ dependencyToken: tokenName(dep.token),
368
+ dependencyScope: dep.scope,
369
+ path
370
+ });
337
371
  }
338
- return count;
339
372
  }
340
- [Symbol.dispose]() {
341
- throw new InternalError("Container disposal is async. Use `await using container = Container.create()` or call `await container.dispose()` instead of `using`.");
342
- }
343
- /**
344
- * Constructs a {@link ContainerInspector} wired to this container's full hierarchy.
345
- */
346
- createInspector() {
347
- return new ContainerInspector({
348
- collectAllRegistryKeys: () => this.collectAllRegistryKeysInHierarchy(),
349
- lookupBindings: (token) => this.lookupBindings(token),
350
- isBindingCached: (binding) => this.ownScopeManager.isBindingCached(binding),
351
- metadataReader: this.metadataReader
352
- });
353
- }
354
- /**
355
- * Collects the union of all registry keys from this container and every ancestor.
356
- * Deduplicates by reference equality (tokens are objects).
357
- */
358
- collectAllRegistryKeysInHierarchy() {
359
- const keys = /* @__PURE__ */ new Set();
360
- this.accumulateRegistryKeysFromHierarchy(keys, this);
361
- return [...keys];
362
- }
363
- /**
364
- * Recursive helper: walks the parent chain bottom-up, adding each level's registry keys.
365
- */
366
- accumulateRegistryKeysFromHierarchy(keys, container) {
367
- if (container === void 0) return;
368
- for (const entry of container.ownRegistry.listEntries()) keys.add(entry.key);
369
- this.accumulateRegistryKeysFromHierarchy(keys, container.parent);
370
- }
371
- /**
372
- * Child inherits parent bindings via lookup fallback and shares singleton instances,
373
- * but receives an isolated scoped cache.
374
- */
375
- createChild() {
376
- const childRegistry = new BindingRegistry();
377
- const childScope = this.ownScopeManager.createChildScope();
378
- const holder = { current: void 0 };
379
- const resolver = new DependencyResolver({
380
- lookup: (token) => {
381
- const current = holder.current;
382
- if (current === void 0) throw new InternalError("child container is not initialized");
383
- return current.lookupBindings(token);
384
- },
385
- scopeManager: childScope,
386
- metadataReader: this.metadataReader
387
- });
388
- const child = new DefaultContainer(childRegistry, childScope, this, resolver, this.metadataReader);
389
- holder.current = child;
390
- return child;
391
- }
392
- /**
393
- * Lookup helper with parent fallback: own bindings take precedence over parent bindings.
394
- */
373
+ has(token, hint) {
374
+ this._assertNotDisposed();
375
+ return this._inspector.has(token, hint, () => this._parent?.has(token, hint) ?? false);
376
+ }
377
+ hasOwn(token, hint) {
378
+ this._assertNotDisposed();
379
+ return this._inspector.hasOwn(token, hint);
380
+ }
395
381
  lookupBindings(token) {
396
- const own = this.ownRegistry.get(token);
397
- if (own !== void 0 && own.length > 0) return own;
398
- return this.parent?.lookupBindings(token);
382
+ this._assertNotDisposed();
383
+ return this._inspector.lookupBindings(token);
399
384
  }
400
- /**
401
- * Disposes this container's scope manager and runs async deactivation hooks.
402
- */
403
- async dispose() {
404
- await this.ownScopeManager.disposeAsync();
385
+ inspect() {
386
+ this._assertNotDisposed();
387
+ return this._inspector.inspect();
405
388
  }
406
- /**
407
- * Async-dispose protocol hook used by `await using`.
408
- */
409
- [Symbol.asyncDispose]() {
410
- return this.dispose();
389
+ generateDependencyGraph(options) {
390
+ this._assertNotDisposed();
391
+ return buildDependencyGraph(this._registry, this._getMetadataReader(), options, this._parent?._registry);
411
392
  }
412
- /**
413
- * Returns a `bind` function scoped to a module. Registrations are tracked in
414
- * {@link loadedModules} for {@link unload}.
415
- *
416
- * - **Last-wins** (replaces every binding for that token): `bind(token).to*(...)` with no
417
- * `whenNamed` / `whenTagged` / `when` **before** the `to*()` call.
418
- * - **Multi-binding** (append): call `whenNamed`, `whenTagged`, and/or `when` **before** `to*()`
419
- * so the disambiguator exists at registration time — supports `resolveAll` and per-binding
420
- * hints in {@link Container.initializeAsync}.
421
- */
422
- bindForModule(owner) {
423
- return (token) => new BindingBuilder(token, owner.name, {
424
- register: (built) => {
425
- this.invalidateDevValidationState();
426
- if (moduleBindingUsesMultiSlot(built)) this.ownRegistry.add(token, built);
427
- else this.ownRegistry.replaceKeyLastWins(token, built, (removed) => {
428
- this.ownScopeManager.releaseBinding(removed);
429
- });
430
- this.recordBindingForModule(owner, built.id);
431
- },
432
- update: (built) => {
433
- this.invalidateDevValidationState();
434
- this.ownRegistry.replaceById(built.id, built);
435
- }
436
- });
437
- }
438
- /**
439
- * Appends a binding ID to the tracking list for `owner` so {@link unload} can remove it later.
440
- */
441
- recordBindingForModule(owner, id) {
442
- const list = this.loadedModules.get(owner);
443
- if (list === void 0) {
444
- this.loadedModules.set(owner, [id]);
445
- return;
446
- }
447
- list.push(id);
393
+ _assertNotDisposed() {
394
+ if (this._disposed) throw new DisposedContainerError();
448
395
  }
449
- /**
450
- * Creates the {@link ModuleBuilder} passed to a sync module's setup callback.
451
- * The `import` method throws {@link InternalError} if an {@link AsyncModule} is passed.
452
- */
453
- createSyncModuleBuilder(module) {
454
- return {
455
- import: (...deps) => {
456
- for (const dep of deps) {
457
- if (dep instanceof AsyncModule) throw new InternalError(`Module "${module.name}" cannot synchronously import async module "${dep.name}".`);
458
- this.ensureSyncModuleLoaded(dep);
459
- }
460
- },
461
- bind: this.bindForModule(module)
396
+ };
397
+ var BindToBuilderImpl = class {
398
+ constructor(_token, _commit) {
399
+ this._token = _token;
400
+ this._commit = _commit;
401
+ }
402
+ to(type) {
403
+ return new BindingBuilderImpl(this._token, {
404
+ kind: "class",
405
+ target: type,
406
+ scope: "transient"
407
+ }, this._commit);
408
+ }
409
+ toSelf() {
410
+ if (typeof this._token !== "function") throw new Error("toSelf() requires token to be a Constructor");
411
+ return new BindingBuilderImpl(this._token, {
412
+ kind: "class",
413
+ target: this._token,
414
+ scope: "transient"
415
+ }, this._commit);
416
+ }
417
+ toConstantValue(value) {
418
+ return new ConstantBindingBuilderImpl(this._token, value, this._commit);
419
+ }
420
+ toDynamic(factory) {
421
+ return new BindingBuilderImpl(this._token, {
422
+ kind: "dynamic",
423
+ factory,
424
+ scope: "transient"
425
+ }, this._commit);
426
+ }
427
+ toDynamicAsync(factory) {
428
+ return new BindingBuilderImpl(this._token, {
429
+ kind: "dynamic-async",
430
+ factory,
431
+ scope: "transient"
432
+ }, this._commit);
433
+ }
434
+ toResolved(factory, deps) {
435
+ const normalizedDeps = deps.map((d) => normalizeToDescriptor(d));
436
+ return new BindingBuilderImpl(this._token, {
437
+ kind: "resolved",
438
+ factory,
439
+ deps: normalizedDeps,
440
+ scope: "transient"
441
+ }, this._commit);
442
+ }
443
+ toResolvedAsync(factory, deps) {
444
+ const normalizedDeps = deps.map((d) => normalizeToDescriptor(d));
445
+ return new BindingBuilderImpl(this._token, {
446
+ kind: "resolved-async",
447
+ factory,
448
+ deps: normalizedDeps,
449
+ scope: "transient"
450
+ }, this._commit);
451
+ }
452
+ toAlias(target) {
453
+ return new AliasBindingBuilderImpl(this._token, target, this._commit);
454
+ }
455
+ };
456
+ var BindingBuilderImpl = class {
457
+ _slot;
458
+ _predicate;
459
+ _committed;
460
+ constructor(_token, _partial, _commit) {
461
+ this._token = _token;
462
+ this._partial = _partial;
463
+ this._commit = _commit;
464
+ this._slot = {
465
+ ...DEFAULT_SLOT,
466
+ tags: []
462
467
  };
468
+ this._doCommit();
469
+ }
470
+ _doCommit() {
471
+ const previousId = this._committed;
472
+ const binding = {
473
+ ...this._partial,
474
+ id: generateBindingId(),
475
+ token: this._token,
476
+ slot: this._slot,
477
+ predicate: this._predicate
478
+ };
479
+ this._committed = this._commit(binding, previousId);
463
480
  }
464
- /**
465
- * Creates the {@link AsyncModuleBuilder} and a companion `awaitImports` thunk.
466
- * Async sub-imports are collected into `pendingImports` and flushed after the module's
467
- * own setup returns — this avoids interleaving setup code with dependency loading.
468
- */
469
- createAsyncModuleBuilder(module) {
470
- const pendingImports = [];
471
- return {
472
- moduleBuilder: {
473
- import: (...deps) => {
474
- for (const dep of deps) if (dep instanceof AsyncModule) pendingImports.push(this.ensureAsyncModuleLoaded(dep));
475
- else this.ensureSyncModuleLoaded(dep);
476
- },
477
- bind: this.bindForModule(module)
478
- },
479
- awaitImports: async () => {
480
- if (pendingImports.length === 0) return;
481
- await Promise.all(pendingImports);
482
- }
481
+ when(predicate) {
482
+ this._predicate = predicate;
483
+ this._doCommit();
484
+ return this;
485
+ }
486
+ whenNamed(name) {
487
+ this._slot = {
488
+ ...this._slot,
489
+ name
483
490
  };
491
+ this._doCommit();
492
+ return this;
493
+ }
494
+ whenTagged(tag, value) {
495
+ const existing = [...this._slot.tags];
496
+ const idx = existing.findIndex(([k]) => k === tag);
497
+ if (idx !== -1) existing[idx] = [tag, value];
498
+ else existing.push([tag, value]);
499
+ this._slot = {
500
+ ...this._slot,
501
+ tags: existing
502
+ };
503
+ this._doCommit();
504
+ return this;
505
+ }
506
+ whenDefault() {
507
+ return this;
508
+ }
509
+ singleton() {
510
+ const previousId = this._committed;
511
+ this._committed = void 0;
512
+ return new ScopeBuilderImpl(this._token, {
513
+ ...this._partial,
514
+ scope: "singleton"
515
+ }, this._slot, this._predicate, this._commit, "singleton", previousId);
516
+ }
517
+ transient() {
518
+ const previousId = this._committed;
519
+ this._committed = void 0;
520
+ return new ScopeBuilderImpl(this._token, {
521
+ ...this._partial,
522
+ scope: "transient"
523
+ }, this._slot, this._predicate, this._commit, "transient", previousId);
524
+ }
525
+ scoped() {
526
+ const previousId = this._committed;
527
+ this._committed = void 0;
528
+ return new ScopeBuilderImpl(this._token, {
529
+ ...this._partial,
530
+ scope: "scoped"
531
+ }, this._slot, this._predicate, this._commit, "scoped", previousId);
532
+ }
533
+ id() {
534
+ return this._committed;
484
535
  }
485
- /**
486
- * Loads a sync module if not already loaded. Detects circular module imports via
487
- * {@link syncModuleStack} and throws {@link CircularDependencyError} with the full cycle path.
488
- * The module is marked as loaded *before* its setup runs so that re-entrant imports are deduped.
489
- */
490
- ensureSyncModuleLoaded(module) {
491
- if (this.loadedModules.has(module)) return;
492
- if (this.syncModuleStack.includes(module)) throw new CircularDependencyError([...this.syncModuleStack.map((stackedModule) => stackedModule.name), module.name]);
493
- this.syncModuleStack.push(module);
494
- try {
495
- this.loadedModules.set(module, []);
496
- const moduleBuilder = this.createSyncModuleBuilder(module);
497
- module.runSyncSetup(moduleBuilder);
498
- } finally {
499
- this.syncModuleStack.pop();
500
- }
536
+ };
537
+ var ScopeBuilderImpl = class {
538
+ _onActivation;
539
+ _onDeactivation;
540
+ _committed;
541
+ constructor(_token, _partial, _slot, _predicate, _commit, _scope, _initialPreviousId) {
542
+ this._token = _token;
543
+ this._partial = _partial;
544
+ this._slot = _slot;
545
+ this._predicate = _predicate;
546
+ this._commit = _commit;
547
+ this._scope = _scope;
548
+ this._initialPreviousId = _initialPreviousId;
549
+ this._doCommit();
550
+ }
551
+ _doCommit() {
552
+ const previousId = this._committed ?? this._initialPreviousId;
553
+ const binding = {
554
+ ...this._partial,
555
+ id: generateBindingId(),
556
+ token: this._token,
557
+ slot: this._slot,
558
+ predicate: this._predicate,
559
+ onActivation: this._onActivation,
560
+ onDeactivation: this._onDeactivation
561
+ };
562
+ this._committed = this._commit(binding, previousId);
501
563
  }
502
- /**
503
- * Async counterpart of {@link ensureSyncModuleLoaded}: loads the module's async setup,
504
- * then flushes any pending async sub-imports collected during setup.
505
- */
506
- async ensureAsyncModuleLoaded(asyncModule) {
507
- if (this.loadedModules.has(asyncModule)) return;
508
- if (this.asyncModuleStack.includes(asyncModule)) throw new CircularDependencyError([...this.asyncModuleStack.map((stackedAsyncModule) => stackedAsyncModule.name), asyncModule.name]);
509
- this.asyncModuleStack.push(asyncModule);
510
- try {
511
- this.loadedModules.set(asyncModule, []);
512
- const { moduleBuilder, awaitImports } = this.createAsyncModuleBuilder(asyncModule);
513
- await asyncModule.runAsyncSetup(moduleBuilder);
514
- await awaitImports();
515
- } finally {
516
- this.asyncModuleStack.pop();
517
- }
564
+ onActivation(fn) {
565
+ this._onActivation = fn;
566
+ this._doCommit();
567
+ return this;
568
+ }
569
+ onDeactivation(fn) {
570
+ this._onDeactivation = fn;
571
+ this._doCommit();
572
+ return this;
573
+ }
574
+ id() {
575
+ return this._committed;
576
+ }
577
+ };
578
+ var ConstantBindingBuilderImpl = class {
579
+ _slot;
580
+ _predicate;
581
+ _onActivation;
582
+ _onDeactivation;
583
+ _committed;
584
+ constructor(_token, _value, _commit) {
585
+ this._token = _token;
586
+ this._value = _value;
587
+ this._commit = _commit;
588
+ this._slot = {
589
+ ...DEFAULT_SLOT,
590
+ tags: []
591
+ };
592
+ this._doCommit();
593
+ }
594
+ _doCommit() {
595
+ const previousId = this._committed;
596
+ const binding = {
597
+ kind: "constant",
598
+ id: generateBindingId(),
599
+ token: this._token,
600
+ slot: this._slot,
601
+ predicate: this._predicate,
602
+ value: this._value,
603
+ scope: "singleton",
604
+ onActivation: this._onActivation,
605
+ onDeactivation: this._onDeactivation
606
+ };
607
+ this._committed = this._commit(binding, previousId);
608
+ }
609
+ when(predicate) {
610
+ this._predicate = predicate;
611
+ this._doCommit();
612
+ return this;
613
+ }
614
+ whenNamed(name) {
615
+ this._slot = {
616
+ ...this._slot,
617
+ name
618
+ };
619
+ this._doCommit();
620
+ return this;
621
+ }
622
+ whenTagged(tag, value) {
623
+ const existing = [...this._slot.tags];
624
+ const idx = existing.findIndex(([k]) => k === tag);
625
+ if (idx !== -1) existing[idx] = [tag, value];
626
+ else existing.push([tag, value]);
627
+ this._slot = {
628
+ ...this._slot,
629
+ tags: existing
630
+ };
631
+ this._doCommit();
632
+ return this;
633
+ }
634
+ whenDefault() {
635
+ return this;
636
+ }
637
+ onActivation(fn) {
638
+ this._onActivation = fn;
639
+ this._doCommit();
640
+ return this;
641
+ }
642
+ onDeactivation(fn) {
643
+ this._onDeactivation = fn;
644
+ this._doCommit();
645
+ return this;
646
+ }
647
+ id() {
648
+ return this._committed;
649
+ }
650
+ };
651
+ var AliasBindingBuilderImpl = class {
652
+ _slot;
653
+ _predicate;
654
+ _committed;
655
+ constructor(_token, _target, _commit) {
656
+ this._token = _token;
657
+ this._target = _target;
658
+ this._commit = _commit;
659
+ this._slot = {
660
+ ...DEFAULT_SLOT,
661
+ tags: []
662
+ };
663
+ this._doCommit();
664
+ }
665
+ _doCommit() {
666
+ const previousId = this._committed;
667
+ const binding = {
668
+ kind: "alias",
669
+ id: generateBindingId(),
670
+ token: this._token,
671
+ slot: this._slot,
672
+ predicate: this._predicate,
673
+ target: this._target
674
+ };
675
+ this._committed = this._commit(binding, previousId);
676
+ }
677
+ when(predicate) {
678
+ this._predicate = predicate;
679
+ this._doCommit();
680
+ return this;
681
+ }
682
+ whenNamed(name) {
683
+ this._slot = {
684
+ ...this._slot,
685
+ name
686
+ };
687
+ this._doCommit();
688
+ return this;
689
+ }
690
+ whenTagged(tag, value) {
691
+ const existing = [...this._slot.tags];
692
+ const idx = existing.findIndex(([k]) => k === tag);
693
+ if (idx !== -1) existing[idx] = [tag, value];
694
+ else existing.push([tag, value]);
695
+ this._slot = {
696
+ ...this._slot,
697
+ tags: existing
698
+ };
699
+ this._doCommit();
700
+ return this;
701
+ }
702
+ whenDefault() {
703
+ return this;
704
+ }
705
+ id() {
706
+ return this._committed;
518
707
  }
519
708
  };
520
- let Container;
521
- (function(_Container) {
522
- function create() {
523
- return DefaultContainer.create();
524
- }
525
- _Container.create = create;
526
- function fromModules(...modules) {
527
- const container = DefaultContainer.create();
709
+ const Container = {
710
+ create() {
711
+ return new DefaultContainer();
712
+ },
713
+ fromModules(...modules) {
714
+ const container = new DefaultContainer();
528
715
  container.load(...modules);
529
716
  return container;
530
- }
531
- _Container.fromModules = fromModules;
532
- async function fromModulesAsync(...modules) {
533
- const container = DefaultContainer.create();
717
+ },
718
+ async fromModulesAsync(...modules) {
719
+ const container = new DefaultContainer();
534
720
  await container.loadAsync(...modules);
535
721
  return container;
536
722
  }
537
- _Container.fromModulesAsync = fromModulesAsync;
538
- })(Container || (Container = {}));
723
+ };
539
724
  //#endregion
540
725
  export { Container };