@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
@@ -1,23 +1,40 @@
1
- import { NO_INSTANCE } from "#/binding";
2
1
  import { BindingChain } from "#/container/binding-builders";
3
- import { AsyncModuleLoadError, CircularDependencyError, DisposedContainerError, InternalError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError, } from "#/errors";
2
+ import { NO_INSTANCE } from "#/core/binding";
3
+ import { effectiveBindingScope } from "#/core/binding-scope";
4
+ import { constraintRequirementOf } from "#/core/constraint-requirement";
5
+ import { isSyncModule, MODULE_SETUP } from "#/core/module";
6
+ import { BindingRegistry } from "#/core/registry";
7
+ import { tokenName } from "#/core/token";
8
+ import { RESOLUTION_DIAGNOSTICS } from "#/errors/diagnostics";
9
+ import { AsyncModuleLoadError, CircularDependencyError, DisposedContainerError, InternalError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError, UnreachableConstraintError, UnreachableLifecycleHookError, } from "#/errors/errors";
10
+ import { injectionSlotToResolveOptions, bindingSlotToResolveOptions } from "#/injection/resolve-options";
4
11
  import { buildDependencyGraph } from "#/introspection/dependency-graph";
5
12
  import { Inspector } from "#/introspection/inspector";
13
+ import { LifecycleManager } from "#/lifecycle/lifecycle-manager";
14
+ import { ScopeManager } from "#/lifecycle/scope-manager";
6
15
  import { MetadataReaderToken } from "#/metadata/metadata-reader-token";
7
16
  import { defaultMetadataReader } from "#/metadata/symbol-metadata-reader";
8
- import { isSyncModule, MODULE_SETUP } from "#/module";
9
- import { BindingRegistry } from "#/registry";
10
- import { effectiveBindingScope } from "#/resolution/binding-scope";
11
- import { RESOLUTION_DIAGNOSTICS } from "#/resolution/diagnostics";
12
- import { LifecycleManager } from "#/resolution/lifecycle";
13
- import { ROOT_BRANCH } from "#/resolution/resolution-path";
14
- import { injectionSlotToResolveOptions, bindingSlotToResolveOptions } from "#/resolution/resolve-options";
17
+ import { verifyingMetadataReader } from "#/metadata/verifying-metadata-reader";
18
+ import { ROOT_BRANCH } from "#/resolution/path/resolution-path";
15
19
  import { DependencyResolver } from "#/resolution/resolver";
16
- import { ScopeManager } from "#/resolution/scope";
17
- import { tokenName } from "#/token";
18
- // ── DefaultContainer ──────────────────────────────────────────────────────────
20
+ // A Record rather than an if-chain, so a new `BindingScope` is a compile error here instead of
21
+ // silently landing in whichever branch happened to be last.
22
+ const APPLY_BINDING_SCOPE = {
23
+ singleton: (builder) => {
24
+ builder.singleton();
25
+ },
26
+ scoped: (builder) => {
27
+ builder.scoped();
28
+ },
29
+ transient: (builder) => {
30
+ builder.transient();
31
+ },
32
+ };
33
+ // ── DefaultContainer ─────────────────────────────────────────────────────────────────────────────────────────────────
19
34
  class DefaultContainer {
20
35
  #disposed = false;
36
+ // The one teardown run — every dispose() call returns it once it exists.
37
+ #disposePromise;
21
38
  #registry;
22
39
  #scope;
23
40
  #lifecycle;
@@ -31,12 +48,12 @@ class DefaultContainer {
31
48
  #moduleBindingIds;
32
49
  // One shared registration for every chain this container's own `bind()` creates.
33
50
  #registration;
34
- constructor(parent) {
51
+ constructor(parent, options) {
35
52
  this.#parent = parent;
36
53
  this.#registry = new BindingRegistry();
37
54
  this.#scope = new ScopeManager(parent !== undefined);
38
55
  this.#lifecycle = new LifecycleManager();
39
- this.#initResolver();
56
+ this.#initResolver(options?.metadataReader);
40
57
  }
41
58
  #getInspector() {
42
59
  return (this.#inspector ??= new Inspector(this.#registry, this.#scope, this.#parent !== undefined, () => this.#disposed));
@@ -49,54 +66,59 @@ class DefaultContainer {
49
66
  if (this.#moduleRefs !== undefined || this.#moduleBindingIds !== undefined) {
50
67
  builtSubsystems.push("container.moduleTables");
51
68
  }
52
- if (this.#registry.isBuilt) {
69
+ if (this.#registry.isNamedIndexBuilt) {
53
70
  builtSubsystems.push("registry.namedIndex");
54
71
  }
55
- if (this.#scope.isBuilt) {
72
+ if (this.#registry.isTaggedIndexBuilt) {
73
+ builtSubsystems.push("registry.taggedIndex");
74
+ }
75
+ if (this.#scope.isScopedCacheBuilt) {
56
76
  builtSubsystems.push("scope.scoped");
57
77
  }
58
- if (this.#lifecycle.isBuilt) {
78
+ if (this.#lifecycle.isActivationTableBuilt) {
59
79
  builtSubsystems.push("lifecycle.activationHooks");
60
80
  }
61
81
  return { ...this.#resolver.describeCaches(), scopedInstanceCount: this.#scope.scopedCount, builtSubsystems };
62
82
  }
63
- #initResolver() {
64
- const metadataReader = this.#getMetadataReader();
65
- const parentResolver = this.#parent === undefined ? undefined : this.#parent.#resolver;
83
+ #initResolver(configuredReader) {
84
+ const parent = this.#parent;
85
+ const metadataReader = verifyingMetadataReader(configuredReader ?? (parent === undefined ? defaultMetadataReader : parent.#readerForChild()));
86
+ const parentResolver = parent === undefined ? undefined : parent.#resolver;
66
87
  this.#resolver = new DependencyResolver(this.#registry, this.#scope, this.#lifecycle, metadataReader, this, parentResolver);
67
88
  }
68
- #getMetadataReader() {
69
- // Check if a custom MetadataReader has been bound
70
- const metaBindings = this.#registry.getAll(MetadataReaderToken);
71
- if (metaBindings.length > 0) {
89
+ /** What a container being constructed under this one inherits: a reader bound here, else this one's. */
90
+ #readerForChild() {
91
+ if (this.#registry.getAll(MetadataReaderToken).length > 0) {
72
92
  try {
73
- return this.#resolver.resolve(MetadataReaderToken, undefined, [], []);
93
+ return this.#resolver.resolve(MetadataReaderToken, undefined, []);
74
94
  }
75
95
  catch {
76
- // fall through to default
96
+ // An unresolvable reader binding is not worth failing a child over.
77
97
  }
78
98
  }
79
- if (this.#parent !== undefined) {
80
- return this.#parent.#getMetadataReader();
81
- }
82
- return defaultMetadataReader;
99
+ return this.#resolver.metadataReader;
100
+ }
101
+ /** One reader per container, fixed when its resolver was built — so every path agrees on it. */
102
+ #getMetadataReader() {
103
+ return this.#resolver.metadataReader;
83
104
  }
84
105
  get isDisposed() {
85
106
  return this.#disposed;
86
107
  }
87
- // ── Binding ──────────────────────────────────────────────────────────────
108
+ // ── Binding ────────────────────────────────────────────────────────────────────────────────────────────────────────
88
109
  bind(token) {
89
110
  this.#assertNotDisposed();
90
111
  return this.#createBindToBuilder(token);
91
112
  }
92
113
  /** The registration every non-module chain shares, so `bind()` allocates only the builder. */
93
114
  #ownRegistration() {
94
- return (this.#registration ??= { registry: this.#registry, moduleBindingIds: undefined });
115
+ return (this.#registration ??= { registry: this.#registry, scope: this.#scope, moduleBindingIds: undefined });
95
116
  }
96
117
  /** One registration per module load, holding that module's id list directly. */
97
118
  #moduleRegistration(moduleRef) {
98
119
  return {
99
120
  registry: this.#registry,
121
+ scope: this.#scope,
100
122
  moduleBindingIds: (this.#moduleBindingIds ??= new Map()).getOrInsert(moduleRef, []),
101
123
  };
102
124
  }
@@ -117,7 +139,7 @@ class DefaultContainer {
117
139
  // re-index the token's binding list once per binding.
118
140
  return this.#drainSingletons(this.#registry.removeByToken(tokenOrId));
119
141
  }
120
- /** Drain scope entries for already-removed bindings; only singletons yield deactivation pairs. */
142
+ /** Drain scope entries for already-removed bindings, and pair each one that still owes a deactivation. */
121
143
  #drainSingletons(bindings) {
122
144
  const pairs = [];
123
145
  for (const binding of bindings) {
@@ -125,36 +147,71 @@ class DefaultContainer {
125
147
  pairs.push([binding, binding.instance]);
126
148
  this.#scope.deleteSingleton(binding);
127
149
  }
150
+ else if (this.#owesConstantDeactivation(binding)) {
151
+ pairs.push([binding, binding.value]);
152
+ }
128
153
  this.#scope.deleteScoped(binding.id);
129
154
  }
130
155
  return pairs;
131
156
  }
132
- #unbindSync(tokenOrId) {
157
+ /**
158
+ * Whether a constant still owes its deactivation.
159
+ *
160
+ * @remarks A constant's value is handed in at bind time rather than built on demand, so its hook is
161
+ * owed whether or not anything ever resolved it. Callers check `instance` first: a constant that
162
+ * carries one was cached through activation and is deactivated with that value instead.
163
+ */
164
+ #owesConstantDeactivation(binding) {
165
+ return (binding.kind === "constant" &&
166
+ (binding.onDeactivation !== undefined || this.#lifecycle.hasDeactivationHandlers(binding.token)));
167
+ }
168
+ /** Runs every pair's deactivation even when one throws, then reports what threw. */
169
+ #deactivatePairsSync(pairs) {
170
+ if (pairs.length === 0) {
171
+ return;
172
+ }
133
173
  const reader = this.#getMetadataReader();
134
- for (const [binding, instance] of this.#collectDeactivationPairs(tokenOrId)) {
135
- this.#lifecycle.runDeactivationSync(binding, instance, reader);
174
+ const errors = [];
175
+ for (const [binding, instance] of pairs) {
176
+ try {
177
+ this.#lifecycle.runDeactivationSync(binding, instance, reader);
178
+ }
179
+ catch (error) {
180
+ errors.push(error);
181
+ }
136
182
  }
183
+ throwCollected(errors, "unbind completed, but deactivation hooks threw");
137
184
  }
138
- async unbindAsync(tokenOrId) {
139
- this.#assertNotDisposed();
185
+ async #deactivatePairs(pairs) {
186
+ if (pairs.length === 0) {
187
+ return;
188
+ }
140
189
  const reader = this.#getMetadataReader();
141
- for (const [binding, instance] of this.#collectDeactivationPairs(tokenOrId)) {
142
- await this.#lifecycle.runDeactivation(binding, instance, reader);
190
+ const errors = [];
191
+ for (const [binding, instance] of pairs) {
192
+ try {
193
+ await this.#lifecycle.runDeactivation(binding, instance, reader);
194
+ }
195
+ catch (error) {
196
+ errors.push(error);
197
+ }
143
198
  }
199
+ throwCollected(errors, "unbind completed, but deactivation hooks threw");
200
+ }
201
+ #unbindSync(tokenOrId) {
202
+ this.#deactivatePairsSync(this.#collectDeactivationPairs(tokenOrId));
203
+ }
204
+ async unbindAsync(tokenOrId) {
205
+ this.#assertNotDisposed();
206
+ await this.#deactivatePairs(this.#collectDeactivationPairs(tokenOrId));
144
207
  }
145
208
  unbindAll() {
146
209
  this.#assertNotDisposed();
147
- const reader = this.#getMetadataReader();
148
- for (const [binding, instance] of this.#drainSingletons(this.#registry.clear())) {
149
- this.#lifecycle.runDeactivationSync(binding, instance, reader);
150
- }
210
+ this.#deactivatePairsSync(this.#drainSingletons(this.#registry.clear()));
151
211
  }
152
212
  async unbindAllAsync() {
153
213
  this.#assertNotDisposed();
154
- const reader = this.#getMetadataReader();
155
- for (const [binding, instance] of this.#drainSingletons(this.#registry.clear())) {
156
- await this.#lifecycle.runDeactivation(binding, instance, reader);
157
- }
214
+ await this.#deactivatePairs(this.#drainSingletons(this.#registry.clear()));
158
215
  }
159
216
  rebind(token) {
160
217
  this.#assertNotDisposed();
@@ -165,7 +222,7 @@ class DefaultContainer {
165
222
  this.#unbindSync(token);
166
223
  return this.#createBindToBuilder(token);
167
224
  }
168
- // ── Module ────────────────────────────────────────────────────────────────
225
+ // ── Module ─────────────────────────────────────────────────────────────────────────────────────────────────────────
169
226
  load(...modules) {
170
227
  this.#assertNotDisposed();
171
228
  this.#loadSyncModules(modules);
@@ -186,15 +243,42 @@ class DefaultContainer {
186
243
  }
187
244
  moduleRefs.set(moduleRef, 1);
188
245
  const builder = this.#createModuleBuilder(moduleRef);
189
- module[MODULE_SETUP](builder);
246
+ try {
247
+ module[MODULE_SETUP](builder);
248
+ }
249
+ catch (error) {
250
+ throw this.#rollbackFailedLoadSync(moduleRef, error);
251
+ }
190
252
  }
191
253
  }
192
254
  async loadAsync(...modules) {
193
255
  this.#assertNotDisposed();
194
- for (const module of modules) {
256
+ // Deduped by identity like the sync lane, so `loadAsync(m, m)` ref-counts once.
257
+ for (const module of new Set(modules)) {
195
258
  await this.#loadOneModuleAsync(module);
196
259
  }
197
260
  }
261
+ /** Undo a failed load — bindings registered before the throw and the ref-count both roll back. */
262
+ #rollbackFailedLoadSync(moduleRef, cause) {
263
+ const pairs = this.#removeModuleBindings(moduleRef);
264
+ try {
265
+ this.#deactivatePairsSync(pairs);
266
+ }
267
+ catch (rollbackError) {
268
+ return new AggregateError([cause, rollbackError], "module load failed, and rolling it back threw too");
269
+ }
270
+ return cause;
271
+ }
272
+ async #rollbackFailedLoad(moduleRef, cause) {
273
+ const pairs = this.#removeModuleBindings(moduleRef);
274
+ try {
275
+ await this.#deactivatePairs(pairs);
276
+ }
277
+ catch (rollbackError) {
278
+ return new AggregateError([cause, rollbackError], "module load failed, and rolling it back threw too");
279
+ }
280
+ return cause;
281
+ }
198
282
  async #loadOneModuleAsync(module) {
199
283
  const moduleRef = module;
200
284
  const moduleRefs = (this.#moduleRefs ??= new Map());
@@ -206,15 +290,28 @@ class DefaultContainer {
206
290
  moduleRefs.set(moduleRef, 1);
207
291
  if (isSyncModule(module)) {
208
292
  const builder = this.#createModuleBuilder(moduleRef);
209
- module[MODULE_SETUP](builder);
293
+ try {
294
+ module[MODULE_SETUP](builder);
295
+ }
296
+ catch (error) {
297
+ throw this.#rollbackFailedLoadSync(moduleRef, error);
298
+ }
210
299
  }
211
300
  else {
212
301
  const importPromises = [];
213
302
  const builder = this.#createAsyncModuleBuilder(moduleRef, importPromises);
214
- await module[MODULE_SETUP](builder);
215
- // Await nested async imports triggered inside the setup callback
216
- if (importPromises.length > 0) {
217
- await Promise.all(importPromises);
303
+ try {
304
+ await module[MODULE_SETUP](builder);
305
+ // Await nested async imports triggered inside the setup callback
306
+ if (importPromises.length > 0) {
307
+ await Promise.all(importPromises);
308
+ }
309
+ }
310
+ catch (error) {
311
+ // Nested imports may still be registering — let them settle before rolling back, so no
312
+ // registration lands after the rejection and none becomes an unhandled rejection.
313
+ await Promise.allSettled(importPromises);
314
+ throw await this.#rollbackFailedLoad(moduleRef, error);
218
315
  }
219
316
  }
220
317
  }
@@ -249,27 +346,19 @@ class DefaultContainer {
249
346
  this.#moduleRefs?.delete(ref);
250
347
  const ids = this.#moduleBindingIds?.get(ref) ?? [];
251
348
  this.#moduleBindingIds?.delete(ref);
252
- const pairs = [];
349
+ const removed = [];
253
350
  for (const id of ids) {
254
- const binding = this.#registry.getById(id);
351
+ const binding = this.#registry.removeById(id);
255
352
  if (binding !== undefined) {
256
- this.#registry.removeById(id);
257
- if (binding.instance !== NO_INSTANCE) {
258
- pairs.push([binding, binding.instance]);
259
- this.#scope.deleteSingleton(binding);
260
- }
261
- this.#scope.deleteScoped(binding.id);
353
+ removed.push(binding);
262
354
  }
263
355
  }
264
- return pairs;
356
+ return this.#drainSingletons(removed);
265
357
  }
266
358
  #unloadModuleSync(ref) {
267
359
  const count = this.#moduleRefs?.get(ref) ?? 0;
268
360
  if (count <= 1) {
269
- const reader = this.#getMetadataReader();
270
- for (const [binding, instance] of this.#removeModuleBindings(ref)) {
271
- this.#lifecycle.runDeactivationSync(binding, instance, reader);
272
- }
361
+ this.#deactivatePairsSync(this.#removeModuleBindings(ref));
273
362
  }
274
363
  else {
275
364
  this.#moduleRefs.set(ref, count - 1);
@@ -284,10 +373,7 @@ class DefaultContainer {
284
373
  async #unloadModuleAsync(ref) {
285
374
  const count = this.#moduleRefs?.get(ref) ?? 0;
286
375
  if (count <= 1) {
287
- const reader = this.#getMetadataReader();
288
- for (const [binding, instance] of this.#removeModuleBindings(ref)) {
289
- await this.#lifecycle.runDeactivation(binding, instance, reader);
290
- }
376
+ await this.#deactivatePairs(this.#removeModuleBindings(ref));
291
377
  }
292
378
  else {
293
379
  this.#moduleRefs.set(ref, count - 1);
@@ -297,21 +383,11 @@ class DefaultContainer {
297
383
  this.#assertNotDisposed();
298
384
  const entries = registry.entries();
299
385
  for (const { target, scope } of entries) {
300
- const builder = this.#createBindToBuilder(target);
301
- const bindingBuilder = builder.toSelf();
302
- if (scope === "singleton") {
303
- bindingBuilder.singleton();
304
- }
305
- else if (scope === "scoped") {
306
- bindingBuilder.scoped();
307
- }
308
- else {
309
- bindingBuilder.transient();
310
- }
386
+ APPLY_BINDING_SCOPE[scope](this.#createBindToBuilder(target).toSelf());
311
387
  }
312
388
  return entries.length;
313
389
  }
314
- // ── Lifecycle hooks ────────────────────────────────────────────────────────
390
+ // ── Lifecycle hooks ────────────────────────────────────────────────────────────────────────────────────────────────
315
391
  onActivation(token, handler) {
316
392
  this.#assertNotDisposed();
317
393
  this.#lifecycle.registerActivation(token, handler);
@@ -320,69 +396,97 @@ class DefaultContainer {
320
396
  this.#assertNotDisposed();
321
397
  this.#lifecycle.registerDeactivation(token, handler);
322
398
  }
323
- // ── Resolution ────────────────────────────────────────────────────────────
399
+ // ── Resolution ─────────────────────────────────────────────────────────────────────────────────────────────────────
324
400
  resolve(token, options) {
325
401
  this.#assertNotDisposed();
326
402
  const rootStack = this.#resolver.rootStack;
327
- // A resolve already holding the shared pair means this one is nested; it mints its own.
403
+ // A resolve already holding the shared stack means this one is nested; it mints its own.
328
404
  if (rootStack.length !== 0) {
329
405
  return options === undefined
330
- ? this.#resolver.resolveFromContext(token, [], [])
331
- : this.#resolver.resolve(token, options, [], []);
406
+ ? this.#resolver.resolveFromContext(token, [])
407
+ : this.#resolver.resolve(token, options, []);
332
408
  }
333
409
  if (options === undefined) {
334
- return this.#resolver.resolveFromContext(token, this.#resolver.rootPath, rootStack);
410
+ return this.#resolver.resolveFromContext(token, rootStack);
335
411
  }
336
- return this.#resolver.resolve(token, options, this.#resolver.rootPath, rootStack);
412
+ return this.#resolver.resolve(token, options, rootStack);
337
413
  }
338
414
  resolveAsync(token, options) {
339
415
  this.#assertNotDisposed();
340
416
  if (options === undefined) {
341
417
  return this.#resolver.resolveAsyncFromRoot(token);
342
418
  }
343
- return this.#resolver.resolveAsync(token, options, [], [], ROOT_BRANCH);
419
+ return this.#resolver.resolveAsync(token, options, [], ROOT_BRANCH);
344
420
  }
345
421
  resolveOptional(token, options) {
346
422
  this.#assertNotDisposed();
347
423
  const rootStack = this.#resolver.rootStack;
348
424
  return rootStack.length === 0
349
- ? this.#resolver.resolveOptional(token, options, this.#resolver.rootPath, rootStack)
350
- : this.#resolver.resolveOptional(token, options, [], []);
425
+ ? this.#resolver.resolveOptional(token, options, rootStack)
426
+ : this.#resolver.resolveOptional(token, options, []);
351
427
  }
352
428
  resolveOptionalAsync(token, options) {
353
429
  this.#assertNotDisposed();
354
- return this.#resolver.resolveOptionalAsync(token, options, [], [], ROOT_BRANCH);
430
+ return this.#resolver.resolveOptionalAsync(token, options, [], ROOT_BRANCH);
355
431
  }
356
432
  resolveAll(token, options) {
357
433
  this.#assertNotDisposed();
358
434
  const rootStack = this.#resolver.rootStack;
359
435
  return rootStack.length === 0
360
- ? this.#resolver.resolveAll(token, options, this.#resolver.rootPath, rootStack)
361
- : this.#resolver.resolveAll(token, options, [], []);
436
+ ? this.#resolver.resolveAll(token, options, rootStack)
437
+ : this.#resolver.resolveAll(token, options, []);
362
438
  }
363
439
  resolveAllAsync(token, options) {
364
440
  this.#assertNotDisposed();
365
- return this.#resolver.resolveAllAsync(token, options, [], [], ROOT_BRANCH);
441
+ return this.#resolver.resolveAllAsync(token, options, [], ROOT_BRANCH);
366
442
  }
367
- // ── Child ─────────────────────────────────────────────────────────────────
443
+ // ── Child ──────────────────────────────────────────────────────────────────────────────────────────────────────────
368
444
  createChild() {
369
445
  this.#assertNotDisposed();
370
446
  return new DefaultContainer(this);
371
447
  }
372
- // ── Dispose ───────────────────────────────────────────────────────────────
373
- async dispose() {
374
- if (this.#disposed) {
375
- return;
376
- }
448
+ // ── Dispose ────────────────────────────────────────────────────────────────────────────────────────────────────────
449
+ dispose() {
450
+ // One teardown, shared: a second caller awaits the same run instead of returning early while
451
+ // hooks from the first are still executing.
452
+ return (this.#disposePromise ??= this.#runDispose());
453
+ }
454
+ async #runDispose() {
377
455
  this.#disposed = true;
378
- // Deactivate all singletons in this container (own only)
456
+ // Refuses new materializations immediately; in-flight ones are drained just below.
457
+ this.#scope.markClosed();
458
+ await this.#scope.settleInflight();
379
459
  const reader = this.#getMetadataReader();
460
+ const errors = [];
380
461
  // Iterate a copy: a deactivation handler is user code, and the live list is what
381
- // materializing or dropping a singleton mutates.
382
- for (const binding of this.#scope.cachedSingletons().slice()) {
383
- await this.#lifecycle.runDeactivation(binding, binding.instance, reader);
462
+ // materializing or dropping a singleton mutates. Reverse materialization order, so a
463
+ // dependent tears down before the dependencies it may still reach through.
464
+ const cached = this.#scope.cachedSingletons().slice();
465
+ for (let index = cached.length - 1; index >= 0; index -= 1) {
466
+ const binding = cached[index];
467
+ try {
468
+ await this.#lifecycle.runDeactivation(binding, binding.instance, reader);
469
+ }
470
+ catch (error) {
471
+ errors.push(error);
472
+ }
473
+ }
474
+ // A constant never reaches the singleton cache unless activation put it there, so its hook is
475
+ // owed from the registry instead — and only a container that has held one pays for looking.
476
+ if (this.#registry.hasHeldConstantBinding) {
477
+ for (const binding of this.#registry.allBindings()) {
478
+ if (binding.instance === NO_INSTANCE && this.#owesConstantDeactivation(binding)) {
479
+ try {
480
+ await this.#lifecycle.runDeactivation(binding, binding.value, reader);
481
+ }
482
+ catch (error) {
483
+ errors.push(error);
484
+ }
485
+ }
486
+ }
384
487
  }
385
488
  this.#scope.clearAll();
489
+ throwCollected(errors, "dispose() completed, but deactivation hooks threw");
386
490
  }
387
491
  [Symbol.asyncDispose]() {
388
492
  return this.dispose();
@@ -390,7 +494,7 @@ class DefaultContainer {
390
494
  [Symbol.dispose]() {
391
495
  throw new SyncDisposalNotSupportedError();
392
496
  }
393
- // ── Initialization ────────────────────────────────────────────────────────
497
+ // ── Initialization ─────────────────────────────────────────────────────────────────────────────────────────────────
394
498
  async initializeAsync() {
395
499
  this.#assertNotDisposed();
396
500
  const allBindings = this.#registry.allBindings();
@@ -403,8 +507,11 @@ class DefaultContainer {
403
507
  if (binding.predicate !== undefined) {
404
508
  continue;
405
509
  }
406
- // Has activation — need to resolve
407
- if (binding.kind === "constant" && binding.onActivation === undefined) {
510
+ // A constant is already its own instance, so only an activation hook gives the warm-up
511
+ // something to run — and a container-level hook counts as one just as a per-binding hook does.
512
+ if (binding.kind === "constant" &&
513
+ binding.onActivation === undefined &&
514
+ !this.#lifecycle.hasActivationHandlers(binding.token)) {
408
515
  continue;
409
516
  }
410
517
  const slotOptions = bindingSlotToResolveOptions(binding.slot);
@@ -412,7 +519,7 @@ class DefaultContainer {
412
519
  }
413
520
  }
414
521
  }
415
- // ── Validate ──────────────────────────────────────────────────────────────
522
+ // ── Validate ───────────────────────────────────────────────────────────────────────────────────────────────────────
416
523
  validate() {
417
524
  this.#assertNotDisposed();
418
525
  const reader = this.#getMetadataReader();
@@ -423,6 +530,48 @@ class DefaultContainer {
423
530
  }
424
531
  this.#validateSingletonBindingGraph(binding, reader);
425
532
  }
533
+ for (const [hookToken, phase] of this.#lifecycle.hookedTokens()) {
534
+ if (!this.#isBoundInChain(hookToken)) {
535
+ throw new UnreachableLifecycleHookError(tokenName(hookToken), phase);
536
+ }
537
+ }
538
+ this.#validateConstraintRequirements(allBindings);
539
+ }
540
+ /** A constraint waiting on a slot name no binding declares can never hold. */
541
+ #validateConstraintRequirements(allBindings) {
542
+ let declaredSlotNames;
543
+ for (const binding of allBindings) {
544
+ const { predicate } = binding;
545
+ if (predicate === undefined) {
546
+ continue;
547
+ }
548
+ const requirement = constraintRequirementOf(predicate);
549
+ if (requirement === undefined) {
550
+ continue;
551
+ }
552
+ declaredSlotNames ??= this.#slotNamesInChain();
553
+ if (!declaredSlotNames.has(requirement.name)) {
554
+ throw new UnreachableConstraintError(tokenName(binding.token), requirement.name, requirement.helperName);
555
+ }
556
+ }
557
+ }
558
+ /** Every slot name declared anywhere a resolve through this container could reach. */
559
+ #slotNamesInChain() {
560
+ const names = this.#parent === undefined ? new Set() : this.#parent.#slotNamesInChain();
561
+ for (const binding of this.#registry.allBindings()) {
562
+ if (binding.slot.name !== undefined) {
563
+ names.add(binding.slot.name);
564
+ }
565
+ }
566
+ return names;
567
+ }
568
+ // Ancestors count: a parent-owned binding is one this container can still resolve through.
569
+ #isBoundInChain(token) {
570
+ if (this.#registry.has(token)) {
571
+ return true;
572
+ }
573
+ const parent = this.#parent;
574
+ return parent !== undefined && parent.#isBoundInChain(token);
426
575
  }
427
576
  #isSingletonStaticAnalyzableBinding(binding) {
428
577
  if (effectiveBindingScope(binding) !== "singleton") {
@@ -432,7 +581,7 @@ class DefaultContainer {
432
581
  }
433
582
  /**
434
583
  * DFS over explicit constructor / `toResolved*` dependency edges. Follows `toAlias` chains to the
435
- * terminal binding for scope checks (SPEC §6.9).
584
+ * terminal binding for scope checks.
436
585
  *
437
586
  * @remarks A `toDynamic*` dependency is scope-checked like any other — its declared scope is what
438
587
  * makes it captive — but the DFS does not descend into the factory, whose body is opaque.
@@ -531,7 +680,7 @@ class DefaultContainer {
531
680
  }
532
681
  return edges;
533
682
  }
534
- // ── Introspection ─────────────────────────────────────────────────────────
683
+ // ── Introspection ──────────────────────────────────────────────────────────────────────────────────────────────────
535
684
  has(token, options) {
536
685
  this.#assertNotDisposed();
537
686
  return this.#getInspector().has(token, options, () => this.#parent?.has(token, options) ?? false);
@@ -552,21 +701,33 @@ class DefaultContainer {
552
701
  this.#assertNotDisposed();
553
702
  return buildDependencyGraph(this.#registry, this.#getMetadataReader(), options, this.#parent === undefined ? undefined : this.#parent.#registry);
554
703
  }
555
- // ── Internal ──────────────────────────────────────────────────────────────
704
+ // ── Internal ───────────────────────────────────────────────────────────────────────────────────────────────────────
556
705
  #assertNotDisposed() {
557
706
  if (this.#disposed) {
558
707
  throw new DisposedContainerError();
559
708
  }
560
709
  }
561
710
  }
562
- // ── Container static ──────────────────────────────────────────────────────────
711
+ /** Reports collected hook failures once teardown finished: the one error as itself, several aggregated. */
712
+ function throwCollected(errors, message) {
713
+ if (errors.length === 0) {
714
+ return;
715
+ }
716
+ if (errors.length === 1) {
717
+ throw errors[0];
718
+ }
719
+ throw new AggregateError(errors, message);
720
+ }
721
+ // ── Container static ─────────────────────────────────────────────────────────────────────────────────────────────────
563
722
  /**
564
723
  * @since 0.3.16-canary.0
565
724
  */
566
725
  export const Container = {
567
- create() {
568
- return new DefaultContainer();
726
+ create(options) {
727
+ return new DefaultContainer(undefined, options);
569
728
  },
729
+ // Variadic modules leave no room for an options argument. A container that needs both is
730
+ // `Container.create(options)` followed by `load(...)`, which is what these two do anyway.
570
731
  fromModules(...modules) {
571
732
  const container = new DefaultContainer();
572
733
  container.load(...modules);