@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
package/dist/binding.mjs CHANGED
@@ -1,337 +1,24 @@
1
- import { InternalError } from "./errors.mjs";
2
1
  //#region src/binding.ts
3
- /**
4
- * Allocates a new opaque binding identifier (used when `.id(...)` is not supplied).
5
- */
6
- function createBindingIdentifier() {
7
- return globalThis.crypto.randomUUID();
2
+ function slotKeyEquals(a, b) {
3
+ if (a.name !== b.name) return false;
4
+ if (a.tags.length !== b.tags.length) return false;
5
+ for (const [tagKey, tagValue] of a.tags) if (!b.tags.some(([k, v]) => k === tagKey && Object.is(v, tagValue))) return false;
6
+ return true;
8
7
  }
9
- /**
10
- * Fluent builder for registering a single binding against a {@link Token} or {@link Constructor}.
11
- *
12
- * A builder has two phases:
13
- * 1. **Strategy selection** — exactly one `to*()` call (`to`, `toSelf`, `toConstantValue`,
14
- * `toDynamic`, `toDynamicAsync`, `toResolved`, `toAlias`) that determines how the value is produced.
15
- * 2. **Refinement chain** — optional calls to `singleton()`, `transient()`, `scoped()`,
16
- * `onActivation()`, `onDeactivation()`, `whenNamed()`, `whenTagged()`, `when()`, and `id()`.
17
- *
18
- * Calling a second `to*()` method throws {@link InternalError}.
19
- *
20
- * The builder is created by {@link Container.bind} or by `bind` on {@link ModuleBuilder}.
21
- * The container injects {@link RegistryCallbacks} so that every strategy selection and
22
- * refinement is immediately reflected in the live registry.
23
- */
24
- var BindingBuilder = class {
25
- /**
26
- * Current resolution strategy; starts as `"unset"` until a `to*()` method is called.
27
- */
28
- strategy = { type: "unset" };
29
- /**
30
- * Lifetime scope applied to the next binding snapshot; defaults to `"transient"`.
31
- */
32
- scope = "transient";
33
- /**
34
- * True after an explicit `.singleton()` / `.transient()` / `.scoped()` call (prevents constant scope change).
35
- */
36
- isScopeExplicit = false;
37
- /**
38
- * Pre-allocated binding ID set via `id(identifier)` before the first `to*()` call.
39
- */
40
- explicitId;
41
- /**
42
- * Resolve-hint name filter set by `.whenNamed()`.
43
- */
44
- bindingName;
45
- /**
46
- * Tag filters accumulated by successive `.whenTagged()` calls.
47
- */
48
- tags = /* @__PURE__ */ new Map();
49
- /**
50
- * Custom constraint predicates accumulated by `.when()`; all must pass for this binding to be selected.
51
- */
52
- constraintPredicates = [];
53
- /**
54
- * Latest `onActivation` hook provided by `.onActivation(...)`.
55
- * Applied to emitted binding snapshots until replaced.
56
- */
57
- onActivationHandler;
58
- /**
59
- * Latest `onDeactivation` hook provided by `.onDeactivation(...)`.
60
- * Emitted only on builder variants that expose deactivation support.
61
- */
62
- onDeactivationHandler;
63
- /**
64
- * Set when this binding was created inside a {@link Module} / {@link AsyncModule} setup callback.
65
- */
66
- moduleId;
67
- /**
68
- * The most recently emitted {@link Binding} snapshot; `undefined` before the first `to*()` call.
69
- */
70
- currentBinding;
71
- /**
72
- * Container-injected hooks that sync builder mutations into the live registry.
73
- */
74
- callbacks;
75
- constructor(bindingKey, moduleId, callbacks) {
76
- this.bindingKey = bindingKey;
77
- this.moduleId = moduleId;
78
- this.callbacks = callbacks ?? {};
79
- }
80
- /**
81
- * Binds the token to a concrete implementation class; the container constructs it on demand.
82
- */
83
- to(implementationClass) {
84
- this.registerWithStrategy({
85
- type: "class",
86
- implementationClass
87
- });
88
- return this;
89
- }
90
- /**
91
- * Binds the class key to itself — only valid when the key is a constructor.
92
- */
93
- toSelf() {
94
- if (typeof this.bindingKey !== "function") throw new InternalError("toSelf() requires the binding key to be a constructor; use bind(SomeClass) or call to(Class) instead.");
95
- this.registerWithStrategy({
96
- type: "class",
97
- implementationClass: this.bindingKey
98
- });
99
- return this;
100
- }
101
- /**
102
- * Binds the token to a pre-existing value; always resolved as singleton, no construction.
103
- */
104
- toConstantValue(value) {
105
- this.scope = "singleton";
106
- this.registerWithStrategy({
107
- type: "constant",
108
- value
109
- });
110
- return this;
111
- }
112
- /**
113
- * Binds to a synchronous factory; `ctx` provides nested resolution within the same path.
114
- */
115
- toDynamic(factory) {
116
- this.registerWithStrategy({
117
- type: "dynamic",
118
- factory
119
- });
120
- return this;
121
- }
122
- /**
123
- * Binds to an async factory; must be resolved via `resolveAsync` / `resolveAllAsync`.
124
- */
125
- toDynamicAsync(factory) {
126
- this.registerWithStrategy({
127
- type: "async-dynamic",
128
- factory
129
- });
130
- return this;
131
- }
132
- /**
133
- * Binds to a factory whose dependencies are declared explicitly in `deps` and pre-resolved by
134
- * the container before the factory is called — no `ResolutionContext` needed inside the factory.
135
- */
136
- toResolved(factory, deps) {
137
- this.registerWithStrategy({
138
- type: "resolved",
139
- factory,
140
- dependencyTokens: deps
141
- });
142
- return this;
143
- }
144
- /**
145
- * Redirects resolution to `targetToken`; the container resolves whatever is bound there.
146
- */
147
- toAlias(targetToken) {
148
- this.registerWithStrategy({
149
- type: "alias",
150
- targetToken
151
- });
152
- return this;
153
- }
154
- /**
155
- * One instance per container; supports `onDeactivation`.
156
- */
157
- singleton() {
158
- this.assertScopeMutable();
159
- this.scope = "singleton";
160
- this.isScopeExplicit = true;
161
- this.refreshRegisteredBinding();
162
- return this;
163
- }
164
- /**
165
- * New instance on every resolution (default scope).
166
- */
167
- transient() {
168
- this.assertScopeMutable();
169
- this.scope = "transient";
170
- this.isScopeExplicit = true;
171
- this.refreshRegisteredBinding();
172
- return this;
173
- }
174
- /**
175
- * One instance per child container scope.
176
- */
177
- scoped() {
178
- this.assertScopeMutable();
179
- this.scope = "scoped";
180
- this.isScopeExplicit = true;
181
- this.refreshRegisteredBinding();
182
- return this;
183
- }
184
- /**
185
- * Called with the resolved instance after construction; the return value replaces the instance.
186
- */
187
- onActivation(handler) {
188
- this.onActivationHandler = handler;
189
- this.refreshRegisteredBinding();
190
- return this;
191
- }
192
- /**
193
- * @internal Keep public for runtime correctness; hidden from transient/scoped builders via type aliases.
194
- */
195
- onDeactivation(handler) {
196
- this.onDeactivationHandler = handler;
197
- this.refreshRegisteredBinding();
198
- return this;
199
- }
200
- /**
201
- * This binding only resolves when the caller passes `{ name }` as the resolve hint.
202
- */
203
- whenNamed(name) {
204
- this.bindingName = name;
205
- this.refreshRegisteredBinding();
206
- return this;
207
- }
208
- /**
209
- * This binding only resolves when the caller passes `{ tag: [tag, tagValue] }` as the resolve hint.
210
- */
211
- whenTagged(tag, tagValue) {
212
- this.tags.set(tag, tagValue);
213
- this.refreshRegisteredBinding();
214
- return this;
215
- }
216
- /**
217
- * Adds a custom predicate; all predicates must pass for the binding to be selected.
218
- */
219
- when(constraint) {
220
- this.constraintPredicates.push(constraint);
221
- this.refreshRegisteredBinding();
222
- return this;
223
- }
224
- id(identifier) {
225
- if (this.currentBinding !== void 0) {
226
- if (identifier !== void 0 && identifier !== this.currentBinding.id) throw new InternalError("Cannot change binding identifier after registration.");
227
- return this.currentBinding.id;
228
- }
229
- if (identifier !== void 0) {
230
- this.explicitId = identifier;
231
- return this.explicitId;
232
- }
233
- this.explicitId = this.explicitId ?? createBindingIdentifier();
234
- return this.explicitId;
235
- }
236
- /**
237
- * Records the chosen strategy and emits the first {@link Binding} snapshot.
238
- * Throws {@link InternalError} if a strategy was already selected (double `to*()` call).
239
- */
240
- registerWithStrategy(next) {
241
- if (this.strategy.type !== "unset") throw new InternalError("A binding strategy was already selected; only one to*(...) chain is allowed per builder.");
242
- this.strategy = next;
243
- const bindingId = this.id();
244
- const binding = this.createBinding(bindingId, next);
245
- this.currentBinding = binding;
246
- this.callbacks.register?.(binding);
247
- }
248
- /**
249
- * Re-creates the {@link Binding} snapshot from the current builder state and pushes
250
- * the update to the registry. No-op if no strategy has been set yet.
251
- */
252
- refreshRegisteredBinding() {
253
- if (this.currentBinding === void 0 || this.strategy.type === "unset") return;
254
- const next = this.createBinding(this.currentBinding.id, this.strategy);
255
- this.currentBinding = next;
256
- this.callbacks.update?.(next);
257
- }
258
- /**
259
- * Guards against calling `.singleton()` / `.transient()` / `.scoped()` on a constant binding,
260
- * which is locked to `"singleton"` scope by invariant.
261
- */
262
- assertScopeMutable() {
263
- if (this.strategy.type === "constant") throw new InternalError("Constant bindings are always singleton and do not support scope changes.");
264
- }
265
- /**
266
- * Produces an immutable {@link Binding} snapshot from the current builder fields.
267
- * Called by both {@link registerWithStrategy} and {@link refreshRegisteredBinding}.
268
- */
269
- createBinding(id, strategy) {
270
- const constraint = this.constraintPredicates.length === 0 ? void 0 : (ctx) => this.constraintPredicates.every((predicate) => predicate(ctx));
271
- const lifecycle = {
272
- bindingName: this.bindingName,
273
- tags: new Map(this.tags),
274
- onActivation: this.onActivationHandler,
275
- onDeactivation: this.onDeactivationHandler,
276
- constraint
277
- };
278
- const moduleFields = this.moduleId === void 0 ? {} : { moduleId: this.moduleId };
279
- switch (strategy.type) {
280
- case "constant": return {
281
- ...lifecycle,
282
- ...moduleFields,
283
- id,
284
- scope: "singleton",
285
- kind: "constant",
286
- value: strategy.value
287
- };
288
- case "class": return {
289
- ...lifecycle,
290
- ...moduleFields,
291
- id,
292
- scope: this.scope,
293
- kind: "class",
294
- implementationClass: strategy.implementationClass
295
- };
296
- case "dynamic": return {
297
- ...lifecycle,
298
- ...moduleFields,
299
- id,
300
- scope: this.scope,
301
- kind: "dynamic",
302
- factory: strategy.factory
303
- };
304
- case "async-dynamic": return {
305
- ...lifecycle,
306
- ...moduleFields,
307
- id,
308
- scope: this.scope,
309
- kind: "async-dynamic",
310
- factory: strategy.factory
311
- };
312
- case "resolved": return {
313
- ...lifecycle,
314
- ...moduleFields,
315
- id,
316
- scope: this.scope,
317
- kind: "resolved",
318
- dependencyTokens: strategy.dependencyTokens,
319
- factory: strategy.factory
320
- };
321
- case "alias": return {
322
- ...lifecycle,
323
- ...moduleFields,
324
- id,
325
- scope: this.scope,
326
- kind: "alias",
327
- targetToken: strategy.targetToken
328
- };
329
- default: return strategy;
330
- }
331
- }
8
+ const DEFAULT_SLOT = {
9
+ name: void 0,
10
+ tags: []
332
11
  };
333
- function bind(key) {
334
- return new BindingBuilder(key, void 0);
12
+ function slotKeyToString(slot) {
13
+ if (slot.name === void 0 && slot.tags.length === 0) return "default";
14
+ const parts = [];
15
+ if (slot.name !== void 0) parts.push(`name:${slot.name}`);
16
+ for (const [k, v] of slot.tags) parts.push(`tag:${k}=${String(v)}`);
17
+ return parts.join(",");
18
+ }
19
+ let _idCounter = 0;
20
+ function generateBindingId() {
21
+ return String(++_idCounter);
335
22
  }
336
23
  //#endregion
337
- export { BindingBuilder, bind, createBindingIdentifier };
24
+ export { DEFAULT_SLOT, generateBindingId, slotKeyEquals, slotKeyToString };
@@ -1,33 +1,15 @@
1
+ import { Constructor } from "./constructor-type.mjs";
1
2
  import { Token } from "./token.mjs";
2
- import { ConstraintContext, Constructor } from "./binding.mjs";
3
+ import { ConstraintContext } from "./types.mjs";
3
4
 
4
5
  //#region src/constraints.d.ts
5
- /**
6
- * Constraint predicate factory: matches when the direct parent on the materialization stack
7
- * was registered under `registryKey`. Pass the result to {@link BindingBuilder.when}.
8
- *
9
- * @param registryKey - Token or constructor that the parent binding must be registered against.
10
- * @returns A predicate compatible with {@link BindingBuilder.when}.
11
- */
12
- declare function whenParentIs(registryKey: Token<unknown> | Constructor<unknown>): (ctx: ConstraintContext) => boolean;
13
- /**
14
- * Constraint predicate factory: matches when *any* ancestor on the materialization stack
15
- * (not just the immediate parent) was registered under `registryKey`.
16
- * Pass the result to {@link BindingBuilder.when}.
17
- *
18
- * @param registryKey - Token or constructor to search for across the full construction chain.
19
- * @returns A predicate compatible with {@link BindingBuilder.when}.
20
- */
21
- declare function whenAnyAncestorIs(registryKey: Token<unknown> | Constructor<unknown>): (ctx: ConstraintContext) => boolean;
22
- /**
23
- * Constraint predicate factory: matches when the immediate parent binding carries a tag
24
- * whose key is `tag` and whose value is reference-equal to `tagValue` (`Object.is`).
25
- * Pass the result to {@link BindingBuilder.when}.
26
- *
27
- * @param tag - Tag key to check on the parent binding.
28
- * @param tagValue - Expected value; compared via `Object.is`.
29
- * @returns A predicate compatible with {@link BindingBuilder.when}.
30
- */
31
- declare function whenTargetTagged(tag: string, tagValue: unknown): (ctx: ConstraintContext) => boolean;
6
+ declare function whenParentIs(t: Token<unknown> | Constructor): (ctx: ConstraintContext) => boolean;
7
+ declare function whenNoParentIs(t: Token<unknown> | Constructor): (ctx: ConstraintContext) => boolean;
8
+ declare function whenAnyAncestorIs(t: Token<unknown> | Constructor): (ctx: ConstraintContext) => boolean;
9
+ declare function whenNoAncestorIs(t: Token<unknown> | Constructor): (ctx: ConstraintContext) => boolean;
10
+ declare function whenParentNamed(name: string): (ctx: ConstraintContext) => boolean;
11
+ declare function whenAnyAncestorNamed(name: string): (ctx: ConstraintContext) => boolean;
12
+ declare function whenParentTagged(tag: string, value: unknown): (ctx: ConstraintContext) => boolean;
13
+ declare function whenAnyAncestorTagged(tag: string, value: unknown): (ctx: ConstraintContext) => boolean;
32
14
  //#endregion
33
- export { whenAnyAncestorIs, whenParentIs, whenTargetTagged };
15
+ export { whenAnyAncestorIs, whenAnyAncestorNamed, whenAnyAncestorTagged, whenNoAncestorIs, whenNoParentIs, whenParentIs, whenParentNamed, whenParentTagged };
@@ -1,39 +1,35 @@
1
+ import { tokenName } from "./token.mjs";
1
2
  //#region src/constraints.ts
2
- /**
3
- * Constraint predicate factory: matches when the direct parent on the materialization stack
4
- * was registered under `registryKey`. Pass the result to {@link BindingBuilder.when}.
5
- *
6
- * @param registryKey - Token or constructor that the parent binding must be registered against.
7
- * @returns A predicate compatible with {@link BindingBuilder.when}.
8
- */
9
- function whenParentIs(registryKey) {
10
- return (ctx) => ctx.parent?.registryKey === registryKey;
11
- }
12
- /**
13
- * Constraint predicate factory: matches when *any* ancestor on the materialization stack
14
- * (not just the immediate parent) was registered under `registryKey`.
15
- * Pass the result to {@link BindingBuilder.when}.
16
- *
17
- * @param registryKey - Token or constructor to search for across the full construction chain.
18
- * @returns A predicate compatible with {@link BindingBuilder.when}.
19
- */
20
- function whenAnyAncestorIs(registryKey) {
21
- return (ctx) => ctx.materializationStack.some((frame) => frame.registryKey === registryKey);
22
- }
23
- /**
24
- * Constraint predicate factory: matches when the immediate parent binding carries a tag
25
- * whose key is `tag` and whose value is reference-equal to `tagValue` (`Object.is`).
26
- * Pass the result to {@link BindingBuilder.when}.
27
- *
28
- * @param tag - Tag key to check on the parent binding.
29
- * @param tagValue - Expected value; compared via `Object.is`.
30
- * @returns A predicate compatible with {@link BindingBuilder.when}.
31
- */
32
- function whenTargetTagged(tag, tagValue) {
33
- return (ctx) => {
34
- if (ctx.parent === void 0) return false;
35
- return Object.is(ctx.parent.tags.get(tag), tagValue);
36
- };
3
+ function tokenNameOf(t) {
4
+ return tokenName(t);
5
+ }
6
+ function whenParentIs(t) {
7
+ const name = tokenNameOf(t);
8
+ return (ctx) => ctx.parent !== void 0 && ctx.parent.tokenName === name;
9
+ }
10
+ function whenNoParentIs(t) {
11
+ const name = tokenNameOf(t);
12
+ return (ctx) => ctx.parent === void 0 || ctx.parent.tokenName !== name;
13
+ }
14
+ function whenAnyAncestorIs(t) {
15
+ const name = tokenNameOf(t);
16
+ return (ctx) => ctx.ancestors.some((f) => f.tokenName === name);
17
+ }
18
+ function whenNoAncestorIs(t) {
19
+ const name = tokenNameOf(t);
20
+ return (ctx) => ctx.ancestors.every((f) => f.tokenName !== name);
21
+ }
22
+ function whenParentNamed(name) {
23
+ return (ctx) => ctx.parent !== void 0 && ctx.parent.slot.name === name;
24
+ }
25
+ function whenAnyAncestorNamed(name) {
26
+ return (ctx) => ctx.ancestors.some((f) => f.slot.name === name);
27
+ }
28
+ function whenParentTagged(tag, value) {
29
+ return (ctx) => ctx.parent !== void 0 && ctx.parent.slot.tags.some(([t, v]) => t === tag && Object.is(v, value));
30
+ }
31
+ function whenAnyAncestorTagged(tag, value) {
32
+ return (ctx) => ctx.ancestors.some((f) => f.slot.tags.some(([t, v]) => t === tag && Object.is(v, value)));
37
33
  }
38
34
  //#endregion
39
- export { whenAnyAncestorIs, whenParentIs, whenTargetTagged };
35
+ export { whenAnyAncestorIs, whenAnyAncestorNamed, whenAnyAncestorTagged, whenNoAncestorIs, whenNoParentIs, whenParentIs, whenParentNamed, whenParentTagged };
@@ -0,0 +1,17 @@
1
+ //#region src/constructor-type.d.ts
2
+ /**
3
+ * A class (newable) that produces `Value`. Rest parameters are `never[]` so
4
+ * real classes with typed constructors remain assignable under
5
+ * `strictFunctionTypes` (unlike `unknown[]`, which is not assignable from
6
+ * narrower parameter types). Runtime construction still uses the real shape;
7
+ * this alias is the DI “class token” surface only.
8
+ */
9
+ type Constructor<Value = unknown> = new (...args: never[]) => Value;
10
+ /**
11
+ * Class constructor as invoked by the resolver after metadata-driven
12
+ * resolution of `unknown[]` dependencies — separate from {@link Constructor},
13
+ * which is the public assignable class token.
14
+ */
15
+ type ConstructorInvocation = new (...args: unknown[]) => unknown;
16
+ //#endregion
17
+ export { Constructor, ConstructorInvocation };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,132 +1,53 @@
1
+ import { Constructor } from "./constructor-type.mjs";
1
2
  import { Token } from "./token.mjs";
2
- import { RegistryKey } from "./registry.mjs";
3
- import { Binding, BindingBuilder, BindingIdentifier, Constructor, ResolveHint } from "./binding.mjs";
4
- import { ContainerGraphJson, ContainerSnapshot, GraphOptions } from "./inspector.mjs";
5
- import { AsyncModule, Module } from "./module.mjs";
3
+ import { ActivationHandler, BindingIdentifier, DeactivationHandler, ResolveOptions } from "./types.mjs";
4
+ import { BindToBuilder } from "./binding.mjs";
5
+ import { AsyncModule, SyncModule } from "./module.mjs";
6
+ import { BindingSnapshot, ContainerSnapshot } from "./inspector.mjs";
7
+ import { ContainerGraphJson, GraphOptions } from "./dependency-graph.mjs";
8
+ import { AutoRegisterRegistry } from "./decorators/injectable.mjs";
6
9
 
7
10
  //#region src/container.d.ts
8
- /**
9
- * Union of sync and async modules accepted by `loadAsync` / `unloadAsync`.
10
- */
11
- type ModuleLike = Module | AsyncModule;
12
- /**
13
- * Public contract for an IoC container (registry, modules, resolution, lifecycle).
14
- * Construct instances with {@link Container.create} or {@link Container.fromModules}.
15
- *
16
- * Implements {@link AsyncDisposable} so `await using container = Container.create()` runs
17
- * {@link Container.dispose} automatically at scope exit (TC39 Explicit Resource Management).
18
- */
19
- interface Container extends AsyncDisposable {
20
- /**
21
- * Starts a fluent binding builder for the given token or constructor.
22
- */
23
- bind<Value>(token: Token<Value> | Constructor<Value>): BindingBuilder<Value>;
24
- /**
25
- * Removes all existing bindings for the token (with sync deactivation) then starts a fresh builder.
26
- */
27
- rebind<Value>(token: Token<Value> | Constructor<Value>): BindingBuilder<Value>;
28
- /**
29
- * Removes all bindings for a token or a single binding by its {@link BindingIdentifier}; runs sync deactivation.
30
- */
31
- unbind(tokenOrId: RegistryKey | BindingIdentifier): void;
32
- /**
33
- * Same as {@link unbind} but awaits async `onDeactivation` handlers before removing.
34
- */
35
- unbindAsync(tokenOrId: RegistryKey | BindingIdentifier): Promise<void>;
36
- /**
37
- * Returns `true` if at least one binding exists for `token`, optionally filtered by `hint`.
38
- */
39
- has(token: RegistryKey, hint?: ResolveHint): boolean;
40
- /**
41
- * Resolves the token synchronously. Throws {@link AsyncResolutionError} if any binding in the chain is async.
42
- */
43
- resolve<Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveHint): Value;
44
- /**
45
- * Resolves the token, awaiting any async factory in the chain. Safe for both sync and async bindings.
46
- */
47
- resolveAsync<Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveHint): Promise<Value>;
48
- /**
49
- * Resolves all bindings registered for the token (multi-binding). Throws {@link AsyncResolutionError} if any is async.
50
- */
51
- resolveAll<Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveHint): Value[];
52
- /**
53
- * Async variant of {@link resolveAll} — safe when the multi-binding set contains async factories.
54
- */
55
- resolveAllAsync<Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveHint): Promise<Value[]>;
56
- /**
57
- * Resolves the token or returns `undefined` if no binding is registered (never throws on missing).
58
- */
59
- resolveOptional<Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveHint): Value | undefined;
60
- /**
61
- * Registers bindings from one or more synchronous modules. Re-loading a module already present is a no-op.
62
- */
63
- load(...modules: Module[]): void;
64
- /**
65
- * Registers bindings from sync and/or async modules, awaiting each async setup in sequence.
66
- */
67
- loadAsync(...modules: ModuleLike[]): Promise<void>;
68
- /**
69
- * Removes all bindings contributed by the given modules; runs sync deactivation on released singletons.
70
- */
71
- unload(...modules: ModuleLike[]): void;
72
- /**
73
- * Same as {@link unload} but awaits async `onDeactivation` handlers.
74
- */
75
- unloadAsync(...modules: ModuleLike[]): Promise<void>;
76
- /**
77
- * Eagerly constructs every singleton binding so the first request is never cold.
78
- */
11
+ interface Container {
12
+ readonly isDisposed: boolean;
13
+ bind<const Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
14
+ unbind(tokenOrId: Token<unknown> | Constructor | BindingIdentifier): void;
15
+ unbindAsync(tokenOrId: Token<unknown> | Constructor | BindingIdentifier): Promise<void>;
16
+ unbindAll(): void;
17
+ unbindAllAsync(): Promise<void>;
18
+ rebind<const Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
19
+ load(...modules: SyncModule[]): void;
20
+ loadAsync(...modules: Array<SyncModule | AsyncModule>): Promise<void>;
21
+ unload(...modules: SyncModule[]): void;
22
+ unloadAsync(...modules: Array<SyncModule | AsyncModule>): Promise<void>;
23
+ loadAutoRegistered(registry: AutoRegisterRegistry): number;
24
+ onActivation<const Value>(token: Token<Value> | Constructor<Value>, handler: ActivationHandler<Value>): void;
25
+ onDeactivation<const Value>(token: Token<Value> | Constructor<Value>, handler: DeactivationHandler<Value>): void;
26
+ resolve<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Value;
27
+ resolveAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Value>;
28
+ resolveOptional<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Value | undefined;
29
+ resolveOptionalAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Value | undefined>;
30
+ resolveAll<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Value[];
31
+ resolveAllAsync<const Value>(token: Token<Value> | Constructor<Value>, hint?: ResolveOptions): Promise<Value[]>;
32
+ createChild(): Container;
33
+ dispose(): Promise<void>;
34
+ [Symbol.asyncDispose](): Promise<void>;
35
+ [Symbol.dispose](): never;
79
36
  initializeAsync(): Promise<void>;
80
- /**
81
- * Scans {@link getAutoRegistered} entries and binds each to its declared scope. Returns the count added.
82
- */
83
- loadAutoRegistered(): number;
84
- /**
85
- * Checks for scope violations (captive dependencies). Throws {@link ScopeViolationError} on the first violation found.
86
- */
87
37
  validate(): void;
88
- /**
89
- * Returns a debug snapshot of all registered bindings and their activation state.
90
- */
38
+ has(token: Token<unknown> | Constructor, hint?: ResolveOptions): boolean;
39
+ hasOwn(token: Token<unknown> | Constructor, hint?: ResolveOptions): boolean;
40
+ lookupBindings<const Value>(token: Token<Value> | Constructor<Value>): readonly BindingSnapshot[];
91
41
  inspect(): ContainerSnapshot;
92
- /**
93
- * Returns the canonical dependency graph as typed JSON (`nodes` + `edges`).
94
- */
95
42
  generateDependencyGraph(options?: GraphOptions): ContainerGraphJson;
96
- /**
97
- * Creates a child container that inherits bindings from this container without polluting its registry.
98
- */
99
- createChild(): Container;
100
- /**
101
- * @throws Always — container disposal is async; use `await using` or `await container.dispose()`.
102
- */
103
- [Symbol.dispose](): never;
104
- /**
105
- * Returns the raw binding list for a token without triggering resolution. `undefined` means no binding.
106
- */
107
- lookupBindings(token: RegistryKey): readonly Binding<unknown>[] | undefined;
108
- /**
109
- * Runs all `onDeactivation` hooks on active singletons and releases all caches.
110
- */
111
- dispose(): Promise<void>;
112
- [Symbol.asyncDispose](): Promise<void>;
113
43
  }
114
- /**
115
- * Factory functions for {@link Container} instances (interface + namespace merge).
116
- */
117
- declare namespace Container {
118
- /**
119
- * Creates an empty container with no bindings.
120
- */
121
- function create(): Container;
122
- /**
123
- * Creates a container and immediately loads the given sync modules.
124
- */
125
- function fromModules(...modules: Module[]): Container;
126
- /**
127
- * Creates a container and awaits loading of sync and/or async modules.
128
- */
129
- function fromModulesAsync(...modules: (Module | AsyncModule)[]): Promise<Container>;
44
+ interface ContainerStatic {
45
+ create(): Container;
46
+ fromModules(...modules: SyncModule[]): Container;
47
+ fromModulesAsync(...modules: Array<SyncModule | AsyncModule>): Promise<Container>;
130
48
  }
49
+ declare const Container: ContainerStatic & {
50
+ create(): Container;
51
+ };
131
52
  //#endregion
132
- export { Container };
53
+ export { Container, ContainerStatic };