@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,96 +1,69 @@
1
- import { registryKeyLabel } from "./binding-select.mjs";
2
- import { collectStaticDependencyEdges } from "./dependency-graph.mjs";
1
+ import { effectiveBindingScope } from "./binding-scope.mjs";
2
+ import { selectBinding } from "./binding-select.mjs";
3
+ import { tokenName } from "./token.mjs";
3
4
  //#region src/inspector.ts
4
- /**
5
- * Maps a binding's scope and cache state to a {@link BindingActivationStatus} label.
6
- */
7
- function activationStatusFor(binding, isCached) {
8
- if (binding.scope === "transient") return "transient";
9
- return isCached(binding) ? "cached" : "not-cached";
10
- }
11
- /**
12
- * Returns `true` when the label string belongs to a framework-internal registry key.
13
- */
14
- function registryKeyLabelIsInternal(label) {
15
- return label.startsWith("CODEFAST_DI_");
16
- }
17
- /**
18
- * Returns `true` when the registry key resolves to an internal framework label.
19
- */
20
- function isInternalRegistryKey(key) {
21
- return registryKeyLabelIsInternal(registryKeyLabel(key));
22
- }
23
- /**
24
- * Reads the container's registry and scope-cache state to produce debug snapshots
25
- * and canonical dependency-graph output (`nodes` + `edges`).
26
- *
27
- * Constructed internally by the container; advanced consumers can also construct it
28
- * directly via the `@codefast/di/inspector` subpath export.
29
- */
30
- var ContainerInspector = class {
31
- constructor(ctx) {
32
- this.ctx = ctx;
5
+ var Inspector = class {
6
+ constructor(_registry, _scope, _hasParent, _isDisposed) {
7
+ this._registry = _registry;
8
+ this._scope = _scope;
9
+ this._hasParent = _hasParent;
10
+ this._isDisposed = _isDisposed;
33
11
  }
34
- /**
35
- * Collects all registered bindings into a flat, serialisable snapshot.
36
- */
37
- getSnapshot() {
38
- const bindings = [];
39
- const seen = /* @__PURE__ */ new Set();
40
- for (const registryKey of this.ctx.collectAllRegistryKeys()) {
41
- const list = this.ctx.lookupBindings(registryKey);
42
- if (list === void 0 || list.length === 0) continue;
43
- const registryLabel = registryKeyLabel(registryKey);
44
- for (const binding of list) {
45
- if (seen.has(binding.id)) continue;
46
- seen.add(binding.id);
47
- const row = {
48
- registryKeyLabel: registryLabel,
49
- bindingId: binding.id,
50
- kind: binding.kind,
51
- scope: binding.scope,
52
- activationStatus: activationStatusFor(binding, (bindingArg) => this.ctx.isBindingCached(bindingArg)),
53
- hasConditionalConstraint: binding.constraint !== void 0
54
- };
55
- bindings.push(binding.moduleId === void 0 ? row : {
56
- ...row,
57
- moduleId: binding.moduleId
58
- });
59
- }
60
- }
61
- return { bindings };
12
+ inspect() {
13
+ const snapshots = this.allBindingSnapshots();
14
+ return {
15
+ ownBindings: snapshots,
16
+ bindings: snapshots,
17
+ cachedSingletonCount: this._scope.getAllSingletons().size,
18
+ hasParent: this._hasParent,
19
+ isDisposed: this._isDisposed()
20
+ };
21
+ }
22
+ lookupBindings(token) {
23
+ return this._registry.getAll(token).map((b) => this._toSnapshot(b));
24
+ }
25
+ has(token, hint, parentHas) {
26
+ const bindings = this._registry.getAll(token);
27
+ if (bindings.length > 0) if (hint !== void 0) {
28
+ if (selectBinding(bindings, hint, {
29
+ resolutionPath: [],
30
+ materializationStack: [],
31
+ parent: void 0,
32
+ ancestors: [],
33
+ currentResolveHint: hint
34
+ }, tokenName(token)) !== void 0) return true;
35
+ } else return true;
36
+ return parentHas?.() ?? false;
37
+ }
38
+ hasOwn(token, hint) {
39
+ const bindings = this._registry.getAll(token);
40
+ if (bindings.length === 0) return false;
41
+ if (hint !== void 0) return selectBinding(bindings, hint, {
42
+ resolutionPath: [],
43
+ materializationStack: [],
44
+ parent: void 0,
45
+ ancestors: [],
46
+ currentResolveHint: hint
47
+ }, tokenName(token)) !== void 0;
48
+ return true;
49
+ }
50
+ allBindingSnapshots() {
51
+ return this._registry.allBindings().map((b) => this._toSnapshot(b));
62
52
  }
63
- /**
64
- * Builds the canonical JSON graph (`nodes` + `edges`).
65
- */
66
- generateDependencyGraph(options) {
67
- const hideInternals = options?.hideInternals === true;
68
- const snapshot = this.getSnapshot();
69
- const visibleNodes = hideInternals ? snapshot.bindings.filter((row) => !registryKeyLabelIsInternal(row.registryKeyLabel)) : [...snapshot.bindings];
70
- const allowedBindingIds = new Set(visibleNodes.map((row) => row.bindingId));
71
- const edges = [];
72
- const edgeSeen = /* @__PURE__ */ new Set();
73
- for (const registryKey of this.ctx.collectAllRegistryKeys()) {
74
- if (hideInternals && isInternalRegistryKey(registryKey)) continue;
75
- const list = this.ctx.lookupBindings(registryKey);
76
- if (list === void 0) continue;
77
- const pathStart = [registryKeyLabel(registryKey)];
78
- for (const consumerBinding of list) {
79
- if (hideInternals && !allowedBindingIds.has(consumerBinding.id)) continue;
80
- for (const edge of collectStaticDependencyEdges(consumerBinding, (dependencyKey) => this.ctx.lookupBindings(dependencyKey), this.ctx.metadataReader, pathStart)) {
81
- if (hideInternals && (!allowedBindingIds.has(edge.fromBindingId) || !allowedBindingIds.has(edge.toBindingId))) continue;
82
- const edgeKey = `${edge.fromBindingId}->${edge.toBindingId}:${edge.edgeKind}:${edge.injectHintLabel ?? ""}`;
83
- if (edgeSeen.has(edgeKey)) continue;
84
- edgeSeen.add(edgeKey);
85
- edges.push(edge);
86
- }
87
- }
88
- }
53
+ _toSnapshot(b) {
54
+ const scope = effectiveBindingScope(b);
55
+ const slot = b.slot.name !== void 0 ? {
56
+ name: b.slot.name,
57
+ tags: b.slot.tags
58
+ } : { tags: b.slot.tags };
89
59
  return {
90
- nodes: visibleNodes,
91
- edges
60
+ tokenName: tokenName(b.token),
61
+ kind: b.kind,
62
+ scope,
63
+ slot,
64
+ id: b.id
92
65
  };
93
66
  }
94
67
  };
95
68
  //#endregion
96
- export { ContainerInspector };
69
+ export { Inspector };
@@ -1,56 +1,23 @@
1
- import { Binding, Constructor, ResolutionContext } from "./binding.mjs";
2
- import { LifecycleMetadata } from "./metadata/metadata-types.mjs";
1
+ import { Constructor } from "./constructor-type.mjs";
2
+ import { Token } from "./token.mjs";
3
+ import { ActivationHandler, DeactivationHandler, ResolutionContext } from "./types.mjs";
4
+ import { Binding } from "./binding.mjs";
5
+ import { MetadataReader } from "./metadata/metadata-types.mjs";
3
6
 
4
7
  //#region src/lifecycle.d.ts
5
- /**
6
- * Duck-typed Promise check: returns `true` when `value` has a `then` method.
7
- * Used throughout the lifecycle layer to guard against async return values on sync resolution paths.
8
- */
9
- declare function isPromiseLike(value: unknown): value is Promise<unknown>;
10
- /**
11
- * Runs `onActivation` synchronously on a newly constructed instance.
12
- * If the handler returns a Promise during synchronous resolution, throws
13
- * {@link AsyncResolutionError} — callers must use `resolveAsync` for async activation handlers.
14
- *
15
- * Lifecycle ordering: `construct` → `@postConstruct` → **`onActivation`** → cache.
16
- */
17
- declare function runActivation(binding: Binding<unknown>, instance: unknown, ctx: ResolutionContext, pathLabels: readonly string[]): unknown;
18
- /**
19
- * Reads lifecycle metadata ({@link LifecycleMetadata}) directly from a constructor's
20
- * `Symbol.metadata` object. Bypasses the {@link MetadataReader} abstraction —
21
- * used by the scope manager during deactivation when the reader is not available.
22
- */
23
- declare function readLifecycleMetadataFromCtor(implementationClass: Constructor<unknown>): LifecycleMetadata | undefined;
24
- /**
25
- * Runs the `@postConstruct()` method synchronously if present.
26
- * Throws {@link AsyncResolutionError} if the method returns a Promise — async lifecycle
27
- * methods require `resolveAsync`.
28
- *
29
- * Lifecycle ordering: `construct` → **`@postConstruct`** → `onActivation` → cache.
30
- */
31
- declare function runPostConstruct(implementationClass: Constructor<unknown>, instance: unknown, pathLabels?: string[]): void;
32
- /**
33
- * Runs the `@postConstruct()` method, awaiting if it returns a Promise.
34
- */
35
- declare function runPostConstructAsync(implementationClass: Constructor<unknown>, instance: unknown): Promise<void>;
36
- /**
37
- * Runs the `@preDestroy()` method synchronously if present.
38
- * Throws if the method returns a Promise — use `disposeAsync()` / `unloadAsync()` for async teardown.
39
- *
40
- * Lifecycle ordering: `onDeactivation` → **`@preDestroy`**.
41
- */
42
- declare function runPreDestroy(implementationClass: Constructor<unknown>, instance: unknown): void;
43
- /**
44
- * Runs the `@preDestroy()` method, awaiting if it returns a Promise.
45
- *
46
- * Lifecycle ordering: `onDeactivation` → **`@preDestroy`** (async variant).
47
- */
48
- declare function runPreDestroyAsync(implementationClass: Constructor<unknown>, instance: unknown): Promise<void>;
49
- /**
50
- * Runs `onActivation`, awaiting if the handler returns a Promise.
51
- *
52
- * Lifecycle ordering: `construct` → `@postConstruct` → **`onActivation`** (async variant) → cache.
53
- */
54
- declare function runActivationAsync(binding: Binding<unknown>, instance: unknown, ctx: ResolutionContext, _pathLabels: readonly string[]): Promise<unknown>;
8
+ declare class LifecycleManager {
9
+ private readonly _activationHooks;
10
+ private readonly _deactivationHooks;
11
+ private _activationVersion;
12
+ registerActivation<const Value>(t: Token<Value> | Constructor<Value>, handler: ActivationHandler<Value>): void;
13
+ hasActivationHandlers<const Value>(t: Token<Value> | Constructor<Value>): boolean;
14
+ get activationVersion(): number;
15
+ registerDeactivation<const Value>(t: Token<Value> | Constructor<Value>, handler: DeactivationHandler<Value>): void;
16
+ runActivation<const Value>(ctx: ResolutionContext, binding: Binding<Value>, instance: Value, metadataReader: MetadataReader): Promise<Value>;
17
+ runActivationSync<const Value>(ctx: ResolutionContext, binding: Binding<Value>, instance: Value, metadataReader: MetadataReader): Value;
18
+ runDeactivation<const Value>(binding: Binding<Value>, instance: Value, metadataReader: MetadataReader): Promise<void>;
19
+ runDeactivationSync<const Value>(binding: Binding<Value>, instance: Value, metadataReader: MetadataReader): void;
20
+ hasAsyncDeactivation<const Value>(binding: Binding<Value>, instance: Value, _metadataReader: MetadataReader): boolean;
21
+ }
55
22
  //#endregion
56
- export { isPromiseLike, readLifecycleMetadataFromCtor, runActivation, runActivationAsync, runPostConstruct, runPostConstructAsync, runPreDestroy, runPreDestroyAsync };
23
+ export { LifecycleManager };
@@ -1,102 +1,132 @@
1
- import { AsyncResolutionError } from "./errors.mjs";
2
- import { CODEFAST_DI_LIFECYCLE_METADATA, decoratorMetadataObjectSymbol } from "./metadata/metadata-keys.mjs";
1
+ import { AsyncDeactivationError } from "./errors.mjs";
2
+ import { tokenName } from "./token.mjs";
3
3
  //#region src/lifecycle.ts
4
- /**
5
- * Duck-typed Promise check: returns `true` when `value` has a `then` method.
6
- * Used throughout the lifecycle layer to guard against async return values on sync resolution paths.
7
- */
8
- function isPromiseLike(value) {
9
- return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
10
- }
11
- /**
12
- * Runs `onActivation` synchronously on a newly constructed instance.
13
- * If the handler returns a Promise during synchronous resolution, throws
14
- * {@link AsyncResolutionError} — callers must use `resolveAsync` for async activation handlers.
15
- *
16
- * Lifecycle ordering: `construct` → `@postConstruct` → **`onActivation`** → cache.
17
- */
18
- function runActivation(binding, instance, ctx, pathLabels) {
19
- const handler = binding.onActivation;
20
- if (handler === void 0) return instance;
21
- const bindingLabel = pathLabels[pathLabels.length - 1] ?? "(unknown)";
22
- const activationResult = handler(ctx, instance);
23
- if (isPromiseLike(activationResult)) throw new AsyncResolutionError(bindingLabel, pathLabels, "onActivation returned a Promise during synchronous resolution");
24
- return activationResult;
25
- }
26
- /**
27
- * Reads lifecycle metadata ({@link LifecycleMetadata}) directly from a constructor's
28
- * `Symbol.metadata` object. Bypasses the {@link MetadataReader} abstraction —
29
- * used by the scope manager during deactivation when the reader is not available.
30
- */
31
- function readLifecycleMetadataFromCtor(implementationClass) {
32
- const metadataObject = implementationClass[decoratorMetadataObjectSymbol()];
33
- if (typeof metadataObject !== "object" || metadataObject === null) return;
34
- const raw = metadataObject[CODEFAST_DI_LIFECYCLE_METADATA];
35
- return typeof raw === "object" && raw !== null ? raw : void 0;
36
- }
37
- /**
38
- * Runs the `@postConstruct()` method synchronously if present.
39
- * Throws {@link AsyncResolutionError} if the method returns a Promise — async lifecycle
40
- * methods require `resolveAsync`.
41
- *
42
- * Lifecycle ordering: `construct` → **`@postConstruct`** → `onActivation` → cache.
43
- */
44
- function runPostConstruct(implementationClass, instance, pathLabels) {
45
- const meta = readLifecycleMetadataFromCtor(implementationClass);
46
- if (meta?.postConstruct === void 0) return;
47
- const methodName = meta.postConstruct;
48
- const lifecycleMethod = instance[methodName];
49
- if (typeof lifecycleMethod !== "function") return;
50
- if (isPromiseLike(lifecycleMethod.call(instance))) {
51
- const labels = pathLabels ?? [];
52
- throw new AsyncResolutionError(labels[labels.length - 1] ?? "(unknown)", labels, `@postConstruct() "${methodName}" returned a Promise during synchronous resolution`);
4
+ var LifecycleManager = class {
5
+ _activationHooks = /* @__PURE__ */ new Map();
6
+ _deactivationHooks = /* @__PURE__ */ new Map();
7
+ _activationVersion = 0;
8
+ registerActivation(t, handler) {
9
+ this._activationVersion += 1;
10
+ let list = this._activationHooks.get(t);
11
+ if (list === void 0) {
12
+ list = [];
13
+ this._activationHooks.set(t, list);
14
+ }
15
+ list.push(handler);
53
16
  }
54
- }
55
- /**
56
- * Runs the `@postConstruct()` method, awaiting if it returns a Promise.
57
- */
58
- async function runPostConstructAsync(implementationClass, instance) {
59
- const meta = readLifecycleMetadataFromCtor(implementationClass);
60
- if (meta?.postConstruct === void 0) return;
61
- const lifecycleMethod = instance[meta.postConstruct];
62
- if (typeof lifecycleMethod !== "function") return;
63
- await lifecycleMethod.call(instance);
64
- }
65
- /**
66
- * Runs the `@preDestroy()` method synchronously if present.
67
- * Throws if the method returns a Promise — use `disposeAsync()` / `unloadAsync()` for async teardown.
68
- *
69
- * Lifecycle ordering: `onDeactivation` → **`@preDestroy`**.
70
- */
71
- function runPreDestroy(implementationClass, instance) {
72
- const meta = readLifecycleMetadataFromCtor(implementationClass);
73
- if (meta?.preDestroy === void 0) return;
74
- const methodName = meta.preDestroy;
75
- const lifecycleMethod = instance[methodName];
76
- if (typeof lifecycleMethod !== "function") return;
77
- if (isPromiseLike(lifecycleMethod.call(instance))) throw new Error(`@preDestroy() "${methodName}" returned a Promise during synchronous disposal; use disposeAsync() / unloadAsync().`);
78
- }
79
- /**
80
- * Runs the `@preDestroy()` method, awaiting if it returns a Promise.
81
- *
82
- * Lifecycle ordering: `onDeactivation` → **`@preDestroy`** (async variant).
83
- */
84
- async function runPreDestroyAsync(implementationClass, instance) {
85
- const meta = readLifecycleMetadataFromCtor(implementationClass);
86
- if (meta?.preDestroy === void 0) return;
87
- const lifecycleMethod = instance[meta.preDestroy];
88
- if (typeof lifecycleMethod !== "function") return;
89
- await lifecycleMethod.call(instance);
90
- }
91
- /**
92
- * Runs `onActivation`, awaiting if the handler returns a Promise.
93
- *
94
- * Lifecycle ordering: `construct` → `@postConstruct` → **`onActivation`** (async variant) → cache.
95
- */
96
- async function runActivationAsync(binding, instance, ctx, _pathLabels) {
97
- const handler = binding.onActivation;
98
- if (handler === void 0) return instance;
99
- return await handler(ctx, instance);
100
- }
17
+ hasActivationHandlers(t) {
18
+ if (this._activationHooks.size === 0) return false;
19
+ const list = this._activationHooks.get(t);
20
+ return list !== void 0 && list.length > 0;
21
+ }
22
+ get activationVersion() {
23
+ return this._activationVersion;
24
+ }
25
+ registerDeactivation(t, handler) {
26
+ let list = this._deactivationHooks.get(t);
27
+ if (list === void 0) {
28
+ list = [];
29
+ this._deactivationHooks.set(t, list);
30
+ }
31
+ list.push(handler);
32
+ }
33
+ async runActivation(ctx, binding, instance, metadataReader) {
34
+ let result = instance;
35
+ if (binding.kind === "class") {
36
+ const lifecycle = metadataReader.getLifecycleMetadata(binding.target);
37
+ if (lifecycle?.postConstruct && lifecycle.postConstruct.length > 0) for (const methodName of lifecycle.postConstruct) {
38
+ const method = result[methodName];
39
+ if (typeof method === "function") {
40
+ const r = method.call(result);
41
+ if (r instanceof Promise) await r;
42
+ }
43
+ }
44
+ }
45
+ if (binding.kind !== "alias" && binding.onActivation !== void 0) {
46
+ const activated = binding.onActivation(ctx, result);
47
+ result = activated instanceof Promise ? await activated : activated;
48
+ }
49
+ const containerHooks = this._activationHooks.get(binding.token);
50
+ if (containerHooks !== void 0) for (const hook of containerHooks) {
51
+ const activated = hook(ctx, result);
52
+ result = activated instanceof Promise ? await activated : activated;
53
+ }
54
+ return result;
55
+ }
56
+ runActivationSync(ctx, binding, instance, metadataReader) {
57
+ let result = instance;
58
+ if (binding.kind === "class") {
59
+ const lifecycle = metadataReader.getLifecycleMetadata(binding.target);
60
+ if (lifecycle?.postConstruct && lifecycle.postConstruct.length > 0) for (const methodName of lifecycle.postConstruct) {
61
+ const method = result[methodName];
62
+ if (typeof method === "function") {
63
+ if (method.call(result) instanceof Promise) throw new Error(`@postConstruct method '${methodName}' returned a Promise. Use resolveAsync() instead.`);
64
+ }
65
+ }
66
+ }
67
+ if (binding.kind !== "alias" && binding.onActivation !== void 0) {
68
+ const activated = binding.onActivation(ctx, result);
69
+ if (activated instanceof Promise) throw new Error(`onActivation for '${tokenName(binding.token)}' returned a Promise. Use resolveAsync() instead.`);
70
+ result = activated;
71
+ }
72
+ const key = tokenName(binding.token);
73
+ const containerHooks = this._activationHooks.get(binding.token);
74
+ if (containerHooks !== void 0) for (const hook of containerHooks) {
75
+ const activated = hook(ctx, result);
76
+ if (activated instanceof Promise) throw new Error(`Container-level onActivation for '${key}' returned a Promise. Use resolveAsync() instead.`);
77
+ result = activated;
78
+ }
79
+ return result;
80
+ }
81
+ async runDeactivation(binding, instance, metadataReader) {
82
+ const key = binding.token;
83
+ const containerHooks = this._deactivationHooks.get(key);
84
+ if (containerHooks !== void 0) for (const hook of containerHooks) {
85
+ const r = hook(instance);
86
+ if (r instanceof Promise) await r;
87
+ }
88
+ if (binding.kind !== "alias" && binding.onDeactivation !== void 0) {
89
+ const r = binding.onDeactivation(instance);
90
+ if (r instanceof Promise) await r;
91
+ }
92
+ if (binding.kind === "class") {
93
+ const lifecycle = metadataReader.getLifecycleMetadata(binding.target);
94
+ if (lifecycle?.preDestroy && lifecycle.preDestroy.length > 0) for (const methodName of lifecycle.preDestroy) {
95
+ const method = instance[methodName];
96
+ if (typeof method === "function") {
97
+ const r = method.call(instance);
98
+ if (r instanceof Promise) await r;
99
+ }
100
+ }
101
+ }
102
+ }
103
+ runDeactivationSync(binding, instance, metadataReader) {
104
+ const tName = tokenName(binding.token);
105
+ const key = binding.token;
106
+ const containerHooks = this._deactivationHooks.get(key);
107
+ if (containerHooks !== void 0) {
108
+ for (const hook of containerHooks) if (hook(instance) instanceof Promise) throw new AsyncDeactivationError(tName);
109
+ }
110
+ if (binding.kind !== "alias" && binding.onDeactivation !== void 0) {
111
+ if (binding.onDeactivation(instance) instanceof Promise) throw new AsyncDeactivationError(tName);
112
+ }
113
+ if (binding.kind === "class") {
114
+ const lifecycle = metadataReader.getLifecycleMetadata(binding.target);
115
+ if (lifecycle?.preDestroy && lifecycle.preDestroy.length > 0) for (const methodName of lifecycle.preDestroy) {
116
+ const method = instance[methodName];
117
+ if (typeof method === "function") {
118
+ if (method.call(instance) instanceof Promise) throw new AsyncDeactivationError(tName);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ hasAsyncDeactivation(binding, instance, _metadataReader) {
124
+ if (binding.kind === "alias") return false;
125
+ if (binding.onDeactivation !== void 0) {
126
+ if (binding.onDeactivation(instance) instanceof Promise) return true;
127
+ }
128
+ return false;
129
+ }
130
+ };
101
131
  //#endregion
102
- export { isPromiseLike, readLifecycleMetadataFromCtor, runActivation, runActivationAsync, runPostConstruct, runPostConstructAsync, runPreDestroy, runPreDestroyAsync };
132
+ export { LifecycleManager };
@@ -1,28 +1,11 @@
1
+ import { ConstructorMetadata, MutableLifecycleMetadata } from "./metadata-types.mjs";
2
+
1
3
  //#region src/metadata/metadata-keys.d.ts
2
- /**
3
- * Well-known property key for constructor injection metadata on `Symbol.metadata`.
4
- * Written by `@injectable()`, read by `SymbolMetadataReader.getConstructorMetadata()`.
5
- * The `:v1` suffix is a schema version — bump only when the `ConstructorMetadata` shape changes.
6
- */
7
- declare const CODEFAST_DI_CONSTRUCTOR_METADATA = "codefast/di:constructor-metadata:v1";
8
- /**
9
- * Well-known property key for accessor-field injection metadata on `Symbol.metadata`.
10
- * Written by `@inject()` when used as an accessor decorator; read during post-construction
11
- * property injection by the resolver.
12
- */
13
- declare const CODEFAST_DI_ACCESSOR_INJECTIONS = "codefast/di:accessor-injections:v1";
14
- /**
15
- * Well-known key for lifecycle method names written by `@postConstruct()` / `@preDestroy()`.
16
- */
17
- declare const CODEFAST_DI_LIFECYCLE_METADATA = "codefast/di:lifecycle-metadata:v1";
18
- /**
19
- * Returns the runtime symbol used to access TC39 decorator metadata on a class.
20
- *
21
- * Prefers the native `Symbol.metadata` when available (Stage 3 decorators); falls back to
22
- * `Symbol.for("Symbol.metadata")` for Node/runtime versions that polyfill or partially
23
- * implement the proposal. The fallback key is the de facto convention used by TypeScript
24
- * and polyfill libraries.
25
- */
26
- declare function decoratorMetadataObjectSymbol(): symbol;
4
+ declare const INJECTABLE_KEY: unique symbol;
5
+ declare const LIFECYCLE_KEY: unique symbol;
6
+ declare const INJECT_ACCESSOR_KEY: unique symbol;
7
+ declare const constructorMetadataMap: WeakMap<object, ConstructorMetadata>;
8
+ declare const lifecycleMetadataMap: WeakMap<object, MutableLifecycleMetadata>;
9
+ declare const lifecycleByConstructorMetadataMap: WeakMap<object, MutableLifecycleMetadata>;
27
10
  //#endregion
28
- export { CODEFAST_DI_ACCESSOR_INJECTIONS, CODEFAST_DI_CONSTRUCTOR_METADATA, CODEFAST_DI_LIFECYCLE_METADATA, decoratorMetadataObjectSymbol };
11
+ export { INJECTABLE_KEY, INJECT_ACCESSOR_KEY, LIFECYCLE_KEY, constructorMetadataMap, lifecycleByConstructorMetadataMap, lifecycleMetadataMap };
@@ -1,30 +1,9 @@
1
1
  //#region src/metadata/metadata-keys.ts
2
- /**
3
- * Well-known property key for constructor injection metadata on `Symbol.metadata`.
4
- * Written by `@injectable()`, read by `SymbolMetadataReader.getConstructorMetadata()`.
5
- * The `:v1` suffix is a schema version — bump only when the `ConstructorMetadata` shape changes.
6
- */
7
- const CODEFAST_DI_CONSTRUCTOR_METADATA = "codefast/di:constructor-metadata:v1";
8
- /**
9
- * Well-known property key for accessor-field injection metadata on `Symbol.metadata`.
10
- * Written by `@inject()` when used as an accessor decorator; read during post-construction
11
- * property injection by the resolver.
12
- */
13
- const CODEFAST_DI_ACCESSOR_INJECTIONS = "codefast/di:accessor-injections:v1";
14
- /**
15
- * Well-known key for lifecycle method names written by `@postConstruct()` / `@preDestroy()`.
16
- */
17
- const CODEFAST_DI_LIFECYCLE_METADATA = "codefast/di:lifecycle-metadata:v1";
18
- /**
19
- * Returns the runtime symbol used to access TC39 decorator metadata on a class.
20
- *
21
- * Prefers the native `Symbol.metadata` when available (Stage 3 decorators); falls back to
22
- * `Symbol.for("Symbol.metadata")` for Node/runtime versions that polyfill or partially
23
- * implement the proposal. The fallback key is the de facto convention used by TypeScript
24
- * and polyfill libraries.
25
- */
26
- function decoratorMetadataObjectSymbol() {
27
- return typeof Symbol.metadata === "symbol" ? Symbol.metadata : Symbol.for("Symbol.metadata");
28
- }
2
+ const INJECTABLE_KEY = Symbol("di:injectable");
3
+ const LIFECYCLE_KEY = Symbol("di:lifecycle");
4
+ const INJECT_ACCESSOR_KEY = Symbol("di:inject-accessor");
5
+ const constructorMetadataMap = /* @__PURE__ */ new WeakMap();
6
+ const lifecycleMetadataMap = /* @__PURE__ */ new WeakMap();
7
+ const lifecycleByConstructorMetadataMap = /* @__PURE__ */ new WeakMap();
29
8
  //#endregion
30
- export { CODEFAST_DI_ACCESSOR_INJECTIONS, CODEFAST_DI_CONSTRUCTOR_METADATA, CODEFAST_DI_LIFECYCLE_METADATA, decoratorMetadataObjectSymbol };
9
+ export { INJECTABLE_KEY, INJECT_ACCESSOR_KEY, LIFECYCLE_KEY, constructorMetadataMap, lifecycleByConstructorMetadataMap, lifecycleMetadataMap };
@@ -0,0 +1,7 @@
1
+ import { Token } from "../token.mjs";
2
+ import { MetadataReader } from "./metadata-types.mjs";
3
+
4
+ //#region src/metadata/metadata-reader-token.d.ts
5
+ declare const MetadataReaderToken: Token<MetadataReader>;
6
+ //#endregion
7
+ export { MetadataReaderToken };
@@ -0,0 +1,5 @@
1
+ import { token } from "../token.mjs";
2
+ //#region src/metadata/metadata-reader-token.ts
3
+ const MetadataReaderToken = token("MetadataReader");
4
+ //#endregion
5
+ export { MetadataReaderToken };