@codefast/di 0.5.0-canary.9 → 0.6.0

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 (266) hide show
  1. package/CHANGELOG.md +865 -0
  2. package/README.md +119 -30
  3. package/dist/ambient/active-container.d.ts +37 -0
  4. package/dist/ambient/active-container.d.ts.map +1 -0
  5. package/dist/ambient/active-container.js +41 -0
  6. package/dist/ambient/active-container.js.map +1 -0
  7. package/dist/container/binding-builders.d.ts +12 -8
  8. package/dist/container/binding-builders.d.ts.map +1 -1
  9. package/dist/container/binding-builders.js +56 -31
  10. package/dist/container/binding-builders.js.map +1 -1
  11. package/dist/container/container.d.ts +33 -19
  12. package/dist/container/container.d.ts.map +1 -1
  13. package/dist/container/container.js +279 -118
  14. package/dist/container/container.js.map +1 -1
  15. package/dist/{resolution → core}/binding-scope.d.ts +2 -2
  16. package/dist/core/binding-scope.d.ts.map +1 -0
  17. package/dist/core/binding-scope.js.map +1 -0
  18. package/dist/{binding.d.ts → core/binding.d.ts} +70 -15
  19. package/dist/core/binding.d.ts.map +1 -0
  20. package/dist/{binding.js → core/binding.js} +27 -12
  21. package/dist/core/binding.js.map +1 -0
  22. package/dist/core/constraint-requirement.d.ts +38 -0
  23. package/dist/core/constraint-requirement.d.ts.map +1 -0
  24. package/dist/core/constraint-requirement.js +28 -0
  25. package/dist/core/constraint-requirement.js.map +1 -0
  26. package/dist/{constructor-type.d.ts → core/constructor-type.d.ts} +1 -1
  27. package/dist/core/constructor-type.d.ts.map +1 -0
  28. package/dist/core/constructor-type.js.map +1 -0
  29. package/dist/{module.d.ts → core/module.d.ts} +5 -5
  30. package/dist/core/module.d.ts.map +1 -0
  31. package/dist/{module.js → core/module.js} +4 -4
  32. package/dist/core/module.js.map +1 -0
  33. package/dist/{registry.d.ts → core/registry.d.ts} +27 -7
  34. package/dist/core/registry.d.ts.map +1 -0
  35. package/dist/{registry.js → core/registry.js} +125 -52
  36. package/dist/core/registry.js.map +1 -0
  37. package/dist/core/tag.d.ts +80 -0
  38. package/dist/core/tag.d.ts.map +1 -0
  39. package/dist/core/tag.js +84 -0
  40. package/dist/core/tag.js.map +1 -0
  41. package/dist/{token.d.ts → core/token.d.ts} +2 -6
  42. package/dist/core/token.d.ts.map +1 -0
  43. package/dist/core/token.js +13 -0
  44. package/dist/core/token.js.map +1 -0
  45. package/dist/{types.d.ts → core/types.d.ts} +27 -21
  46. package/dist/core/types.d.ts.map +1 -0
  47. package/dist/core/types.js.map +1 -0
  48. package/dist/decorators/inject.d.ts +5 -49
  49. package/dist/decorators/inject.d.ts.map +1 -1
  50. package/dist/decorators/inject.js +34 -104
  51. package/dist/decorators/inject.js.map +1 -1
  52. package/dist/decorators/injectable.d.ts +25 -5
  53. package/dist/decorators/injectable.d.ts.map +1 -1
  54. package/dist/decorators/injectable.js +2 -3
  55. package/dist/decorators/injectable.js.map +1 -1
  56. package/dist/decorators/lifecycle-decorators.d.ts +4 -2
  57. package/dist/decorators/lifecycle-decorators.d.ts.map +1 -1
  58. package/dist/decorators/lifecycle-decorators.js +19 -25
  59. package/dist/decorators/lifecycle-decorators.js.map +1 -1
  60. package/dist/{resolution → errors}/diagnostics.d.ts +2 -0
  61. package/dist/errors/diagnostics.d.ts.map +1 -0
  62. package/dist/errors/diagnostics.js.map +1 -0
  63. package/dist/{errors.d.ts → errors/errors.d.ts} +89 -4
  64. package/dist/errors/errors.d.ts.map +1 -0
  65. package/dist/{errors.js → errors/errors.js} +129 -7
  66. package/dist/errors/errors.js.map +1 -0
  67. package/dist/index.d.ts +21 -14
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +15 -7
  70. package/dist/index.js.map +1 -1
  71. package/dist/injection/descriptor.d.ts +79 -0
  72. package/dist/injection/descriptor.d.ts.map +1 -0
  73. package/dist/injection/descriptor.js +118 -0
  74. package/dist/injection/descriptor.js.map +1 -0
  75. package/dist/{resolution → injection}/resolve-options.d.ts +21 -11
  76. package/dist/injection/resolve-options.d.ts.map +1 -0
  77. package/dist/injection/resolve-options.js +101 -0
  78. package/dist/injection/resolve-options.js.map +1 -0
  79. package/dist/introspection/dependency-graph.d.ts +16 -5
  80. package/dist/introspection/dependency-graph.d.ts.map +1 -1
  81. package/dist/introspection/dependency-graph.js +138 -57
  82. package/dist/introspection/dependency-graph.js.map +1 -1
  83. package/dist/introspection/graph-adapters/cytoscape.d.ts +6 -3
  84. package/dist/introspection/graph-adapters/cytoscape.d.ts.map +1 -1
  85. package/dist/introspection/graph-adapters/cytoscape.js +3 -0
  86. package/dist/introspection/graph-adapters/cytoscape.js.map +1 -1
  87. package/dist/introspection/graph-adapters/dot.d.ts.map +1 -1
  88. package/dist/introspection/graph-adapters/dot.js +12 -5
  89. package/dist/introspection/graph-adapters/dot.js.map +1 -1
  90. package/dist/introspection/graph-adapters/mermaid.d.ts +9 -0
  91. package/dist/introspection/graph-adapters/mermaid.d.ts.map +1 -0
  92. package/dist/introspection/graph-adapters/mermaid.js +47 -0
  93. package/dist/introspection/graph-adapters/mermaid.js.map +1 -0
  94. package/dist/introspection/graph-adapters/reactflow.d.ts +6 -3
  95. package/dist/introspection/graph-adapters/reactflow.d.ts.map +1 -1
  96. package/dist/introspection/graph-adapters/reactflow.js +5 -1
  97. package/dist/introspection/graph-adapters/reactflow.js.map +1 -1
  98. package/dist/introspection/inspector.d.ts +4 -4
  99. package/dist/introspection/inspector.d.ts.map +1 -1
  100. package/dist/introspection/inspector.js +10 -6
  101. package/dist/introspection/inspector.js.map +1 -1
  102. package/dist/lifecycle/lifecycle-manager.d.ts +27 -0
  103. package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
  104. package/dist/{resolution/lifecycle.js → lifecycle/lifecycle-manager.js} +39 -9
  105. package/dist/lifecycle/lifecycle-manager.js.map +1 -0
  106. package/dist/lifecycle/scope-manager.d.ts +48 -0
  107. package/dist/lifecycle/scope-manager.d.ts.map +1 -0
  108. package/dist/lifecycle/scope-manager.js +135 -0
  109. package/dist/lifecycle/scope-manager.js.map +1 -0
  110. package/dist/metadata/metadata-reader-token.d.ts +1 -1
  111. package/dist/metadata/metadata-reader-token.d.ts.map +1 -1
  112. package/dist/metadata/metadata-reader-token.js +1 -1
  113. package/dist/metadata/metadata-reader-token.js.map +1 -1
  114. package/dist/metadata/metadata-types.d.ts +9 -9
  115. package/dist/metadata/metadata-types.d.ts.map +1 -1
  116. package/dist/metadata/symbol-metadata-reader.d.ts +19 -6
  117. package/dist/metadata/symbol-metadata-reader.d.ts.map +1 -1
  118. package/dist/metadata/symbol-metadata-reader.js +89 -9
  119. package/dist/metadata/symbol-metadata-reader.js.map +1 -1
  120. package/dist/metadata/verifying-metadata-reader.d.ts +16 -0
  121. package/dist/metadata/verifying-metadata-reader.d.ts.map +1 -0
  122. package/dist/metadata/verifying-metadata-reader.js +32 -0
  123. package/dist/metadata/verifying-metadata-reader.js.map +1 -0
  124. package/dist/resolution/{activation-need.d.ts → cache/activation-need.d.ts} +5 -5
  125. package/dist/resolution/cache/activation-need.d.ts.map +1 -0
  126. package/dist/resolution/cache/activation-need.js.map +1 -0
  127. package/dist/resolution/{binding-lookup-cache.d.ts → cache/binding-lookup-cache.d.ts} +6 -5
  128. package/dist/resolution/cache/binding-lookup-cache.d.ts.map +1 -0
  129. package/dist/resolution/cache/binding-lookup-cache.js.map +1 -0
  130. package/dist/resolution/cache/class-introspector.d.ts +66 -0
  131. package/dist/resolution/cache/class-introspector.d.ts.map +1 -0
  132. package/dist/resolution/cache/class-introspector.js +194 -0
  133. package/dist/resolution/cache/class-introspector.js.map +1 -0
  134. package/dist/resolution/context.d.ts +84 -0
  135. package/dist/resolution/context.d.ts.map +1 -0
  136. package/dist/resolution/{environment.js → context.js} +54 -80
  137. package/dist/resolution/context.js.map +1 -0
  138. package/dist/resolution/path/resolution-path.d.ts +94 -0
  139. package/dist/resolution/path/resolution-path.d.ts.map +1 -0
  140. package/dist/resolution/path/resolution-path.js +117 -0
  141. package/dist/resolution/path/resolution-path.js.map +1 -0
  142. package/dist/resolution/{instantiation-plan.d.ts → plan/instantiation-plan.d.ts} +30 -7
  143. package/dist/resolution/plan/instantiation-plan.d.ts.map +1 -0
  144. package/dist/resolution/plan/instantiation-plan.js +390 -0
  145. package/dist/resolution/plan/instantiation-plan.js.map +1 -0
  146. package/dist/resolution/resolver.d.ts +28 -28
  147. package/dist/resolution/resolver.d.ts.map +1 -1
  148. package/dist/resolution/resolver.js +552 -239
  149. package/dist/resolution/resolver.js.map +1 -1
  150. package/dist/resolution/select/binding-select.d.ts +34 -0
  151. package/dist/resolution/select/binding-select.d.ts.map +1 -0
  152. package/dist/resolution/select/binding-select.js +175 -0
  153. package/dist/resolution/select/binding-select.js.map +1 -0
  154. package/dist/resolution/{constraints.d.ts → select/constraints.d.ts} +13 -12
  155. package/dist/resolution/select/constraints.d.ts.map +1 -0
  156. package/dist/resolution/{constraints.js → select/constraints.js} +33 -22
  157. package/dist/resolution/select/constraints.js.map +1 -0
  158. package/package.json +160 -20
  159. package/src/ambient/active-container.ts +65 -0
  160. package/src/container/binding-builders.ts +72 -44
  161. package/src/container/container.ts +355 -170
  162. package/src/{resolution → core}/binding-scope.ts +2 -2
  163. package/src/{binding.ts → core/binding.ts} +101 -39
  164. package/src/core/constraint-requirement.ts +51 -0
  165. package/src/{constructor-type.ts → core/constructor-type.ts} +1 -1
  166. package/src/{module.ts → core/module.ts} +10 -10
  167. package/src/core/registry.ts +365 -0
  168. package/src/core/tag.ts +144 -0
  169. package/src/{token.ts → core/token.ts} +2 -14
  170. package/src/{types.ts → core/types.ts} +37 -34
  171. package/src/decorators/inject.ts +39 -175
  172. package/src/decorators/injectable.ts +40 -10
  173. package/src/decorators/lifecycle-decorators.ts +22 -34
  174. package/src/{resolution → errors}/diagnostics.ts +2 -0
  175. package/src/{errors.ts → errors/errors.ts} +152 -8
  176. package/src/index.ts +43 -15
  177. package/src/injection/descriptor.ts +206 -0
  178. package/src/injection/resolve-options.ts +144 -0
  179. package/src/introspection/dependency-graph.ts +223 -63
  180. package/src/introspection/graph-adapters/cytoscape.ts +9 -3
  181. package/src/introspection/graph-adapters/dot.ts +13 -5
  182. package/src/introspection/graph-adapters/mermaid.ts +61 -0
  183. package/src/introspection/graph-adapters/reactflow.ts +11 -4
  184. package/src/introspection/inspector.ts +16 -12
  185. package/src/{resolution/lifecycle.ts → lifecycle/lifecycle-manager.ts} +68 -32
  186. package/src/lifecycle/scope-manager.ts +156 -0
  187. package/src/metadata/metadata-reader-token.ts +2 -2
  188. package/src/metadata/metadata-types.ts +9 -9
  189. package/src/metadata/symbol-metadata-reader.ts +95 -18
  190. package/src/metadata/verifying-metadata-reader.ts +41 -0
  191. package/src/resolution/{activation-need.ts → cache/activation-need.ts} +8 -8
  192. package/src/resolution/{binding-lookup-cache.ts → cache/binding-lookup-cache.ts} +6 -5
  193. package/src/resolution/cache/class-introspector.ts +251 -0
  194. package/src/resolution/{environment.ts → context.ts} +84 -145
  195. package/src/resolution/path/resolution-path.ts +167 -0
  196. package/src/resolution/plan/instantiation-plan.ts +579 -0
  197. package/src/resolution/resolver.ts +635 -313
  198. package/src/resolution/select/binding-select.ts +210 -0
  199. package/src/resolution/select/constraints.ts +130 -0
  200. package/dist/binding.d.ts.map +0 -1
  201. package/dist/binding.js.map +0 -1
  202. package/dist/constructor-type.d.ts.map +0 -1
  203. package/dist/constructor-type.js.map +0 -1
  204. package/dist/errors.d.ts.map +0 -1
  205. package/dist/errors.js.map +0 -1
  206. package/dist/module.d.ts.map +0 -1
  207. package/dist/module.js.map +0 -1
  208. package/dist/registry.d.ts.map +0 -1
  209. package/dist/registry.js.map +0 -1
  210. package/dist/resolution/activation-need.d.ts.map +0 -1
  211. package/dist/resolution/activation-need.js.map +0 -1
  212. package/dist/resolution/binding-lookup-cache.d.ts.map +0 -1
  213. package/dist/resolution/binding-lookup-cache.js.map +0 -1
  214. package/dist/resolution/binding-scope.d.ts.map +0 -1
  215. package/dist/resolution/binding-scope.js.map +0 -1
  216. package/dist/resolution/binding-select.d.ts +0 -23
  217. package/dist/resolution/binding-select.d.ts.map +0 -1
  218. package/dist/resolution/binding-select.js +0 -121
  219. package/dist/resolution/binding-select.js.map +0 -1
  220. package/dist/resolution/class-introspector.d.ts +0 -27
  221. package/dist/resolution/class-introspector.d.ts.map +0 -1
  222. package/dist/resolution/class-introspector.js +0 -60
  223. package/dist/resolution/class-introspector.js.map +0 -1
  224. package/dist/resolution/constraints.d.ts.map +0 -1
  225. package/dist/resolution/constraints.js.map +0 -1
  226. package/dist/resolution/diagnostics.d.ts.map +0 -1
  227. package/dist/resolution/diagnostics.js.map +0 -1
  228. package/dist/resolution/environment.d.ts +0 -94
  229. package/dist/resolution/environment.d.ts.map +0 -1
  230. package/dist/resolution/environment.js.map +0 -1
  231. package/dist/resolution/instantiation-plan.d.ts.map +0 -1
  232. package/dist/resolution/instantiation-plan.js +0 -183
  233. package/dist/resolution/instantiation-plan.js.map +0 -1
  234. package/dist/resolution/lifecycle.d.ts +0 -23
  235. package/dist/resolution/lifecycle.d.ts.map +0 -1
  236. package/dist/resolution/lifecycle.js.map +0 -1
  237. package/dist/resolution/resolution-path.d.ts +0 -98
  238. package/dist/resolution/resolution-path.d.ts.map +0 -1
  239. package/dist/resolution/resolution-path.js +0 -98
  240. package/dist/resolution/resolution-path.js.map +0 -1
  241. package/dist/resolution/resolve-options.d.ts.map +0 -1
  242. package/dist/resolution/resolve-options.js +0 -56
  243. package/dist/resolution/resolve-options.js.map +0 -1
  244. package/dist/resolution/scope.d.ts +0 -28
  245. package/dist/resolution/scope.d.ts.map +0 -1
  246. package/dist/resolution/scope.js +0 -88
  247. package/dist/resolution/scope.js.map +0 -1
  248. package/dist/token.d.ts.map +0 -1
  249. package/dist/token.js +0 -22
  250. package/dist/token.js.map +0 -1
  251. package/dist/types.d.ts.map +0 -1
  252. package/dist/types.js.map +0 -1
  253. package/src/registry.ts +0 -282
  254. package/src/resolution/binding-select.ts +0 -154
  255. package/src/resolution/class-introspector.ts +0 -74
  256. package/src/resolution/constraints.ts +0 -121
  257. package/src/resolution/instantiation-plan.ts +0 -292
  258. package/src/resolution/resolution-path.ts +0 -156
  259. package/src/resolution/resolve-options.ts +0 -89
  260. package/src/resolution/scope.ts +0 -105
  261. /package/dist/{resolution → core}/binding-scope.js +0 -0
  262. /package/dist/{constructor-type.js → core/constructor-type.js} +0 -0
  263. /package/dist/{types.js → core/types.js} +0 -0
  264. /package/dist/{resolution → errors}/diagnostics.js +0 -0
  265. /package/dist/resolution/{activation-need.js → cache/activation-need.js} +0 -0
  266. /package/dist/resolution/{binding-lookup-cache.js → cache/binding-lookup-cache.js} +0 -0
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Per-class decorator metadata, cached by constructor.
3
+ *
4
+ * @remarks Metadata cannot change once a class is defined, so nothing here needs version stamping.
5
+ */
6
+
7
+ import { runWithAmbientResolution } from "#/ambient/active-container";
8
+ import type { AmbientResolution } from "#/ambient/active-container";
9
+ import type { Container } from "#/container/container";
10
+ import type { ConstructorInvocation } from "#/core/constructor-type";
11
+ import type { Constructor } from "#/core/types";
12
+ import { InvalidMetadataError } from "#/errors/errors";
13
+ import type { InjectionDescriptor } from "#/injection/descriptor";
14
+ import type { ConstructorMetadata, LifecycleMetadata, MetadataReader } from "#/metadata/metadata-types";
15
+
16
+ // Verified pairs, not verified classes: two readers may disagree about the same class, and a reader
17
+ // that goes out of scope takes its record with it.
18
+ const verifiedTargets = new WeakMap<MetadataReader, WeakSet<Constructor>>();
19
+ const verifiedLifecycleTargets = new WeakMap<MetadataReader, WeakSet<Constructor>>();
20
+ const verifiedAccessorTargets = new WeakMap<MetadataReader, WeakSet<Constructor>>();
21
+
22
+ function isVerified(
23
+ cache: WeakMap<MetadataReader, WeakSet<Constructor>>,
24
+ reader: MetadataReader,
25
+ target: Constructor,
26
+ ): boolean {
27
+ return cache.get(reader)?.has(target) ?? false;
28
+ }
29
+
30
+ function markVerified(
31
+ cache: WeakMap<MetadataReader, WeakSet<Constructor>>,
32
+ reader: MetadataReader,
33
+ target: Constructor,
34
+ ): void {
35
+ let verified = cache.get(reader);
36
+ if (verified === undefined) {
37
+ verified = new WeakSet();
38
+ cache.set(reader, verified);
39
+ }
40
+ verified.add(target);
41
+ }
42
+
43
+ /**
44
+ * A reader's constructor metadata for a class, verified the first time this process asks.
45
+ *
46
+ * @remarks Metadata cannot change once a class is defined, so re-checking per container would charge
47
+ * every fresh container for a fact already established — a per-request child or a cold boot pays
48
+ * that repeatedly.
49
+ *
50
+ * @since 0.6.0
51
+ */
52
+ export function verifyConstructorMetadata(
53
+ reader: MetadataReader,
54
+ target: Constructor,
55
+ ): ConstructorMetadata | undefined {
56
+ const metadata = reader.getConstructorMetadata(target);
57
+ if (metadata === undefined) {
58
+ return undefined;
59
+ }
60
+ if (isVerified(verifiedTargets, reader, target)) {
61
+ return metadata;
62
+ }
63
+ assertConstructorMetadata(metadata, target);
64
+ markVerified(verifiedTargets, reader, target);
65
+
66
+ return metadata;
67
+ }
68
+
69
+ /**
70
+ * Verifies what a reader claims about a class, since a `MetadataReader` is a public seam.
71
+ *
72
+ * @remarks Every path that reads constructor metadata comes through here, so a reader that answers
73
+ * wrongly is a named error at the class it described rather than a `TypeError` raised later inside a
74
+ * resolve. Only what a consumer dereferences is checked: `params` and each entry's `token`.
75
+ * `optional`/`multi` degrade to falsy without crashing, and `index` is decorative — dependencies are
76
+ * consumed positionally.
77
+ *
78
+ * @since 0.6.0
79
+ */
80
+ export function assertConstructorMetadata(metadata: unknown, target: Constructor): ConstructorMetadata | undefined {
81
+ if (metadata === undefined) {
82
+ return undefined;
83
+ }
84
+ if (typeof metadata !== "object" || metadata === null) {
85
+ throw new InvalidMetadataError(
86
+ target.name,
87
+ `constructor metadata: expected an object, received ${typeof metadata}`,
88
+ );
89
+ }
90
+ const params: unknown = Reflect.get(metadata, "params");
91
+ if (!Array.isArray(params)) {
92
+ throw new InvalidMetadataError(target.name, "constructor metadata: params is not an array");
93
+ }
94
+ for (const [position, param] of params.entries()) {
95
+ if (typeof param !== "object" || param === null) {
96
+ throw new InvalidMetadataError(target.name, `constructor metadata: params[${String(position)}] is not an object`);
97
+ }
98
+ const dependency: unknown = Reflect.get(param, "token");
99
+ if (typeof dependency !== "object" && typeof dependency !== "function") {
100
+ throw new InvalidMetadataError(
101
+ target.name,
102
+ `constructor metadata: params[${String(position)}].token is not a token or a class`,
103
+ );
104
+ }
105
+ }
106
+
107
+ return metadata as ConstructorMetadata;
108
+ }
109
+
110
+ /**
111
+ * A reader's lifecycle metadata for a class, verified the first time this process asks.
112
+ *
113
+ * @since 0.6.0
114
+ */
115
+ export function verifyLifecycleMetadata(reader: MetadataReader, target: Constructor): LifecycleMetadata | undefined {
116
+ const metadata = reader.getLifecycleMetadata(target);
117
+ if (metadata === undefined || isVerified(verifiedLifecycleTargets, reader, target)) {
118
+ return metadata;
119
+ }
120
+ if (typeof metadata !== "object" || metadata === null) {
121
+ throw new InvalidMetadataError(target.name, `lifecycle metadata: expected an object, received ${typeof metadata}`);
122
+ }
123
+ for (const phase of ["postConstruct", "preDestroy"] as const) {
124
+ const methods: unknown = Reflect.get(metadata, phase);
125
+ if (methods === undefined) {
126
+ continue;
127
+ }
128
+ if (!Array.isArray(methods)) {
129
+ throw new InvalidMetadataError(target.name, `lifecycle metadata: ${phase} is not an array`);
130
+ }
131
+ for (const [position, name] of methods.entries()) {
132
+ if (typeof name !== "string") {
133
+ throw new InvalidMetadataError(
134
+ target.name,
135
+ `lifecycle metadata: ${phase}[${String(position)}] is not a string`,
136
+ );
137
+ }
138
+ }
139
+ }
140
+ markVerified(verifiedLifecycleTargets, reader, target);
141
+ return metadata;
142
+ }
143
+
144
+ /**
145
+ * A reader's accessor metadata for a class, verified the first time this process asks.
146
+ *
147
+ * @since 0.6.0
148
+ */
149
+ export function verifyAccessorMetadata(
150
+ reader: MetadataReader,
151
+ target: Constructor,
152
+ ): ReadonlyArray<{ readonly key: string | symbol; readonly descriptor: InjectionDescriptor }> | undefined {
153
+ const metadata = reader.getAccessorMetadata?.(target);
154
+ if (metadata === undefined || isVerified(verifiedAccessorTargets, reader, target)) {
155
+ return metadata;
156
+ }
157
+ if (!Array.isArray(metadata)) {
158
+ throw new InvalidMetadataError(target.name, "accessor metadata: expected an array");
159
+ }
160
+ for (const [position, entry] of metadata.entries()) {
161
+ if (typeof entry !== "object" || entry === null) {
162
+ throw new InvalidMetadataError(target.name, `accessor metadata: [${String(position)}] is not an object`);
163
+ }
164
+ const key: unknown = Reflect.get(entry, "key");
165
+ if (typeof key !== "string" && typeof key !== "symbol") {
166
+ throw new InvalidMetadataError(
167
+ target.name,
168
+ `accessor metadata: [${String(position)}].key is not a string or symbol`,
169
+ );
170
+ }
171
+ const descriptor: unknown = Reflect.get(entry, "descriptor");
172
+ if (typeof descriptor !== "object" || descriptor === null) {
173
+ throw new InvalidMetadataError(
174
+ target.name,
175
+ `accessor metadata: [${String(position)}].descriptor is not an object`,
176
+ );
177
+ }
178
+ const dependency: unknown = Reflect.get(descriptor, "token");
179
+ if (typeof dependency !== "object" && typeof dependency !== "function") {
180
+ throw new InvalidMetadataError(
181
+ target.name,
182
+ `accessor metadata: [${String(position)}].descriptor.token is not a token or a class`,
183
+ );
184
+ }
185
+ }
186
+ markVerified(verifiedAccessorTargets, reader, target);
187
+ return metadata;
188
+ }
189
+
190
+ /**
191
+ * @since 0.5.0-canary.8
192
+ */
193
+ export class ClassIntrospector {
194
+ // Unallocated until the container resolves its first class binding — a container bound entirely
195
+ // to constants, factories or aliases never introspects one.
196
+ #constructorMetadata: WeakMap<Constructor, ConstructorMetadata | null> | undefined;
197
+ #hasPostConstruct: WeakMap<Constructor, boolean> | undefined;
198
+ #needsActiveContainer: WeakMap<Constructor, boolean> | undefined;
199
+ readonly #reader: MetadataReader;
200
+ readonly #container: Container;
201
+
202
+ constructor(reader: MetadataReader, container: Container) {
203
+ this.#reader = reader;
204
+ this.#container = container;
205
+ }
206
+
207
+ constructorMetadata(target: Constructor): ConstructorMetadata | undefined {
208
+ const cached = this.#constructorMetadata?.get(target);
209
+ if (cached !== undefined) {
210
+ return cached === null ? undefined : cached;
211
+ }
212
+ const metadata = this.#reader.getConstructorMetadata(target);
213
+ (this.#constructorMetadata ??= new WeakMap()).set(target, metadata ?? null);
214
+ return metadata;
215
+ }
216
+
217
+ /**
218
+ * Whether the class has a `@postConstruct` hook, or `undefined` until {@link discoverPostConstruct}.
219
+ *
220
+ * @remarks Callers treat unknown as "assume it does", so the first activation settles it.
221
+ */
222
+ knownPostConstruct(target: Constructor): boolean | undefined {
223
+ return this.#hasPostConstruct?.get(target);
224
+ }
225
+
226
+ discoverPostConstruct(target: Constructor): void {
227
+ const lifecycle = this.#reader.getLifecycleMetadata(target);
228
+ (this.#hasPostConstruct ??= new WeakMap()).set(
229
+ target,
230
+ lifecycle !== undefined && lifecycle.postConstruct !== undefined && lifecycle.postConstruct.length > 0,
231
+ );
232
+ }
233
+
234
+ /** True when the class has accessor injection, which reads the container during construction. */
235
+ needsActiveContainer(target: Constructor): boolean {
236
+ let needsActiveContainer = this.#needsActiveContainer?.get(target);
237
+ if (needsActiveContainer === undefined) {
238
+ needsActiveContainer = (this.#reader.getAccessorMetadata?.(target)?.length ?? 0) > 0;
239
+ (this.#needsActiveContainer ??= new WeakMap()).set(target, needsActiveContainer);
240
+ }
241
+ return needsActiveContainer;
242
+ }
243
+
244
+ instantiate(target: Constructor, deps: Array<unknown>, resolution?: AmbientResolution): unknown {
245
+ const invokable = target as ConstructorInvocation;
246
+ if (!this.needsActiveContainer(target)) {
247
+ return new invokable(...deps);
248
+ }
249
+ return runWithAmbientResolution(this.#container, resolution, () => new invokable(...deps));
250
+ }
251
+ }