@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,79 +1,30 @@
1
+ import { Constructor } from "../constructor-type.mjs";
1
2
  import { Token } from "../token.mjs";
2
- import { Constructor } from "../binding.mjs";
3
3
 
4
4
  //#region src/metadata/metadata-types.d.ts
5
- /**
6
- * Metadata written per `accessor` field decorated with `@inject`; collected into `Symbol.metadata`.
7
- */
8
- type AccessorInjectionMetadata = {
9
- /** Accessor property name to inject after construction. */readonly name: string; /** Token/constructor resolved for this accessor. */
10
- readonly token: Token<unknown> | Constructor<unknown>; /** Whether missing binding resolves to `undefined` instead of throwing. */
11
- readonly optional: boolean; /** Optional name/tag filter forwarded to binding selection. */
12
- readonly resolveHint?: {
13
- /** Named-binding discriminator (`whenNamed`). */readonly name?: string; /** Tagged-binding discriminator (`whenTagged`). */
14
- readonly tag?: readonly [tag: string, value: unknown];
15
- };
16
- };
17
- /**
18
- * Lifecycle method names written by `@postConstruct()` / `@preDestroy()` into `Symbol.metadata`.
19
- */
20
- type LifecycleMetadata = {
21
- /** Method name marked with `@postConstruct()`. */readonly postConstruct?: string; /** Method name marked with `@preDestroy()`. */
22
- readonly preDestroy?: string;
23
- };
24
- /**
25
- * Per-parameter injection description collected by `@injectable()`.
26
- */
27
- type ParamMetadata = {
28
- /** Zero-based constructor parameter index. */readonly index: number; /** Token/constructor used to resolve this parameter. */
29
- readonly token: Token<unknown> | Constructor<unknown>; /** Whether missing binding resolves to `undefined`. */
30
- readonly optional: boolean; /** Optional named-binding discriminator. */
31
- readonly name?: string; /** Optional tagged-binding discriminator. */
32
- readonly tag?: readonly [tag: string, value: unknown];
33
- /**
34
- * When true, the parameter receives every binding for `token` as an array (same semantics as
35
- * `Container.resolveAll` / `ResolutionContext.resolveAll`), using `name` / `tag` as a filter when set.
36
- */
37
- readonly all?: boolean;
38
- };
39
- /**
40
- * Resolved form of an `inject()` / `optional()` call: token + optional flag + optional resolve hint.
41
- * Used both as a deps-array entry in `@injectable()` and as accessor-field injection metadata.
42
- */
43
- type InjectionDescriptor<Value = unknown> = {
44
- /** Token/constructor to resolve. */readonly token: Token<Value> | Constructor<Value>; /** Whether unbound token should resolve as `undefined`. */
45
- readonly optional: boolean; /** Optional named-binding discriminator. */
46
- readonly name?: string; /** Optional tagged-binding discriminator. */
47
- readonly tag?: readonly [tag: string, value: unknown]; /** When true, resolve every binding for {@link InjectionDescriptor.token} into an array. */
48
- readonly all?: boolean;
49
- };
50
- /**
51
- * Constructor injection shape stored on the class `Symbol.metadata` object.
52
- */
53
- type ConstructorMetadata = {
54
- /** Ordered constructor dependency descriptors. */readonly params: readonly ParamMetadata[];
55
- };
56
- /**
57
- * Abstraction for reading DI metadata without tying callers to `Symbol.metadata` directly.
58
- * The {@link DependencyResolver} uses this to instantiate `class` bindings and read lifecycle hooks.
59
- *
60
- * The default implementation is {@link SymbolMetadataReader}; consumers can supply a custom
61
- * reader (e.g. backed by a static config object) via `ResolverDependencies.metadataReader`.
62
- */
63
- type MetadataReader = {
64
- /**
65
- * Returns constructor parameter injection metadata for `implementationClass`, or `undefined`
66
- * if the class has no own `@injectable()` metadata. When a {@link MetadataReader} is
67
- * configured on the container, `undefined` here combined with `arity > 0` on the class
68
- * causes {@link MissingMetadataError} during resolution; when no reader is configured, the
69
- * resolver instantiates with zero arguments instead (see `DependencyResolver` class binding path).
70
- */
71
- getConstructorMetadata(implementationClass: Constructor<unknown>): ConstructorMetadata | undefined;
72
- /**
73
- * Returns lifecycle method names (`@postConstruct` / `@preDestroy`), or `undefined` if none.
74
- * Optional: when absent the resolver skips lifecycle hooks entirely.
75
- */
76
- getLifecycleMetadata?(implementationClass: Constructor<unknown>): LifecycleMetadata | undefined;
77
- };
5
+ interface ParamMetadata {
6
+ readonly index: number;
7
+ readonly token: Token<unknown> | Constructor;
8
+ readonly optional: boolean;
9
+ readonly multi: boolean;
10
+ readonly name?: string;
11
+ readonly tags?: ReadonlyArray<readonly [string, unknown]>;
12
+ }
13
+ interface ConstructorMetadata {
14
+ readonly params: readonly ParamMetadata[];
15
+ }
16
+ interface LifecycleMetadata {
17
+ readonly postConstruct: readonly string[];
18
+ readonly preDestroy: readonly string[];
19
+ }
20
+ /** Mutable buckets used while aggregating decorator metadata (same keys as {@link LifecycleMetadata}). */
21
+ interface MutableLifecycleMetadata {
22
+ postConstruct: string[];
23
+ preDestroy: string[];
24
+ }
25
+ interface MetadataReader {
26
+ getConstructorMetadata(target: Constructor): ConstructorMetadata | undefined;
27
+ getLifecycleMetadata(target: Constructor): LifecycleMetadata | undefined;
28
+ }
78
29
  //#endregion
79
- export { AccessorInjectionMetadata, ConstructorMetadata, InjectionDescriptor, LifecycleMetadata, MetadataReader, ParamMetadata };
30
+ export { ConstructorMetadata, LifecycleMetadata, MetadataReader, MutableLifecycleMetadata, ParamMetadata };
@@ -1,32 +1,16 @@
1
- import { Constructor } from "../binding.mjs";
1
+ import { Constructor } from "../constructor-type.mjs";
2
+ import { InjectionDescriptor } from "../decorators/inject.mjs";
2
3
  import { ConstructorMetadata, LifecycleMetadata, MetadataReader } from "./metadata-types.mjs";
3
4
 
4
5
  //#region src/metadata/symbol-metadata-reader.d.ts
5
- /**
6
- * Default {@link MetadataReader} implementation backed by TC39 `Symbol.metadata`.
7
- *
8
- * Constructor metadata (`@injectable()`) is read with `Object.hasOwn` to prevent
9
- * a subclass from silently inheriting a parent's dependency list — each class must
10
- * declare its own `@injectable()` decorator or be constructed with zero arguments.
11
- *
12
- * Lifecycle metadata (`@postConstruct` / `@preDestroy`) *is* inherited through the
13
- * prototype chain, matching the intent that a parent's lifecycle hook applies to children.
14
- */
15
6
  declare class SymbolMetadataReader implements MetadataReader {
16
- /**
17
- * Reads constructor param metadata written by `@injectable()`.
18
- * Returns `undefined` if no own metadata is present on the class.
19
- *
20
- * @remarks
21
- * Uses `Object.hasOwn` intentionally — TC39 `Symbol.metadata` prototype-chains from
22
- * parent to child, so without this guard a subclass without `@injectable()` would
23
- * silently inherit the parent's parameter list and inject the wrong dependency count.
24
- */
25
- getConstructorMetadata(implementationClass: Constructor<unknown>): ConstructorMetadata | undefined;
26
- /**
27
- * Reads lifecycle method names written by `@postConstruct()` / `@preDestroy()`. Inherits from parent classes.
28
- */
29
- getLifecycleMetadata(implementationClass: Constructor<unknown>): LifecycleMetadata | undefined;
7
+ getConstructorMetadata(target: Constructor): ConstructorMetadata | undefined;
8
+ getLifecycleMetadata(target: Constructor): LifecycleMetadata | undefined;
9
+ getAccessorMetadata(target: Constructor): Array<{
10
+ key: string | symbol;
11
+ descriptor: InjectionDescriptor;
12
+ }> | undefined;
30
13
  }
14
+ declare const defaultMetadataReader: SymbolMetadataReader;
31
15
  //#endregion
32
- export { SymbolMetadataReader };
16
+ export { SymbolMetadataReader, defaultMetadataReader };
@@ -1,51 +1,38 @@
1
- import { CODEFAST_DI_CONSTRUCTOR_METADATA, CODEFAST_DI_LIFECYCLE_METADATA, decoratorMetadataObjectSymbol } from "./metadata-keys.mjs";
1
+ import { INJECTABLE_KEY, INJECT_ACCESSOR_KEY, LIFECYCLE_KEY, constructorMetadataMap, lifecycleByConstructorMetadataMap, lifecycleMetadataMap } from "./metadata-keys.mjs";
2
2
  //#region src/metadata/symbol-metadata-reader.ts
3
- /**
4
- * Type guard — returns `true` when `value` has the shape of a {@link ConstructorMetadata} object.
5
- */
6
- function isConstructorMetadata(value) {
7
- if (typeof value !== "object" || value === null || !("params" in value)) return false;
8
- return Array.isArray(value.params);
9
- }
10
- /**
11
- * Default {@link MetadataReader} implementation backed by TC39 `Symbol.metadata`.
12
- *
13
- * Constructor metadata (`@injectable()`) is read with `Object.hasOwn` to prevent
14
- * a subclass from silently inheriting a parent's dependency list — each class must
15
- * declare its own `@injectable()` decorator or be constructed with zero arguments.
16
- *
17
- * Lifecycle metadata (`@postConstruct` / `@preDestroy`) *is* inherited through the
18
- * prototype chain, matching the intent that a parent's lifecycle hook applies to children.
19
- */
20
3
  var SymbolMetadataReader = class {
21
- /**
22
- * Reads constructor param metadata written by `@injectable()`.
23
- * Returns `undefined` if no own metadata is present on the class.
24
- *
25
- * @remarks
26
- * Uses `Object.hasOwn` intentionally — TC39 `Symbol.metadata` prototype-chains from
27
- * parent to child, so without this guard a subclass without `@injectable()` would
28
- * silently inherit the parent's parameter list and inject the wrong dependency count.
29
- */
30
- getConstructorMetadata(implementationClass) {
31
- const rawMetadata = implementationClass[decoratorMetadataObjectSymbol()];
32
- if (typeof rawMetadata !== "object" || rawMetadata === null) return;
33
- const metadataObject = rawMetadata;
34
- if (!Object.hasOwn(metadataObject, "codefast/di:constructor-metadata:v1")) return;
35
- const raw = metadataObject[CODEFAST_DI_CONSTRUCTOR_METADATA];
36
- if (!isConstructorMetadata(raw)) return;
37
- return raw;
4
+ getConstructorMetadata(target) {
5
+ const fromWeakMap = constructorMetadataMap.get(target);
6
+ if (fromWeakMap !== void 0) return fromWeakMap;
7
+ const own = Object.getOwnPropertyDescriptor(target, Symbol.metadata);
8
+ if (own === void 0) return;
9
+ const meta = own.value;
10
+ if (!meta || typeof meta !== "object" || !Object.hasOwn(meta, INJECTABLE_KEY)) return;
11
+ return meta[INJECTABLE_KEY];
38
12
  }
39
- /**
40
- * Reads lifecycle method names written by `@postConstruct()` / `@preDestroy()`. Inherits from parent classes.
41
- */
42
- getLifecycleMetadata(implementationClass) {
43
- const metadataObject = implementationClass[decoratorMetadataObjectSymbol()];
44
- if (typeof metadataObject !== "object" || metadataObject === null) return;
45
- const raw = metadataObject[CODEFAST_DI_LIFECYCLE_METADATA];
46
- if (typeof raw !== "object" || raw === null) return;
47
- return raw;
13
+ getLifecycleMetadata(target) {
14
+ const byConstructor = lifecycleByConstructorMetadataMap.get(target);
15
+ if (byConstructor !== void 0) return byConstructor;
16
+ const own = Object.getOwnPropertyDescriptor(target, Symbol.metadata);
17
+ if (own !== void 0) {
18
+ const meta = own.value;
19
+ if (meta && typeof meta === "object" && Object.hasOwn(meta, LIFECYCLE_KEY)) return meta[LIFECYCLE_KEY];
20
+ }
21
+ const classMeta = target[Symbol.metadata];
22
+ if (classMeta !== void 0) {
23
+ const fromWeakMap = lifecycleMetadataMap.get(classMeta);
24
+ if (fromWeakMap !== void 0) return fromWeakMap;
25
+ }
26
+ }
27
+ getAccessorMetadata(target) {
28
+ const own = Object.getOwnPropertyDescriptor(target, Symbol.metadata);
29
+ if (own === void 0) return;
30
+ const meta = own.value;
31
+ if (!meta || typeof meta !== "object") return;
32
+ if (!Object.hasOwn(meta, INJECT_ACCESSOR_KEY)) return;
33
+ return meta[INJECT_ACCESSOR_KEY];
48
34
  }
49
35
  };
36
+ const defaultMetadataReader = new SymbolMetadataReader();
50
37
  //#endregion
51
- export { SymbolMetadataReader };
38
+ export { SymbolMetadataReader, defaultMetadataReader };
package/dist/module.d.mts CHANGED
@@ -1,104 +1,38 @@
1
+ import { Constructor } from "./constructor-type.mjs";
1
2
  import { Token } from "./token.mjs";
2
- import { BindingBuilder, Constructor } from "./binding.mjs";
3
+ import { BindToBuilder } from "./binding.mjs";
3
4
 
4
5
  //#region src/module.d.ts
5
- /**
6
- * Builder passed to the setup callback of a synchronous {@link Module}.
7
- * Use `import()` to declare module dependencies (sync modules only —
8
- * passing an {@link AsyncModule} throws {@link InternalError}) and `bind()` to register tokens.
9
- *
10
- * **Single slot (last-wins)** — `bind(key).to*(...)` with no `whenNamed` / `whenTagged` / `when`
11
- * *before* the `to*()` call replaces all prior bindings for `key` from this module pass.
12
- *
13
- * **Multi-binding** — put at least one disambiguator *before* `to*()` (e.g.
14
- * `bind(key).whenNamed("a").to*(...)`, `whenTagged` before `to*()`, or `when` before `to*()`).
15
- * Each such line **appends** another binding so {@link Container.resolveAll} can return every
16
- * implementation. Use this order in modules; chaining `.to*(...).whenNamed()` only updates that
17
- * binding in place and does not stack multiple registrations across lines.
18
- */
19
- type ModuleBuilder = {
20
- /**
21
- * Declares synchronous module dependencies to load before/alongside current setup.
22
- */
23
- readonly import: (...modules: Module[]) => void;
24
- /**
25
- * Starts binding registration for a token/constructor within this module setup pass.
26
- */
27
- readonly bind: <Value>(key: Token<Value> | Constructor<Value>) => BindingBuilder<Value>;
28
- };
29
- /**
30
- * Builder passed to the setup callback of an {@link AsyncModule}.
31
- * Unlike {@link ModuleBuilder}, `import()` accepts both sync and async modules.
32
- * Async sub-imports are collected and awaited **after** the setup callback returns.
33
- */
34
- type AsyncModuleBuilder = {
35
- /**
36
- * Declares sync/async module dependencies to be loaded by the async module loader.
37
- */
38
- readonly import: (...modules: (Module | AsyncModule)[]) => void;
39
- /**
40
- * Starts binding registration for a token/constructor within this async module setup pass.
41
- */
42
- readonly bind: <Value>(key: Token<Value> | Constructor<Value>) => BindingBuilder<Value>;
43
- };
44
- /**
45
- * Immutable description of a bundle of bindings. A {@link Module} holds no runtime state and the
46
- * same instance may be loaded into any number of containers independently (spec §7.3).
47
- *
48
- * The owning container is responsible for tracking which modules have been loaded and which
49
- * binding ids each module produced; the module itself never sees a container reference.
50
- */
51
- declare class Module {
52
- /**
53
- * Human-readable label used in error messages, graph output, and module-cycle diagnostics.
54
- */
6
+ declare const SYNC_MODULE_BRAND: unique symbol;
7
+ declare const ASYNC_MODULE_BRAND: unique symbol;
8
+ interface SyncModule {
55
9
  readonly name: string;
56
- /**
57
- * The user-supplied setup callback; invoked exactly once per `load()` call.
58
- */
59
- private readonly syncSetup;
60
- /**
61
- * @internal Use {@link Module.create} instead.
62
- */
63
- private constructor();
64
- /**
65
- * Defines a synchronous module.
66
- * @param name - Human-readable label used in error messages and debug output.
67
- * @param setup - Callback that registers bindings via the {@link ModuleBuilder}.
68
- */
69
- static create(name: string, setup: (builder: ModuleBuilder) => void): Module;
70
- /**
71
- * Defines an async module — use when setup requires awaiting (e.g. reading config, dynamic imports).
72
- * Load with {@link Container.loadAsync} or {@link Container.fromModulesAsync}.
73
- */
74
- static createAsync(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
75
- /**
76
- * @internal Invoked by the container while loading this module.
77
- */
78
- runSyncSetup(builder: ModuleBuilder): void;
10
+ readonly [SYNC_MODULE_BRAND]: true;
11
+ readonly _setup: (builder: ModuleBuilder) => void;
79
12
  }
80
- /**
81
- * An async module whose setup callback may `await` before registering bindings.
82
- * Prefer {@link Module.createAsync} over constructing this class directly.
83
- *
84
- * Load via `Container.loadAsync()` or `Container.fromModulesAsync()`;
85
- * passing an `AsyncModule` to the synchronous `Container.load()` throws
86
- * {@link AsyncModuleLoadError}.
87
- */
88
- declare class AsyncModule {
89
- /**
90
- * Human-readable label used in error messages and graph output.
91
- */
13
+ interface AsyncModule {
92
14
  readonly name: string;
93
- /**
94
- * The user-supplied async setup callback; invoked exactly once per `loadAsync()` call.
95
- */
96
- private readonly asyncSetup;
97
- constructor(name: string, asyncSetup: (builder: AsyncModuleBuilder) => Promise<void>);
98
- /**
99
- * @internal Invoked by the container while loading this module.
100
- */
101
- runAsyncSetup(builder: AsyncModuleBuilder): Promise<void>;
15
+ readonly [ASYNC_MODULE_BRAND]: true;
16
+ readonly _setup: (builder: AsyncModuleBuilder) => Promise<void>;
17
+ }
18
+ interface ModuleBuilder {
19
+ bind<const Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
20
+ import(...modules: SyncModule[]): void;
21
+ }
22
+ interface AsyncModuleBuilder {
23
+ bind<const Value>(token: Token<Value> | Constructor<Value>): BindToBuilder<Value>;
24
+ import(...modules: Array<SyncModule | AsyncModule>): void;
102
25
  }
26
+ declare const SyncModule: {
27
+ create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule;
28
+ };
29
+ declare const AsyncModule: {
30
+ create(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
31
+ };
32
+ declare const Module: {
33
+ create(name: string, setup: (builder: ModuleBuilder) => void): SyncModule;
34
+ createAsync(name: string, setup: (builder: AsyncModuleBuilder) => Promise<void>): AsyncModule;
35
+ };
36
+ declare function isSyncModule(m: SyncModule | AsyncModule): m is SyncModule;
103
37
  //#endregion
104
- export { AsyncModule, AsyncModuleBuilder, Module, ModuleBuilder };
38
+ export { AsyncModule, AsyncModuleBuilder, Module, ModuleBuilder, SyncModule, isSyncModule };
package/dist/module.mjs CHANGED
@@ -1,76 +1,30 @@
1
1
  //#region src/module.ts
2
- /**
3
- * Immutable description of a bundle of bindings. A {@link Module} holds no runtime state and the
4
- * same instance may be loaded into any number of containers independently (spec §7.3).
5
- *
6
- * The owning container is responsible for tracking which modules have been loaded and which
7
- * binding ids each module produced; the module itself never sees a container reference.
8
- */
9
- var Module = class Module {
10
- /**
11
- * Human-readable label used in error messages, graph output, and module-cycle diagnostics.
12
- */
13
- name;
14
- /**
15
- * The user-supplied setup callback; invoked exactly once per `load()` call.
16
- */
17
- syncSetup;
18
- /**
19
- * @internal Use {@link Module.create} instead.
20
- */
21
- constructor(name, syncSetup) {
22
- this.name = name;
23
- this.syncSetup = syncSetup;
24
- }
25
- /**
26
- * Defines a synchronous module.
27
- * @param name - Human-readable label used in error messages and debug output.
28
- * @param setup - Callback that registers bindings via the {@link ModuleBuilder}.
29
- */
30
- static create(name, setup) {
31
- return new Module(name, setup);
32
- }
33
- /**
34
- * Defines an async module — use when setup requires awaiting (e.g. reading config, dynamic imports).
35
- * Load with {@link Container.loadAsync} or {@link Container.fromModulesAsync}.
36
- */
37
- static createAsync(name, setup) {
38
- return new AsyncModule(name, setup);
39
- }
40
- /**
41
- * @internal Invoked by the container while loading this module.
42
- */
43
- runSyncSetup(builder) {
44
- this.syncSetup(builder);
45
- }
46
- };
47
- /**
48
- * An async module whose setup callback may `await` before registering bindings.
49
- * Prefer {@link Module.createAsync} over constructing this class directly.
50
- *
51
- * Load via `Container.loadAsync()` or `Container.fromModulesAsync()`;
52
- * passing an `AsyncModule` to the synchronous `Container.load()` throws
53
- * {@link AsyncModuleLoadError}.
54
- */
55
- var AsyncModule = class {
56
- /**
57
- * Human-readable label used in error messages and graph output.
58
- */
59
- name;
60
- /**
61
- * The user-supplied async setup callback; invoked exactly once per `loadAsync()` call.
62
- */
63
- asyncSetup;
64
- constructor(name, asyncSetup) {
65
- this.name = name;
66
- this.asyncSetup = asyncSetup;
67
- }
68
- /**
69
- * @internal Invoked by the container while loading this module.
70
- */
71
- async runAsyncSetup(builder) {
72
- await this.asyncSetup(builder);
2
+ const SYNC_MODULE_BRAND = Symbol("di:sync-module");
3
+ const ASYNC_MODULE_BRAND = Symbol("di:async-module");
4
+ const SyncModule = { create(name, setup) {
5
+ return {
6
+ name,
7
+ [SYNC_MODULE_BRAND]: true,
8
+ _setup: setup
9
+ };
10
+ } };
11
+ const AsyncModule = { create(name, setup) {
12
+ return {
13
+ name,
14
+ [ASYNC_MODULE_BRAND]: true,
15
+ _setup: setup
16
+ };
17
+ } };
18
+ const Module = {
19
+ create(name, setup) {
20
+ return SyncModule.create(name, setup);
21
+ },
22
+ createAsync(name, setup) {
23
+ return AsyncModule.create(name, setup);
73
24
  }
74
25
  };
26
+ function isSyncModule(m) {
27
+ return m[SYNC_MODULE_BRAND] === true;
28
+ }
75
29
  //#endregion
76
- export { AsyncModule, Module };
30
+ export { AsyncModule, Module, SyncModule, isSyncModule };
@@ -1,69 +1,38 @@
1
+ import { Constructor } from "./constructor-type.mjs";
1
2
  import { Token } from "./token.mjs";
2
- import { Binding, BindingIdentifier, Constructor } from "./binding.mjs";
3
+ import { BindingIdentifier } from "./types.mjs";
4
+ import { Binding } from "./binding.mjs";
3
5
 
4
6
  //#region src/registry.d.ts
5
- /**
6
- * Key used to group {@link Binding} instances in the registry (reference equality for tokens).
7
- */
8
- type RegistryKey = Token<unknown> | Constructor<unknown>;
9
- /**
10
- * Flat, in-memory storage for {@link Binding} entries keyed by {@link RegistryKey}.
11
- * Each key maps to an ordered list of bindings (multi-binding support).
12
- *
13
- * The registry is a "dumb" store — it does not perform selection, scope caching, or
14
- * lifecycle management. Those concerns live in `DependencyResolver` and `ScopeManager`.
15
- *
16
- * Mutation styles:
17
- * - `add` — append-only; never removes existing entries.
18
- * - `replaceById` — swaps a single binding in place by ID; no removal callback.
19
- * - `remove` / `removeById` — delete entries **without** notifying callers; the caller
20
- * is responsible for draining the scope cache before calling these.
21
- * - `replaceKeyLastWins` — replaces all bindings for a key with a single new one **and**
22
- * invokes the `onReplaced` callback for each evicted binding, giving the caller
23
- * (typically the container or scope manager) a chance to run deactivation.
24
- */
25
7
  declare class BindingRegistry {
26
- /**
27
- * Primary index: registry key → ordered binding list.
28
- * Reference equality on the key (i.e. the same {@link Token} or {@link Constructor} object).
29
- */
30
- private readonly bindingsByKey;
31
- /**
32
- * Appends `binding` to the list for `key` (multi-binding: each call adds an entry).
33
- */
34
- add<Value>(key: Token<Value> | Constructor<Value>, binding: Binding<Value>): void;
35
- /**
36
- * Returns all bindings registered for `key`, or `undefined` if none exist.
37
- */
38
- get<Value>(key: Token<Value> | Constructor<Value>): readonly Binding<Value>[] | undefined;
39
- /**
40
- * Removes all bindings for `key`. Does **not** invoke any callback — the caller must
41
- * drain the scope cache (run deactivation) for the affected bindings before calling this.
42
- */
43
- remove(key: RegistryKey): void;
44
- /**
45
- * Returns owned registry rows (does not include parent containers).
46
- */
47
- listEntries(): readonly {
48
- key: RegistryKey;
49
- bindings: readonly Binding<unknown>[];
50
- }[];
51
- /**
52
- * Removes the single binding whose `id` matches, scanning all keys.
53
- * Like {@link remove}, does **not** invoke a removal callback.
54
- */
55
- removeById(id: BindingIdentifier): void;
56
- /**
57
- * Swaps the binding with the given `id` in place, preserving its position in the list.
58
- */
59
- replaceById(id: BindingIdentifier, next: Binding<unknown>): void;
60
- /**
61
- * Replaces all bindings for `key` with a single new binding (module "last-wins" semantics).
62
- * Unlike {@link remove} / {@link removeById}, this method invokes `onReplaced` for every
63
- * evicted binding **before** inserting the replacement, giving the caller (e.g. the
64
- * container's scope manager) a chance to release cached instances and run deactivation hooks.
65
- */
66
- replaceKeyLastWins<Value>(key: Token<Value> | Constructor<Value>, binding: Binding<Value>, onReplaced: (removed: Binding<unknown>) => void): void;
8
+ private readonly _bindings;
9
+ private readonly _byId;
10
+ private readonly _simpleNamed;
11
+ private readonly _fastDefault;
12
+ /** Add or replace binding using slot-aware last-wins. */
13
+ add(binding: Binding): void;
14
+ /** Remove all bindings for a token. Returns removed bindings. */
15
+ removeByToken(t: Token<unknown> | Constructor): Binding[];
16
+ /** Remove a specific binding by ID. Returns the removed binding or undefined. */
17
+ removeById(id: BindingIdentifier): Binding | undefined;
18
+ /** Get all bindings for a token. */
19
+ getAll(t: Token<unknown> | Constructor): readonly Binding[];
20
+ /** Get binding by ID. */
21
+ getById(id: BindingIdentifier): Binding | undefined;
22
+ /** Check if any binding exists for token. */
23
+ has(t: Token<unknown> | Constructor): boolean;
24
+ /** All bindings in the registry. */
25
+ allBindings(): readonly Binding[];
26
+ /** Remove all bindings. Returns all removed. */
27
+ clear(): readonly Binding[];
28
+ getSimpleNamed(token: Token<unknown> | Constructor, name: string): Binding | undefined;
29
+ getFastDefault(token: Token<unknown> | Constructor): Binding | undefined;
30
+ /** Summarize available slot strings for a token (for error messages). */
31
+ availableSlotStrings(t: Token<unknown> | Constructor): string[];
32
+ private _isPurePredicateBinding;
33
+ private _indexSimpleNamedBinding;
34
+ private _deindexSimpleNamedBinding;
35
+ private _refreshFastDefaultForToken;
67
36
  }
68
37
  //#endregion
69
- export { BindingRegistry, RegistryKey };
38
+ export { BindingRegistry };