@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.
Files changed (66) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +54 -29
  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 -327
  8. package/dist/binding.mjs +19 -324
  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 +44 -128
  14. package/dist/container.mjs +664 -433
  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 +69 -188
  26. package/dist/errors.mjs +92 -219
  27. package/dist/graph-adapters/cytoscape.d.mts +24 -0
  28. package/dist/graph-adapters/cytoscape.mjs +23 -0
  29. package/dist/graph-adapters/dot.d.mts +6 -0
  30. package/dist/graph-adapters/dot.mjs +17 -0
  31. package/dist/graph-adapters/reactflow.d.mts +29 -0
  32. package/dist/graph-adapters/reactflow.mjs +29 -0
  33. package/dist/graph-adapters/types.d.mts +2 -0
  34. package/dist/graph-adapters/types.mjs +1 -0
  35. package/dist/index.d.mts +16 -9
  36. package/dist/index.mjs +8 -5
  37. package/dist/inspector.d.mts +34 -95
  38. package/dist/inspector.mjs +57 -256
  39. package/dist/lifecycle.d.mts +20 -53
  40. package/dist/lifecycle.mjs +129 -99
  41. package/dist/metadata/metadata-keys.d.mts +9 -26
  42. package/dist/metadata/metadata-keys.mjs +7 -28
  43. package/dist/metadata/metadata-reader-token.d.mts +7 -0
  44. package/dist/metadata/metadata-reader-token.mjs +5 -0
  45. package/dist/metadata/metadata-types.d.mts +22 -71
  46. package/dist/metadata/symbol-metadata-reader.d.mts +10 -26
  47. package/dist/metadata/symbol-metadata-reader.mjs +32 -45
  48. package/dist/module.d.mts +30 -84
  49. package/dist/module.mjs +26 -72
  50. package/dist/registry.d.mts +32 -63
  51. package/dist/registry.mjs +131 -82
  52. package/dist/resolve-options.d.mts +18 -0
  53. package/dist/resolve-options.mjs +22 -0
  54. package/dist/resolver.d.mts +67 -190
  55. package/dist/resolver.mjs +715 -424
  56. package/dist/scope.d.mts +19 -102
  57. package/dist/scope.mjs +37 -192
  58. package/dist/token.d.mts +8 -22
  59. package/dist/token.mjs +9 -11
  60. package/dist/types.d.mts +48 -0
  61. package/dist/types.mjs +1 -0
  62. package/package.json +52 -14
  63. package/dist/metadata/param-registry.d.mts +0 -16
  64. package/dist/metadata/param-registry.mjs +0 -31
  65. package/dist/scope-validation.d.mts +0 -21
  66. package/dist/scope-validation.mjs +0 -35
@@ -1,494 +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
- constructor(ownRegistry, ownScopeManager, parent, resolver, metadataReader) {
57
- this.ownRegistry = ownRegistry;
58
- this.ownScopeManager = ownScopeManager;
59
- this.parent = parent;
60
- this.resolver = resolver;
61
- this.metadataReader = metadataReader;
62
- }
63
- /**
64
- * Creates a root container with an empty registry and fresh singleton/scoped caches.
65
- * Wires the circular container↔resolver reference via a mutable {@link ContainerRef} holder.
66
- */
67
- static create() {
68
- const ownRegistry = new BindingRegistry();
69
- const ownScopeManager = ScopeManager.createRoot();
70
- const metadataReader = new SymbolMetadataReader();
71
- const holder = { current: void 0 };
72
- const container = new DefaultContainer(ownRegistry, ownScopeManager, void 0, new DependencyResolver({
73
- lookup: (token) => {
74
- const current = holder.current;
75
- if (current === void 0) throw new InternalError("container is not initialized");
76
- return current.lookupBindings(token);
77
- },
78
- scopeManager: ownScopeManager,
79
- metadataReader
80
- }), metadataReader);
81
- holder.current = container;
82
- 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;
83
49
  }
84
- /**
85
- * Starts a fluent {@link BindingBuilder} for the given token or constructor.
86
- * The binding is registered into this container's registry immediately when a
87
- * `to*()` strategy method is called on the returned builder.
88
- */
89
50
  bind(token) {
90
- return new BindingBuilder(token, void 0, {
91
- register: (built) => {
92
- this.invalidateDevValidationState();
93
- this.ownRegistry.add(token, built);
94
- },
95
- update: (built) => {
96
- this.invalidateDevValidationState();
97
- 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
+ }
98
66
  }
99
- });
100
- }
101
- has(token, hint) {
102
- const list = this.lookupBindings(token);
103
- if (list === void 0 || list.length === 0) return false;
104
- if (hint === void 0) return true;
105
- return list.some((binding) => {
106
- if (hint.name !== void 0 && binding.bindingName !== hint.name) return false;
107
- if (hint.tag !== void 0) {
108
- const tag = hint.tag;
109
- 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);
110
75
  }
111
- return true;
112
- });
76
+ return b.id;
77
+ };
78
+ return new BindToBuilderImpl(token, commitBinding);
113
79
  }
114
80
  unbind(tokenOrId) {
115
- this.invalidateDevValidationState();
116
- if (typeof tokenOrId === "string") {
117
- this.ownScopeManager.releaseByBindingId(tokenOrId);
118
- this.ownRegistry.removeById(tokenOrId);
119
- 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
+ }
120
93
  }
121
- const owned = this.ownRegistry.get(tokenOrId);
122
- if (owned !== void 0) for (const binding of owned) this.ownScopeManager.releaseBinding(binding);
123
- this.ownRegistry.remove(tokenOrId);
124
94
  }
125
95
  async unbindAsync(tokenOrId) {
126
- this.invalidateDevValidationState();
127
- if (typeof tokenOrId === "string") {
128
- await this.ownScopeManager.releaseByBindingIdAsync(tokenOrId);
129
- this.ownRegistry.removeById(tokenOrId);
130
- 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
+ }
105
+ }
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());
131
123
  }
132
- const owned = this.ownRegistry.get(tokenOrId);
133
- if (owned !== void 0) for (const binding of owned) await this.ownScopeManager.releaseBindingAsync(binding);
134
- this.ownRegistry.remove(tokenOrId);
135
124
  }
136
125
  rebind(token) {
137
- this.invalidateDevValidationState();
138
- const owned = this.ownRegistry.get(token);
139
- if (owned !== void 0) for (const binding of owned) this.ownScopeManager.releaseBinding(binding);
140
- this.ownRegistry.remove(token);
141
- return this.bind(token);
142
- }
143
- /**
144
- * Registers bindings from synchronous modules. Re-loading a module already present on this
145
- * container is a no-op (deduplication, spec §7.3 Phase 3). When {@link isDevelopmentOrTestEnvironment}
146
- * is true, runs {@link validate} at most once after the registry changes.
147
- */
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
+ }
148
138
  load(...modules) {
149
- this.invalidateDevValidationState();
150
- for (const syncModule of modules) {
151
- if (syncModule instanceof AsyncModule) throw new AsyncModuleLoadError(syncModule.name);
152
- 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);
153
154
  }
154
- this.maybeRunDevValidationOnce();
155
155
  }
156
- /**
157
- * Like {@link load} but allows async modules. Re-loading is deduplicated the same way.
158
- */
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
+ }
159
168
  async loadAsync(...modules) {
160
- this.invalidateDevValidationState();
161
- for (const moduleOrAsync of modules) if (moduleOrAsync instanceof AsyncModule) await this.ensureAsyncModuleLoaded(moduleOrAsync);
162
- else this.ensureSyncModuleLoaded(moduleOrAsync);
163
- 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
+ };
164
205
  }
165
206
  unload(...modules) {
166
- this.invalidateDevValidationState();
167
- for (const module of modules) {
168
- const ownedBindingIds = this.loadedModules.get(module);
169
- if (ownedBindingIds === void 0) throw new InternalError(`Module "${module.name}" is not loaded on this container.`);
170
- for (const bindingId of [...ownedBindingIds].reverse()) {
171
- this.ownScopeManager.releaseByBindingId(bindingId);
172
- 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
+ }
173
226
  }
174
- this.loadedModules.delete(module);
175
- }
227
+ } else this._moduleRefs.set(ref, count - 1);
176
228
  }
177
229
  async unloadAsync(...modules) {
178
- this.invalidateDevValidationState();
179
- for (const module of modules) {
180
- const ownedBindingIds = this.loadedModules.get(module);
181
- if (ownedBindingIds === void 0) throw new InternalError(`Module "${module.name}" is not loaded on this container.`);
182
- for (const bindingId of [...ownedBindingIds].reverse()) {
183
- await this.ownScopeManager.releaseByBindingIdAsync(bindingId);
184
- 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
+ }
185
249
  }
186
- 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();
187
260
  }
261
+ return entries.length;
188
262
  }
189
- /**
190
- * Resolves synchronously. Captive dependency (singleton holding scoped/transient) throws
191
- * {@link ScopeViolationError} with binding ids and full resolution path. In dev/test, the first
192
- * successful resolution also triggers a one-time static {@link validate} when not in production.
193
- */
194
- resolve(key, hint) {
195
- try {
196
- return this.resolver.resolveRoot(key, hint);
197
- } finally {
198
- this.maybeRunDevValidationOnce();
199
- }
263
+ onActivation(token, handler) {
264
+ this._assertNotDisposed();
265
+ this._lifecycle.registerActivation(token, handler);
200
266
  }
201
- /**
202
- * Async variant of {@link resolve}; same dev/test validation and runtime scope enforcement.
203
- */
204
- resolveAsync(key, hint) {
205
- return this.resolver.resolveAsyncRoot(key, hint).finally(() => {
206
- this.maybeRunDevValidationOnce();
207
- });
208
- }
209
- resolveOptional(key, hint) {
210
- try {
211
- return this.resolver.resolveOptionalRoot(key, hint);
212
- } finally {
213
- this.maybeRunDevValidationOnce();
214
- }
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, [], []);
215
280
  }
216
- resolveAll(key, hint) {
217
- try {
218
- return this.resolver.resolveAllRoot(key, hint);
219
- } finally {
220
- 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);
221
308
  }
309
+ this._scope.clearAll();
310
+ }
311
+ [Symbol.asyncDispose]() {
312
+ return this.dispose();
313
+ }
314
+ [Symbol.dispose]() {
315
+ throw new SyncDisposalNotSupportedError();
222
316
  }
223
- /**
224
- * Async variant of {@link resolveAll}: safe for multi-bindings that mix sync and async factories
225
- * (spec §5.2).
226
- */
227
- resolveAllAsync(key, hint) {
228
- return this.resolver.resolveAllAsyncRoot(key, hint).finally(() => {
229
- this.maybeRunDevValidationOnce();
230
- });
231
- }
232
- /**
233
- * Eagerly resolves every registered `singleton` binding, including `async-dynamic` factories.
234
- */
235
317
  async initializeAsync() {
236
- for (const registryKey of this.collectAllRegistryKeysInHierarchy()) {
237
- const list = this.lookupBindings(registryKey);
238
- if (list === void 0) continue;
239
- for (const binding of list) {
240
- if (binding.scope !== "singleton") continue;
241
- const hint = resolveHintForBinding(binding);
242
- 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);
243
327
  }
244
328
  }
245
329
  }
246
- /**
247
- * Marks the dev/test one-shot validation as stale so the next resolve or load triggers it again.
248
- * Called on every registry mutation (bind, unbind, rebind, load, unload).
249
- */
250
- invalidateDevValidationState() {
251
- this.devValidationRan = false;
252
- }
253
- /**
254
- * Runs scope validation at most once per registry epoch when `NODE_ENV` is not `"production"`.
255
- * Guards against repeated validation on successive resolves without intervening mutations.
256
- */
257
- maybeRunDevValidationOnce() {
258
- if (!isDevelopmentOrTestEnvironment()) return;
259
- if (this.devValidationRan) return;
260
- this.devValidationRan = true;
261
- this.validate();
262
- }
263
- /**
264
- * Validates static dependency scope rules: a singleton must not depend on scoped/transient
265
- * class/factory/alias targets. Constant dependencies are always allowed.
266
- */
267
330
  validate() {
268
- validateScopeRules({
269
- collectAllRegistryKeys: () => this.collectAllRegistryKeysInHierarchy(),
270
- lookupBindings: (registryKey) => this.lookupBindings(registryKey),
271
- getMetadataReader: () => this.metadataReader
272
- });
273
- }
274
- /**
275
- * Dev/debug snapshot of registered bindings and activation status (design spec §5.5).
276
- */
277
- inspect() {
278
- 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
+ }
279
341
  }
280
- generateDependencyGraph(options) {
281
- const inspector = this.createInspector();
282
- if (options?.format === "json") return inspector.generateDependencyGraph(options);
283
- return inspector.generateDotGraph(options);
284
- }
285
- loadAutoRegistered() {
286
- const entries = getAutoRegistered();
287
- let count = 0;
288
- for (const { implementationClass, scope } of entries) {
289
- const builder = this.bind(implementationClass).toSelf();
290
- switch (scope) {
291
- case "singleton":
292
- builder.singleton();
293
- break;
294
- case "scoped":
295
- builder.scoped();
296
- break;
297
- 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
+ });
298
352
  }
299
- 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
+ });
300
371
  }
301
- return count;
302
372
  }
303
- [Symbol.dispose]() {
304
- throw new InternalError("Container disposal is async. Use `await using container = Container.create()` or call `await container.dispose()` instead of `using`.");
305
- }
306
- /**
307
- * Constructs a {@link ContainerInspector} wired to this container's full hierarchy.
308
- */
309
- createInspector() {
310
- return new ContainerInspector({
311
- collectAllRegistryKeys: () => this.collectAllRegistryKeysInHierarchy(),
312
- lookupBindings: (token) => this.lookupBindings(token),
313
- isBindingCached: (binding) => this.ownScopeManager.isBindingCached(binding),
314
- metadataReader: this.metadataReader
315
- });
316
- }
317
- /**
318
- * Collects the union of all registry keys from this container and every ancestor.
319
- * Deduplicates by reference equality (tokens are objects).
320
- */
321
- collectAllRegistryKeysInHierarchy() {
322
- const keys = /* @__PURE__ */ new Set();
323
- this.accumulateRegistryKeysFromHierarchy(keys, this);
324
- return [...keys];
325
- }
326
- /**
327
- * Recursive helper: walks the parent chain bottom-up, adding each level's registry keys.
328
- */
329
- accumulateRegistryKeysFromHierarchy(keys, container) {
330
- if (container === void 0) return;
331
- for (const entry of container.ownRegistry.listEntries()) keys.add(entry.key);
332
- this.accumulateRegistryKeysFromHierarchy(keys, container.parent);
333
- }
334
- /**
335
- * Child inherits parent bindings via lookup fallback and shares singleton instances,
336
- * but receives an isolated scoped cache.
337
- */
338
- createChild() {
339
- const childRegistry = new BindingRegistry();
340
- const childScope = this.ownScopeManager.createChildScope();
341
- const holder = { current: void 0 };
342
- const resolver = new DependencyResolver({
343
- lookup: (token) => {
344
- const current = holder.current;
345
- if (current === void 0) throw new InternalError("child container is not initialized");
346
- return current.lookupBindings(token);
347
- },
348
- scopeManager: childScope,
349
- metadataReader: this.metadataReader
350
- });
351
- const child = new DefaultContainer(childRegistry, childScope, this, resolver, this.metadataReader);
352
- holder.current = child;
353
- return child;
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);
354
380
  }
355
381
  lookupBindings(token) {
356
- const own = this.ownRegistry.get(token);
357
- if (own !== void 0 && own.length > 0) return own;
358
- return this.parent?.lookupBindings(token);
382
+ this._assertNotDisposed();
383
+ return this._inspector.lookupBindings(token);
359
384
  }
360
- async dispose() {
361
- await this.ownScopeManager.disposeAsync();
385
+ inspect() {
386
+ this._assertNotDisposed();
387
+ return this._inspector.inspect();
362
388
  }
363
- [Symbol.asyncDispose]() {
364
- return this.dispose();
389
+ generateDependencyGraph(options) {
390
+ this._assertNotDisposed();
391
+ return buildDependencyGraph(this._registry, this._getMetadataReader(), options, this._parent?._registry);
365
392
  }
366
- /**
367
- * Returns a `bind` function scoped to a module. Registrations are tracked in
368
- * {@link loadedModules} for {@link unload}.
369
- *
370
- * - **Last-wins** (replaces every binding for that token): `bind(token).to*(...)` with no
371
- * `whenNamed` / `whenTagged` / `when` **before** the `to*()` call.
372
- * - **Multi-binding** (append): call `whenNamed`, `whenTagged`, and/or `when` **before** `to*()`
373
- * so the disambiguator exists at registration time — supports `resolveAll` and per-binding
374
- * hints in {@link Container.initializeAsync}.
375
- */
376
- bindForModule(owner) {
377
- return (token) => new BindingBuilder(token, owner.name, {
378
- register: (built) => {
379
- this.invalidateDevValidationState();
380
- if (moduleBindingUsesMultiSlot(built)) this.ownRegistry.add(token, built);
381
- else this.ownRegistry.replaceKeyLastWins(token, built, (removed) => {
382
- this.ownScopeManager.releaseBinding(removed);
383
- });
384
- this.recordBindingForModule(owner, built.id);
385
- },
386
- update: (built) => {
387
- this.invalidateDevValidationState();
388
- this.ownRegistry.replaceById(built.id, built);
389
- }
390
- });
391
- }
392
- /**
393
- * Appends a binding ID to the tracking list for `owner` so {@link unload} can remove it later.
394
- */
395
- recordBindingForModule(owner, id) {
396
- const list = this.loadedModules.get(owner);
397
- if (list === void 0) {
398
- this.loadedModules.set(owner, [id]);
399
- return;
400
- }
401
- list.push(id);
393
+ _assertNotDisposed() {
394
+ if (this._disposed) throw new DisposedContainerError();
402
395
  }
403
- /**
404
- * Creates the {@link ModuleBuilder} passed to a sync module's setup callback.
405
- * The `import` method throws {@link InternalError} if an {@link AsyncModule} is passed.
406
- */
407
- createSyncModuleBuilder(module) {
408
- return {
409
- import: (...deps) => {
410
- for (const dep of deps) {
411
- if (dep instanceof AsyncModule) throw new InternalError(`Module "${module.name}" cannot synchronously import async module "${dep.name}".`);
412
- this.ensureSyncModuleLoaded(dep);
413
- }
414
- },
415
- 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: []
416
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);
417
480
  }
418
- /**
419
- * Creates the {@link AsyncModuleBuilder} and a companion `awaitImports` thunk.
420
- * Async sub-imports are collected into `pendingImports` and flushed after the module's
421
- * own setup returns — this avoids interleaving setup code with dependency loading.
422
- */
423
- createAsyncModuleBuilder(module) {
424
- const pendingImports = [];
425
- return {
426
- moduleBuilder: {
427
- import: (...deps) => {
428
- for (const dep of deps) if (dep instanceof AsyncModule) pendingImports.push(this.ensureAsyncModuleLoaded(dep));
429
- else this.ensureSyncModuleLoaded(dep);
430
- },
431
- bind: this.bindForModule(module)
432
- },
433
- awaitImports: async () => {
434
- if (pendingImports.length === 0) return;
435
- await Promise.all(pendingImports);
436
- }
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
437
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;
438
535
  }
439
- /**
440
- * Loads a sync module if not already loaded. Detects circular module imports via
441
- * {@link syncModuleStack} and throws {@link CircularDependencyError} with the full cycle path.
442
- * The module is marked as loaded *before* its setup runs so that re-entrant imports are deduped.
443
- */
444
- ensureSyncModuleLoaded(module) {
445
- if (this.loadedModules.has(module)) return;
446
- if (this.syncModuleStack.includes(module)) throw new CircularDependencyError([...this.syncModuleStack.map((stackedModule) => stackedModule.name), module.name]);
447
- this.syncModuleStack.push(module);
448
- try {
449
- this.loadedModules.set(module, []);
450
- const moduleBuilder = this.createSyncModuleBuilder(module);
451
- module.runSyncSetup(moduleBuilder);
452
- } finally {
453
- this.syncModuleStack.pop();
454
- }
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);
455
563
  }
456
- /**
457
- * Async counterpart of {@link ensureSyncModuleLoaded}: loads the module's async setup,
458
- * then flushes any pending async sub-imports collected during setup.
459
- */
460
- async ensureAsyncModuleLoaded(asyncModule) {
461
- if (this.loadedModules.has(asyncModule)) return;
462
- if (this.asyncModuleStack.includes(asyncModule)) throw new CircularDependencyError([...this.asyncModuleStack.map((stackedAsyncModule) => stackedAsyncModule.name), asyncModule.name]);
463
- this.asyncModuleStack.push(asyncModule);
464
- try {
465
- this.loadedModules.set(asyncModule, []);
466
- const { moduleBuilder, awaitImports } = this.createAsyncModuleBuilder(asyncModule);
467
- await asyncModule.runAsyncSetup(moduleBuilder);
468
- await awaitImports();
469
- } finally {
470
- this.asyncModuleStack.pop();
471
- }
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;
472
707
  }
473
708
  };
474
- let Container;
475
- (function(_Container) {
476
- function create() {
477
- return DefaultContainer.create();
478
- }
479
- _Container.create = create;
480
- function fromModules(...modules) {
481
- const container = DefaultContainer.create();
709
+ const Container = {
710
+ create() {
711
+ return new DefaultContainer();
712
+ },
713
+ fromModules(...modules) {
714
+ const container = new DefaultContainer();
482
715
  container.load(...modules);
483
716
  return container;
484
- }
485
- _Container.fromModules = fromModules;
486
- async function fromModulesAsync(...modules) {
487
- const container = DefaultContainer.create();
717
+ },
718
+ async fromModulesAsync(...modules) {
719
+ const container = new DefaultContainer();
488
720
  await container.loadAsync(...modules);
489
721
  return container;
490
722
  }
491
- _Container.fromModulesAsync = fromModulesAsync;
492
- })(Container || (Container = {}));
723
+ };
493
724
  //#endregion
494
725
  export { Container };