@nexus-ioc/core 0.6.2 → 0.7.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 (220) hide show
  1. package/README.md +731 -1
  2. package/dist/cjs/browser.js +2 -2
  3. package/dist/cjs/browser.js.map +1 -1
  4. package/dist/cjs/core/child-module.js +32 -0
  5. package/dist/cjs/core/child-module.js.map +1 -0
  6. package/dist/cjs/core/graph/analyze-lazy-module.js +54 -0
  7. package/dist/cjs/core/graph/analyze-lazy-module.js.map +1 -0
  8. package/dist/cjs/core/graph/analyze-module.js +11 -0
  9. package/dist/cjs/core/graph/analyze-module.js.map +1 -1
  10. package/dist/cjs/core/graph/module-graph.js +685 -70
  11. package/dist/cjs/core/graph/module-graph.js.map +1 -1
  12. package/dist/cjs/core/injection/injection-context.js +29 -0
  13. package/dist/cjs/core/injection/injection-context.js.map +1 -0
  14. package/dist/cjs/core/lazy-module-loader.js +41 -0
  15. package/dist/cjs/core/lazy-module-loader.js.map +1 -0
  16. package/dist/cjs/core/module-ref.js +84 -0
  17. package/dist/cjs/core/module-ref.js.map +1 -0
  18. package/dist/cjs/core/modules/container.js +305 -17
  19. package/dist/cjs/core/modules/container.js.map +1 -1
  20. package/dist/cjs/core/modules/module-container-factory.js +2 -2
  21. package/dist/cjs/core/modules/module-container-factory.js.map +1 -1
  22. package/dist/cjs/core/modules/module-container.js +54 -16
  23. package/dist/cjs/core/modules/module-container.js.map +1 -1
  24. package/dist/cjs/core/modules/module-token-factory.js.map +1 -1
  25. package/dist/cjs/core/modules/modules-container.js +43 -3
  26. package/dist/cjs/core/modules/modules-container.js.map +1 -1
  27. package/dist/cjs/core/nexus-applications.browser.js +57 -15
  28. package/dist/cjs/core/nexus-applications.browser.js.map +1 -1
  29. package/dist/cjs/core/nexus-applications.js +440 -82
  30. package/dist/cjs/core/nexus-applications.js.map +1 -1
  31. package/dist/cjs/core/nexus-applications.server.js +57 -15
  32. package/dist/cjs/core/nexus-applications.server.js.map +1 -1
  33. package/dist/cjs/core/resolver/providers-container.js.map +1 -1
  34. package/dist/cjs/core/resolver/resolution-context.js +62 -0
  35. package/dist/cjs/core/resolver/resolution-context.js.map +1 -0
  36. package/dist/cjs/core/resolver/resolver.js +523 -99
  37. package/dist/cjs/core/resolver/resolver.js.map +1 -1
  38. package/dist/cjs/decorators/inject.js +6 -3
  39. package/dist/cjs/decorators/inject.js.map +1 -1
  40. package/dist/cjs/decorators/injectable.js +0 -7
  41. package/dist/cjs/decorators/injectable.js.map +1 -1
  42. package/dist/cjs/decorators/{nsModule.js → module.js} +9 -7
  43. package/dist/cjs/decorators/module.js.map +1 -0
  44. package/dist/cjs/errors/bootstrap-error.js +47 -0
  45. package/dist/cjs/errors/bootstrap-error.js.map +1 -0
  46. package/dist/cjs/errors/child-errors.js +21 -0
  47. package/dist/cjs/errors/child-errors.js.map +1 -0
  48. package/dist/cjs/errors/index.js +24 -0
  49. package/dist/cjs/errors/index.js.map +1 -0
  50. package/dist/cjs/errors/injection-errors.js +25 -0
  51. package/dist/cjs/errors/injection-errors.js.map +1 -0
  52. package/dist/cjs/errors/lazy-module-errors.js +26 -0
  53. package/dist/cjs/errors/lazy-module-errors.js.map +1 -0
  54. package/dist/cjs/errors/provider-errors.js +95 -0
  55. package/dist/cjs/errors/provider-errors.js.map +1 -0
  56. package/dist/cjs/index.js +30 -6
  57. package/dist/cjs/index.js.map +1 -1
  58. package/dist/cjs/interfaces/index.js +1 -0
  59. package/dist/cjs/interfaces/index.js.map +1 -1
  60. package/dist/cjs/interfaces/modules/index.js +1 -0
  61. package/dist/cjs/interfaces/modules/index.js.map +1 -1
  62. package/dist/cjs/interfaces/modules/module-graph.interface.js +2 -0
  63. package/dist/cjs/interfaces/modules/module-graph.interface.js.map +1 -1
  64. package/dist/cjs/interfaces/modules/parent-link.interface.js +3 -0
  65. package/dist/cjs/interfaces/modules/parent-link.interface.js.map +1 -0
  66. package/dist/cjs/interfaces/on-module-destroy.interface.js +3 -0
  67. package/dist/cjs/interfaces/on-module-destroy.interface.js.map +1 -0
  68. package/dist/cjs/interfaces/scope.interface.js +2 -55
  69. package/dist/cjs/interfaces/scope.interface.js.map +1 -1
  70. package/dist/cjs/interfaces/validate-module-keys.js +2 -0
  71. package/dist/cjs/interfaces/validate-module-keys.js.map +1 -1
  72. package/dist/cjs/internal.js +9 -0
  73. package/dist/cjs/internal.js.map +1 -0
  74. package/dist/cjs/server.js +2 -2
  75. package/dist/cjs/server.js.map +1 -1
  76. package/dist/cjs/utils/forward-ref.js +14 -0
  77. package/dist/cjs/utils/forward-ref.js.map +1 -0
  78. package/dist/cjs/utils/helpers.js +14 -4
  79. package/dist/cjs/utils/helpers.js.map +1 -1
  80. package/dist/cjs/utils/lazy-module.js +33 -0
  81. package/dist/cjs/utils/lazy-module.js.map +1 -0
  82. package/dist/esm/browser.js +1 -1
  83. package/dist/esm/browser.js.map +1 -1
  84. package/dist/esm/core/child-module.js +29 -0
  85. package/dist/esm/core/child-module.js.map +1 -0
  86. package/dist/esm/core/graph/analyze-lazy-module.js +50 -0
  87. package/dist/esm/core/graph/analyze-lazy-module.js.map +1 -0
  88. package/dist/esm/core/graph/analyze-module.js +11 -0
  89. package/dist/esm/core/graph/analyze-module.js.map +1 -1
  90. package/dist/esm/core/graph/module-graph.js +686 -71
  91. package/dist/esm/core/graph/module-graph.js.map +1 -1
  92. package/dist/esm/core/injection/injection-context.js +25 -0
  93. package/dist/esm/core/injection/injection-context.js.map +1 -0
  94. package/dist/esm/core/lazy-module-loader.js +36 -0
  95. package/dist/esm/core/lazy-module-loader.js.map +1 -0
  96. package/dist/esm/core/module-ref.js +80 -0
  97. package/dist/esm/core/module-ref.js.map +1 -0
  98. package/dist/esm/core/modules/container.js +305 -17
  99. package/dist/esm/core/modules/container.js.map +1 -1
  100. package/dist/esm/core/modules/module-container-factory.js +2 -2
  101. package/dist/esm/core/modules/module-container-factory.js.map +1 -1
  102. package/dist/esm/core/modules/module-container.js +55 -17
  103. package/dist/esm/core/modules/module-container.js.map +1 -1
  104. package/dist/esm/core/modules/module-token-factory.js.map +1 -1
  105. package/dist/esm/core/modules/modules-container.js +43 -3
  106. package/dist/esm/core/modules/modules-container.js.map +1 -1
  107. package/dist/esm/core/nexus-applications.browser.js +55 -13
  108. package/dist/esm/core/nexus-applications.browser.js.map +1 -1
  109. package/dist/esm/core/nexus-applications.js +439 -81
  110. package/dist/esm/core/nexus-applications.js.map +1 -1
  111. package/dist/esm/core/nexus-applications.server.js +55 -13
  112. package/dist/esm/core/nexus-applications.server.js.map +1 -1
  113. package/dist/esm/core/resolver/providers-container.js.map +1 -1
  114. package/dist/esm/core/resolver/resolution-context.js +58 -0
  115. package/dist/esm/core/resolver/resolution-context.js.map +1 -0
  116. package/dist/esm/core/resolver/resolver.js +524 -100
  117. package/dist/esm/core/resolver/resolver.js.map +1 -1
  118. package/dist/esm/decorators/inject.js +6 -3
  119. package/dist/esm/decorators/inject.js.map +1 -1
  120. package/dist/esm/decorators/injectable.js +0 -7
  121. package/dist/esm/decorators/injectable.js.map +1 -1
  122. package/dist/esm/decorators/{nsModule.js → module.js} +8 -6
  123. package/dist/esm/decorators/module.js.map +1 -0
  124. package/dist/esm/errors/bootstrap-error.js +42 -0
  125. package/dist/esm/errors/bootstrap-error.js.map +1 -0
  126. package/dist/esm/errors/child-errors.js +17 -0
  127. package/dist/esm/errors/child-errors.js.map +1 -0
  128. package/dist/esm/errors/index.js +6 -0
  129. package/dist/esm/errors/index.js.map +1 -0
  130. package/dist/esm/errors/injection-errors.js +20 -0
  131. package/dist/esm/errors/injection-errors.js.map +1 -0
  132. package/dist/esm/errors/lazy-module-errors.js +21 -0
  133. package/dist/esm/errors/lazy-module-errors.js.map +1 -0
  134. package/dist/esm/errors/provider-errors.js +84 -0
  135. package/dist/esm/errors/provider-errors.js.map +1 -0
  136. package/dist/esm/index.js +9 -4
  137. package/dist/esm/index.js.map +1 -1
  138. package/dist/esm/interfaces/index.js +1 -0
  139. package/dist/esm/interfaces/index.js.map +1 -1
  140. package/dist/esm/interfaces/modules/index.js +1 -0
  141. package/dist/esm/interfaces/modules/index.js.map +1 -1
  142. package/dist/esm/interfaces/modules/module-graph.interface.js +2 -0
  143. package/dist/esm/interfaces/modules/module-graph.interface.js.map +1 -1
  144. package/dist/esm/interfaces/modules/parent-link.interface.js +2 -0
  145. package/dist/esm/interfaces/modules/parent-link.interface.js.map +1 -0
  146. package/dist/esm/interfaces/on-module-destroy.interface.js +2 -0
  147. package/dist/esm/interfaces/on-module-destroy.interface.js.map +1 -0
  148. package/dist/esm/interfaces/scope.interface.js +1 -55
  149. package/dist/esm/interfaces/scope.interface.js.map +1 -1
  150. package/dist/esm/interfaces/validate-module-keys.js +2 -0
  151. package/dist/esm/interfaces/validate-module-keys.js.map +1 -1
  152. package/dist/esm/internal.js +3 -0
  153. package/dist/esm/internal.js.map +1 -0
  154. package/dist/esm/server.js +1 -1
  155. package/dist/esm/server.js.map +1 -1
  156. package/dist/esm/utils/forward-ref.js +10 -0
  157. package/dist/esm/utils/forward-ref.js.map +1 -0
  158. package/dist/esm/utils/helpers.js +14 -4
  159. package/dist/esm/utils/helpers.js.map +1 -1
  160. package/dist/esm/utils/lazy-module.js +29 -0
  161. package/dist/esm/utils/lazy-module.js.map +1 -0
  162. package/dist/types/browser.d.ts +1 -1
  163. package/dist/types/core/child-module.d.ts +8 -0
  164. package/dist/types/core/graph/analyze-lazy-module.d.ts +28 -0
  165. package/dist/types/core/graph/analyze-module.d.ts +4 -2
  166. package/dist/types/core/graph/analyze-provider.d.ts +4 -4
  167. package/dist/types/core/graph/module-graph.d.ts +200 -7
  168. package/dist/types/core/graph/providers/analyze-class-provider.d.ts +1 -1
  169. package/dist/types/core/graph/providers/analyze-factory-provider.d.ts +2 -2
  170. package/dist/types/core/graph/providers/analyze-value-provider.d.ts +1 -1
  171. package/dist/types/core/injection/injection-context.d.ts +29 -0
  172. package/dist/types/core/lazy-module-loader.d.ts +17 -0
  173. package/dist/types/core/module-ref.d.ts +34 -0
  174. package/dist/types/core/modules/container.d.ts +134 -18
  175. package/dist/types/core/modules/module-container-factory.d.ts +3 -3
  176. package/dist/types/core/modules/module-container.d.ts +44 -5
  177. package/dist/types/core/modules/module-token-factory.d.ts +2 -2
  178. package/dist/types/core/modules/modules-container.d.ts +23 -4
  179. package/dist/types/core/nexus-applications.browser.d.ts +30 -8
  180. package/dist/types/core/nexus-applications.d.ts +219 -55
  181. package/dist/types/core/nexus-applications.server.d.ts +30 -8
  182. package/dist/types/core/resolver/providers-container.d.ts +2 -2
  183. package/dist/types/core/resolver/resolution-context.d.ts +22 -0
  184. package/dist/types/core/resolver/resolver.d.ts +122 -4
  185. package/dist/types/decorators/inject.d.ts +9 -5
  186. package/dist/types/decorators/injectable.d.ts +0 -7
  187. package/dist/types/decorators/{nsModule.d.ts → module.d.ts} +6 -5
  188. package/dist/types/errors/bootstrap-error.d.ts +6 -0
  189. package/dist/types/errors/child-errors.d.ts +10 -0
  190. package/dist/types/errors/index.d.ts +5 -0
  191. package/dist/types/errors/injection-errors.d.ts +11 -0
  192. package/dist/types/errors/lazy-module-errors.d.ts +12 -0
  193. package/dist/types/errors/provider-errors.d.ts +47 -0
  194. package/dist/types/index.d.ts +13 -4
  195. package/dist/types/interfaces/abstract.interface.d.ts +1 -3
  196. package/dist/types/interfaces/dynamic-module.interface.d.ts +7 -5
  197. package/dist/types/interfaces/index.d.ts +1 -0
  198. package/dist/types/interfaces/injection-token.interface.d.ts +1 -3
  199. package/dist/types/interfaces/module-metadata.interface.d.ts +12 -4
  200. package/dist/types/interfaces/module-types.interface.d.ts +21 -24
  201. package/dist/types/interfaces/modules/container.interface.d.ts +10 -4
  202. package/dist/types/interfaces/modules/index.d.ts +1 -0
  203. package/dist/types/interfaces/modules/module-container-factory.interface.d.ts +2 -2
  204. package/dist/types/interfaces/modules/module-graph.interface.d.ts +85 -12
  205. package/dist/types/interfaces/modules/module-token-factory.interface.d.ts +2 -2
  206. package/dist/types/interfaces/modules/modules-container.interface.d.ts +5 -4
  207. package/dist/types/interfaces/modules/parent-link.interface.d.ts +12 -0
  208. package/dist/types/interfaces/nexus-application.interface.d.ts +49 -6
  209. package/dist/types/interfaces/on-module-destroy.interface.d.ts +3 -0
  210. package/dist/types/interfaces/plugins/scanner-plugin.interface.d.ts +1 -1
  211. package/dist/types/interfaces/scope.interface.d.ts +1 -54
  212. package/dist/types/interfaces/validate-module-keys.d.ts +1 -0
  213. package/dist/types/internal.d.ts +3 -0
  214. package/dist/types/server.d.ts +1 -1
  215. package/dist/types/utils/forward-ref.d.ts +5 -0
  216. package/dist/types/utils/helpers.d.ts +8 -8
  217. package/dist/types/utils/lazy-module.d.ts +20 -0
  218. package/package.json +21 -4
  219. package/dist/cjs/decorators/nsModule.js.map +0 -1
  220. package/dist/esm/decorators/nsModule.js.map +0 -1
package/README.md CHANGED
@@ -20,6 +20,13 @@ Nexus IoC is a powerful and flexible Inversion of Control (IoC) container for Ty
20
20
  - [Features](#features)
21
21
  - [Installation](#installation)
22
22
  - [Quick Start](#quick-start)
23
+ - [Tokens](#tokens)
24
+ - [inject()](#inject)
25
+ - [get() vs resolve()](#get-vs-resolve)
26
+ - [Lifecycle and close()](#lifecycle-and-close)
27
+ - [Lazy Modules](#lazy-modules)
28
+ - [Child containers](#child-containers)
29
+ - [Migrating to the synchronous get()](#migrating-to-the-synchronous-get)
23
30
  - [Testing](#testing)
24
31
  - [License](#license)
25
32
  - [Author](#author)
@@ -95,6 +102,673 @@ bootstrap();
95
102
 
96
103
  ```
97
104
 
105
+ `get()` is synchronous: singletons are already created when `bootstrap()`
106
+ resolves.
107
+
108
+ ## Tokens
109
+
110
+ A provider is identified by a token: a class, an abstract class, a string, a
111
+ symbol or an `InjectionToken<T>`. The type that describes all of these is
112
+ `ProviderToken<T>`.
113
+
114
+ `InjectionToken<T>` is a typed token for values that have no class, such as
115
+ configuration:
116
+
117
+ ```typescript
118
+ import { Inject, Injectable, InjectionToken, Module } from '@nexus-ioc/core';
119
+
120
+ interface Config {
121
+ apiUrl: string;
122
+ }
123
+
124
+ export const CONFIG = new InjectionToken<Config>('CONFIG');
125
+
126
+ @Injectable()
127
+ class ApiClient {
128
+ constructor(@Inject(CONFIG) private readonly config: Config) {}
129
+ }
130
+
131
+ @Module({
132
+ providers: [
133
+ { provide: CONFIG, useValue: { apiUrl: 'https://example.com' } },
134
+ ApiClient,
135
+ ],
136
+ exports: [CONFIG],
137
+ })
138
+ class ApiModule {}
139
+ ```
140
+
141
+ - Use it as `provide` with `useValue`, `useFactory` or `useClass`, with
142
+ `@Inject(TOKEN)` on constructor parameters and properties, with
143
+ `@Optional()`, and in module `exports`.
144
+ - Tokens are compared by identity. `description` is only a label, so two
145
+ `new InjectionToken('CONFIG')` instances are different tokens. Create the
146
+ token once and import the constant where it is needed.
147
+ - `token.toString()` returns `InjectionToken CONFIG`, and error messages use it.
148
+
149
+ `get()` and `resolve()` infer the result type from the token. This works for
150
+ `NexusApplication`, `ModuleRef`, `Test` and the object returned by
151
+ `Test.compile()`:
152
+
153
+ ```typescript
154
+ const config = app.get(CONFIG); // Config
155
+ const api = app.get(ApiClient); // ApiClient
156
+ const raw = app.get('API_URL'); // unknown
157
+ const url = app.get<string>('API_URL'); // string
158
+ ```
159
+
160
+ Class tokens, including abstract classes, and `InjectionToken<T>` are
161
+ inferred. String and symbol tokens give `unknown` unless you pass `<T>` or the
162
+ expected type supplies it (`const url: string = app.get('API_URL')`).
163
+ `useValue` and `useFactory` results are not type-checked against the token's
164
+ `T`, so make sure the provided value matches it.
165
+
166
+ A class with a `private` or `protected` constructor cannot be used as a token,
167
+ because the container cannot construct it. Use an `InjectionToken` instead.
168
+
169
+ ## inject()
170
+
171
+ `inject()` resolves a dependency from code instead of a constructor
172
+ parameter, in the style of Angular:
173
+
174
+ ```typescript
175
+ import { Inject, Injectable, InjectionToken, inject } from '@nexus-ioc/core';
176
+
177
+ const CONFIG = new InjectionToken<{ apiUrl: string }>('CONFIG');
178
+
179
+ @Injectable()
180
+ class Logger {}
181
+
182
+ // with @Inject
183
+ @Injectable()
184
+ class ApiClientA {
185
+ constructor(
186
+ private readonly logger: Logger,
187
+ @Inject(CONFIG) private readonly config: { apiUrl: string },
188
+ ) {}
189
+ }
190
+
191
+ // with inject()
192
+ @Injectable()
193
+ class ApiClientB {
194
+ private readonly logger = inject(Logger);
195
+ private readonly config = inject(CONFIG);
196
+ }
197
+ ```
198
+
199
+ Signatures:
200
+
201
+ - `inject(token)` returns `T` and throws `ProviderNotFoundError` if the token is
202
+ not visible.
203
+ - `inject(token, { optional: true })` returns `T | undefined`.
204
+ - `inject(token, options: InjectOptions)` returns `T | undefined`.
205
+
206
+ ### Where inject() works
207
+
208
+ Only synchronously, while the container is creating a provider:
209
+
210
+ - class field initializers;
211
+ - the constructor body and constructor parameter defaults;
212
+ - a `useFactory` function, before its first `await`;
213
+ - the same places in providers that are created as a consequence of an
214
+ `inject()` call.
215
+
216
+ Anywhere else `inject()` throws `InjectionContextError`: in `onModuleInit`
217
+ (including that of providers created through `inject()`), in methods, after an
218
+ `await`, and at module scope.
219
+
220
+ ```typescript
221
+ const FEATURES = new InjectionToken<string[]>('FEATURES');
222
+
223
+ @Module({
224
+ providers: [
225
+ Logger,
226
+ {
227
+ provide: FEATURES,
228
+ useFactory: () => {
229
+ const logger = inject(Logger); // ok: inside a factory function
230
+ return logger ? ['a', 'b'] : [];
231
+ },
232
+ },
233
+ ],
234
+ })
235
+ class FeaturesModule {}
236
+ ```
237
+
238
+ ### Visibility and optional
239
+
240
+ `inject()` follows the same rules as `@Inject`: the module of the requesting
241
+ provider must see the token (its own providers, exports of imported modules,
242
+ global modules). Otherwise `inject()` throws `ProviderNotFoundError`, whose
243
+ message names the requester (`requested by "Y" via inject()`; the error has a
244
+ `requestedBy` property). With `{ optional: true }` it returns `undefined`.
245
+
246
+ ### Scopes
247
+
248
+ - Singleton: the cached instance, or a new one created synchronously on the
249
+ first `inject()`.
250
+ - Transient: a new instance on every `inject()` call.
251
+
252
+ ### Async providers
253
+
254
+ A `useFactory` must return its value synchronously. A factory that returns a
255
+ promise (any thenable) throws `AsyncFactoryError`, which names the token, when
256
+ the provider is created: at `bootstrap()`, on `resolve()` in lazy mode, or from
257
+ `inject()`. A returned promise never becomes an unhandled rejection. Move async
258
+ setup into an `onModuleInit` (of the provided class, or of the object the
259
+ factory returns), or create the value before bootstrap and register it with
260
+ `useValue`.
261
+
262
+ An `onModuleInit` of an injected dependency may be async: it completes before
263
+ the requester's own `onModuleInit` runs. A singleton whose async init fails is
264
+ not left cached, and neither is a provider that holds it; the wrapped error has
265
+ the original as `.cause`. When a singleton was created by `inject()`, another
266
+ resolution (for example a concurrent `resolve()`) that meets it while its init
267
+ is still running waits for that init.
268
+
269
+ Concurrent `resolve()` calls that reach the same singleton before it exists
270
+ (directly or through `@Inject`, for example in `.lazy()` mode) create it once:
271
+ the later calls wait for the first creation, including its `onModuleInit`. If
272
+ that creation fails, every waiting call rejects with the same error and a later
273
+ `resolve()` tries again. `inject()` is synchronous and cannot wait, so an
274
+ `inject()` that meets a singleton another `resolve()` is still creating throws
275
+ `ProviderNotInstantiatedError`; await that `resolve()` first.
276
+
277
+ A dependency obtained with `inject()` may still be completing its async
278
+ `onModuleInit` while your constructor or factory body runs; it is guaranteed to
279
+ be done only before your own `onModuleInit`. For a dependency you must use in
280
+ the constructor or factory body after its async init, use `@Inject` (or the
281
+ factory's `inject: [...]`): when the requester is created by `bootstrap()` or
282
+ `resolve()`, that resolves the dependency fully first. When the requester is
283
+ itself created through `inject()`, its `@Inject` dependencies get the same
284
+ guarantee as `inject()` ones: done only before its own `onModuleInit`. Until
285
+ that init settles, `app.get()` of such a singleton throws
286
+ `ProviderNotInstantiatedError`, just as for a singleton still being created by
287
+ `resolve()`. Don't resolve providers that depend on a provider from that
288
+ provider's own `onModuleInit` (for example `await app.resolve(A)` in the
289
+ `onModuleInit` of `S`, where `A` injects `S`): the resolution waits for the
290
+ init that is waiting for it, and never settles. This is not detected, because
291
+ the inner `resolve()` cannot be told apart from an unrelated concurrent one.
292
+
293
+ Under concurrency a `forwardRef` dependency may be the proxy rather than the
294
+ real instance.
295
+
296
+ ### Cycles
297
+
298
+ Any cycle that goes through `inject()` throws `CircularInjectionError` with the
299
+ path, for example `A -> B -> A`.
300
+
301
+ ### Validation and tooling
302
+
303
+ `inject()` dependencies are found at runtime, so compile-time graph validation
304
+ does not see them and errors surface when the provider is created (for
305
+ singletons, in `bootstrap()`). They are recorded as runtime edges
306
+ (`inject: "runtime"`) in the module graph, so unloading a lazy module is
307
+ protected against modules used via `inject()` the same way as for `@Inject`.
308
+ One consequence: a provider of an always-loaded module (for example the root
309
+ module) that `inject()`s from a loaded lazy `@Global()` module keeps that
310
+ module loaded, so `unload()` of it becomes a no-op. Static `@Optional()`
311
+ dependencies follow the same visibility rule (`undefined` when the provider is
312
+ invisible) and pin the module of a later-loaded provider the same way
313
+ `inject()` does.
314
+
315
+ The static type-checker and graph-analyzer do see `inject()` calls, including
316
+ aliased imports. They report them with `location: "inject"` and include them
317
+ in graphs and cycle detection. They do not see namespace calls
318
+ (`core.inject()`) or factories declared in another place.
319
+
320
+ ### Testing
321
+
322
+ `inject()` works with `Test.createModule(...).compile()`; see
323
+ [@nexus-ioc/testing](../testing/README.md).
324
+
325
+ ## get() vs resolve()
326
+
327
+ - `app.get(token)` is synchronous and returns an already created singleton.
328
+ - `await app.resolve(token)` creates the instance on demand. Use it for
329
+ `Transient` providers and for providers in lazy mode.
330
+
331
+ ```typescript
332
+ const singleton = app.get<AppService>(AppService);
333
+ const transient = await app.resolve<TransientService>(TransientService);
334
+
335
+ // Pass { optional: true } to get undefined instead of an error for an
336
+ // unknown token.
337
+ const maybe = app.get<AppService>(AppService, { optional: true });
338
+ ```
339
+
340
+ `get()` throws:
341
+
342
+ - `ProviderNotFoundError` — the token is not a provider in this container
343
+ (or what its parent link provides). With `{ optional: true }` it returns
344
+ `undefined` instead.
345
+ - `ProviderScopeError` — the provider is `Transient`; use
346
+ `resolve()`.
347
+ - `ProviderNotInstantiatedError` — the singleton has not been created yet
348
+ (`.lazy()` mode, or a lazy module that is not loaded); use `resolve()` or
349
+ `load()` first.
350
+ - `ContainerClosedError` — the container is closed or closing.
351
+ - `ContainerNotCompiledError` — `bootstrap()` has not run yet. The parent is
352
+ not consulted in this case.
353
+
354
+ A token found in this container is always answered here; only unknown tokens
355
+ are looked up in the parent (for a child made with `createChild()`, through its link). Falsy
356
+ `useValue` values (`undefined`, `null`, `0`, `false`) are returned as they are.
357
+
358
+ `bootstrap()` rejects with `InvalidScopeError` for a provider whose `scope` is
359
+ not a `Scope` member (`Singleton` or `Transient`).
360
+
361
+ `resolve()` takes the same `{ optional }` option. It throws
362
+ `ProviderNotFoundError`, `ContainerClosedError` and
363
+ `ContainerNotCompiledError` the same way.
364
+
365
+ ## Lifecycle and close()
366
+
367
+ `onModuleInit` runs during `bootstrap()`. `await app.close()` runs
368
+ `onModuleDestroy` on every provider in reverse creation order:
369
+
370
+ ```typescript
371
+ @Injectable()
372
+ class Connection implements OnModuleDestroy {
373
+ async onModuleDestroy() {
374
+ await this.socket.end();
375
+ }
376
+ }
377
+
378
+ await app.close();
379
+ ```
380
+
381
+ - A hook that throws does not stop the others. After all of them have run,
382
+ `close()` throws one `AggregateError` with every failure.
383
+ - `close()` is idempotent: it returns the same promise on every call, and a
384
+ failed close keeps returning the same rejected promise.
385
+ - It first waits for in-flight lazy module loads. A lazy loader that never
386
+ settles makes `close()` hang.
387
+ - From the moment `close()` is called (before the in-flight loads have
388
+ drained), `get`, `resolve`, `load` and `unload` throw
389
+ `ContainerClosedError`. A load that was already in flight still finishes,
390
+ and its singletons are destroyed with the rest.
391
+ - A `ModuleRef` follows its own visibility rules first: for a token the
392
+ module can see, `ref.get()`/`ref.resolve()` throw `ContainerClosedError`
393
+ after `close()`; for a token it cannot see (or after `unload()`), they
394
+ keep returning `undefined`. `ref.unload()` after `close()` rejects with
395
+ `ContainerClosedError`.
396
+ - `close()` first closes the live child containers (see
397
+ [Child containers](#child-containers)), newest first; their errors join the
398
+ `AggregateError`. A service that subscribed to a parent service must
399
+ unsubscribe in `onModuleDestroy`.
400
+ - A `resolve()` that is still in flight when `close()` is called is not
401
+ awaited, so await your `resolve()` calls before closing.
402
+ - A singleton that such a `resolve()` is still creating when `close()` runs,
403
+ or when an unload destroys its module, is not cached. Its `onModuleDestroy`
404
+ is called on a best-effort basis, and the `resolve()` rejects with
405
+ `ContainerClosedError` (after `close()`) or `ProviderUnloadedError` (after
406
+ an unload).
407
+
408
+ ## Lazy Modules
409
+
410
+ Declare a module import as lazy to keep it out of the initial bundle. The
411
+ module is loaded into the same container on demand and its providers become
412
+ resolvable afterwards.
413
+
414
+ ```typescript
415
+ import { lazy, Module, NexusApplication } from '@nexus-ioc/core';
416
+
417
+ export const FeatureLazy = lazy(() =>
418
+ import('./feature/feature.module').then((m) => m.FeatureModule),
419
+ );
420
+
421
+ @Module({ imports: [CoreModule, FeatureLazy] })
422
+ export class AppModule {}
423
+
424
+ const app = await NexusApplication.create(AppModule).bootstrap();
425
+ const ref = await app.load(FeatureLazy);
426
+ const service = ref.get<FeatureService>(FeatureService);
427
+ ```
428
+
429
+ `lazy()` also takes a `name`, used in diagnostics, in error messages and, by
430
+ the Vite plugin, for chunk naming. Without it every ref is called
431
+ `"LazyModule"`, so name the ones you want to recognize:
432
+
433
+ ```typescript
434
+ export const FeatureLazy = lazy(
435
+ () => import('./feature/feature.module').then((m) => m.FeatureModule),
436
+ { name: 'Feature' },
437
+ );
438
+ ```
439
+
440
+ Rules:
441
+
442
+ - Eager providers cannot depend on providers of a not yet loaded module;
443
+ `bootstrap()` reports it as a missing provider.
444
+ - Loading is idempotent: the loader runs once per `lazy()` ref.
445
+ - Modules already in the container (for example a `SharedModule` imported by
446
+ both the root and the lazy module) are reused, so singletons are shared.
447
+ - A lazy module that registers a token another module already provides fails
448
+ with `PROVIDER_TOKEN_CONFLICT` and is rolled back.
449
+
450
+ ### ModuleRef.get() and ModuleRef.resolve()
451
+
452
+ `ModuleRef.get()` is synchronous and `ModuleRef.resolve()` is async, with the
453
+ same split as on the application: `get()` for singletons, `resolve()` for
454
+ `Transient` and lazy-mode providers. Both are strict by default: they see the module's own providers and
455
+ what its imports (and global modules) export to them, and return `undefined`
456
+ for every other token — including tokens that do exist elsewhere in the
457
+ container. Pass `{ strict: false }` to look the token up in the whole
458
+ container instead. Either way the result is `T | undefined`, so check it
459
+ (`resolve()` returns `Promise<T | undefined>`):
460
+
461
+ ```typescript
462
+ const own = ref.get<FeatureService>(FeatureService); // visible: instance
463
+ const other = ref.get<UnrelatedService>(UnrelatedService); // undefined
464
+ const anywhere = ref.get<UnrelatedService>(UnrelatedService, {
465
+ strict: false,
466
+ });
467
+ ```
468
+
469
+ ### Errors
470
+
471
+ - `LazyModuleLoadError` — the loader rejected (a failed dynamic import, for
472
+ example) or did not return a class decorated with `@Module()`. Nothing is
473
+ added to the graph and the ref may be loaded again.
474
+ - `LazyModuleGraphError` — the module loaded but its dependency graph is
475
+ invalid (a missing dependency, a cycle, a token conflict). Its `errors`
476
+ property carries the graph errors, and the segment is rolled back entirely,
477
+ so the container is left exactly as it was.
478
+
479
+ ### Loading from a service
480
+
481
+ To load from a service, inject the built-in `LazyModuleLoader`:
482
+
483
+ ```typescript
484
+ @Injectable()
485
+ class OrdersService {
486
+ constructor(@Inject(LazyModuleLoader) private readonly loader: LazyModuleLoader) {}
487
+
488
+ async check(order: Order) {
489
+ const ref = await this.loader.load(FraudLazy);
490
+ const fraud = ref.get<FraudService>(FraudService);
491
+ return fraud?.check(order);
492
+ }
493
+ }
494
+ ```
495
+
496
+ Under `@nexus-ioc/testing` the same loader is registered by `Test.compile()`,
497
+ so services that inject it work in tests; the `Test` instance itself also has
498
+ `load(ref)` once it is compiled.
499
+
500
+ ### Unloading
501
+
502
+ ```typescript
503
+ const ref = await app.load(FeatureLazy);
504
+ // ... later ...
505
+ const { destroyedModules, destroyedProviders, closedChildren } =
506
+ await app.unload(ref);
507
+ ```
508
+
509
+ - `app.unload(ref)`, `ref.unload()`, and `LazyModuleLoader.unload(ref)` all do the
510
+ same thing; use whichever is convenient at the call site.
511
+ - Only what nothing else still needs is destroyed: a module shared with
512
+ another loaded segment, a `@Global()` module another segment depends on, or
513
+ a nested lazy module loaded from inside the one being unloaded, are all left
514
+ running.
515
+ - `onModuleDestroy` runs on affected singletons in reverse initialization
516
+ order. A hook that throws does not stop the others, and the module is
517
+ unloaded anyway (it can be loaded again); the unload then rejects with one
518
+ `AggregateError` holding every failure, as `close()` does. `ref.loaded` becomes `false`, and `ref.get()` and `ref.resolve()` return
519
+ `undefined` for every token afterward — the ref does not come back to life; loading the
520
+ same `lazy()` ref again returns a new `ModuleRef`.
521
+ - Unloading a ref that was never loaded, or unloading it twice, is a no-op:
522
+ `{ destroyedModules: [], destroyedProviders: [], closedChildren: 0 }`. After `app.close()`,
523
+ unloading rejects with `ContainerClosedError`.
524
+ - Child containers declared by the modules the unload destroys are closed
525
+ first (`closedChildren` counts them); see [Child containers](#child-containers).
526
+ - Not handled: an instance obtained via `app.get()` or `ref.get()` and held
527
+ outside the container (for example in a closure) keeps working as a plain
528
+ object after its module is destroyed — the same contract as an Angular
529
+ injector destroyed while a component still holds an already-injected
530
+ instance. A `get()` call racing an unload that destroys the same provider
531
+ mid-resolution is not guarded against. Likewise, calling `load()` on a ref
532
+ while an `unload()` of that same ref is in flight, without awaiting the
533
+ unload first, can return a `ModuleRef` for a segment that is about to be
534
+ destroyed — this race is not guarded against either.
535
+
536
+ ## Child containers
537
+
538
+ A child container is a second application created from a module of a running
539
+ one. It has its own singletons and its own lifetime, and it can use the
540
+ providers of the application that created it. Typical uses: per-profile,
541
+ per-tenant or per-document state that is created and thrown away while the main
542
+ application keeps running.
543
+
544
+ ### Per-profile containers
545
+
546
+ Declare the module that may become a child in `children` of the module whose
547
+ providers it should use, bootstrap the application, then call `createChild()`:
548
+
549
+ ```typescript
550
+ import {
551
+ DynamicModule,
552
+ Inject,
553
+ Injectable,
554
+ InjectionToken,
555
+ Module,
556
+ NexusApplication,
557
+ } from '@nexus-ioc/core';
558
+
559
+ const PROFILE_ID = new InjectionToken<string>('PROFILE_ID');
560
+
561
+ @Injectable()
562
+ class HttpClient {}
563
+
564
+ @Module({ providers: [HttpClient], exports: [HttpClient] })
565
+ class HttpModule {}
566
+
567
+ @Injectable()
568
+ class EventBus {}
569
+
570
+ @Injectable()
571
+ class ProfileStore {
572
+ constructor(
573
+ @Inject(PROFILE_ID) readonly profileId: string,
574
+ @Inject(EventBus) readonly bus: EventBus,
575
+ @Inject(HttpClient) readonly http: HttpClient,
576
+ ) {}
577
+ }
578
+
579
+ @Module({ providers: [ProfileStore] })
580
+ class ProfileModule {
581
+ static forProfile(id: string): DynamicModule {
582
+ return {
583
+ module: ProfileModule,
584
+ providers: [ProfileStore, { provide: PROFILE_ID, useValue: id }],
585
+ };
586
+ }
587
+ }
588
+
589
+ @Module({ imports: [HttpModule], providers: [EventBus], children: [ProfileModule] })
590
+ class AppModule {}
591
+
592
+ const app = await NexusApplication.create(AppModule).bootstrap();
593
+ let profile = await app.createChild(ProfileModule.forProfile('a'));
594
+ profile.get(ProfileStore).profileId; // 'a'
595
+ // switch profile
596
+ await profile.close();
597
+ profile = await app.createChild(ProfileModule.forProfile('b'));
598
+ ```
599
+
600
+ `createChild()` is a method of the application, not something a provider can
601
+ inject: there is no DI handle for it yet. Keep the `app` at the composition
602
+ root and pass it (or a function that calls `app.createChild()`) to whatever
603
+ switches profiles.
604
+
605
+ `createChild()` returns a `NexusApplication` that is already bootstrapped, with
606
+ its singletons created. As with any dynamic module, the `providers` of the
607
+ `DynamicModule` object are used instead of the ones in the class's `@Module`
608
+ decorator, which is why `forProfile()` lists `ProfileStore` too. `ProfileStore` gets `EventBus` and `HttpClient` from
609
+ `AppModule`; the profile id is data that exists only per child, so it is
610
+ supplied when the child is created.
611
+
612
+ ### Declaring children
613
+
614
+ ```typescript
615
+ const ReportsLazy = lazy(() =>
616
+ import('./reports/reports.module').then((m) => m.ReportsModule),
617
+ );
618
+
619
+ @Module({
620
+ children: [ProfileModule, ReportsLazy],
621
+ })
622
+ class AppModule {}
623
+ ```
624
+
625
+ - `children` takes classes and `lazy()` refs. It is static `@Module` metadata:
626
+ a `DynamicModule` object cannot declare children.
627
+ - A child module is not an import. It is not compiled into the parent graph and
628
+ nothing is created until you call `createChild()`.
629
+ - A given child may be declared by at most one module in the whole parent
630
+ graph. A second declaration is the graph error `CHILD_DECLARED_TWICE`. It is
631
+ also raised when one class is imported as two module ids (two `forX()`
632
+ variants, or a static plus a dynamic import) in a graph where that class
633
+ declares children.
634
+ - A class (`Type`) child and a `lazy()` child that load the same class are
635
+ different declarations.
636
+ - A `lazy()` ref is matched by identity.
637
+
638
+ ### createChild()
639
+
640
+ ```typescript
641
+ createChild(
642
+ child: Type | DynamicModule | LazyModule,
643
+ options?: { providers?: Provider[] },
644
+ ): Promise<NexusApplication>
645
+ ```
646
+
647
+ - A class matches a declared class. A `DynamicModule` matches by its `.module`
648
+ class, so `ProfileModule.forProfile(id)` works when `ProfileModule` is
649
+ declared. A `lazy()` ref matches the declared ref.
650
+ - The loader of a lazy child runs once per ref per parent and its result is
651
+ cached; a failed load is not cached. The loaded class must be the declared
652
+ one or carry `@Module`; otherwise `LazyModuleLoadError`.
653
+ - `options.providers` are appended to the providers of the child's root
654
+ module. Use them for per-instance data:
655
+
656
+ ```typescript
657
+ const profile = await app.createChild(ProfileModule, {
658
+ providers: [{ provide: PROFILE_ID, useValue: 'a' }],
659
+ });
660
+ ```
661
+ - A child root created with `forProfile(id)` or `options.providers` keeps the
662
+ `children` its class declares, so a child can have children of its own.
663
+
664
+ `createChild()` rejects with:
665
+
666
+ - `ChildModuleNotDeclaredError` — no module declares the child. Its `reason`
667
+ is `"unloaded"` when the lazy module that declares it was unloaded while the
668
+ child was being created (a child that had already been started is closed).
669
+ - `ContainerNotCompiledError` — the parent is not bootstrapped.
670
+ - `ContainerClosedError` — the parent is closed or closing. It wins when the
671
+ parent closes during creation.
672
+ - `LazyModuleLoadError` — the loader of a lazy child failed or returned
673
+ something that is not a module.
674
+
675
+ ### What a child sees
676
+
677
+ A child sees what its declaring module sees in the parent graph: that module's
678
+ own providers, what its imports export to it and global modules, plus whatever
679
+ the parent's own link provides, recursively up the chain. Providers a module
680
+ does not export stay hidden. A provider visible in the child itself wins over
681
+ the parent's.
682
+
683
+ - `child.get(token)` and `child.resolve(token)` look in the child first (any
684
+ provider of the child, whatever its exports), then through the link, then
685
+ throw `ProviderNotFoundError` (or return `undefined` with
686
+ `{ optional: true }`).
687
+ - After the parent closes, lookups through the link throw
688
+ `ContainerClosedError`, even with `{ optional: true }`.
689
+ - A child that imports a module itself gets its own instances of that
690
+ module's providers. If `ProfileModule` imports `HttpModule`, every profile
691
+ has a second `HttpClient` singleton, separate from the parent's. To share the
692
+ parent's instance, do not import the module in the child; let the token come
693
+ through the declaring module's visibility (here, `AppModule` imports
694
+ `HttpModule`).
695
+ - Scanner plugins added with `addScannerPlugin()` are not inherited: they run
696
+ on the parent's graph only, never on a child's.
697
+
698
+ ### Scopes across the boundary
699
+
700
+ - `@Inject` of a parent token gives the parent's singleton instance. For a
701
+ parent `Transient` it gives a new instance, created by the parent.
702
+ - `inject()` uses the link's synchronous `get`, which cannot create anything:
703
+ - a parent `Transient` reached through `inject()` throws
704
+ `ProviderScopeError`;
705
+ - a parent singleton that does not exist yet throws
706
+ `ProviderNotInstantiatedError`. That is the case for a parent in `.lazy()`
707
+ mode, for a parent singleton whose async `onModuleInit` is still running,
708
+ and while a `load()` warm-up is still creating it.
709
+
710
+ Use `@Inject` for dependencies on the parent when any of those can happen.
711
+ - A required dependency that neither the child nor the link provides fails the
712
+ child's `bootstrap()`, so `createChild()` rejects with `BootstrapError`
713
+ instead of creating a provider with an `undefined` dependency.
714
+
715
+ ### Lifecycle
716
+
717
+ - The parent tracks its live children. `child.close()` closes only that child
718
+ and removes it from the parent, so it can be garbage-collected; closing a
719
+ child does not touch the parent or its siblings.
720
+ - `parent.close()` first closes all live children, newest first, then tears
721
+ itself down. Errors from the children join the parent's `AggregateError`.
722
+ - Unloading a lazy module closes every live child declared by a module the
723
+ unload destroys, before it destroys providers. `UnloadResult` has a
724
+ `closedChildren` count of the closes that unload itself started (a child that
725
+ was already closing is not counted). A child-close error and a
726
+ provider-destroy error during one unload are joined into one `AggregateError`.
727
+ - A singleton creation that completes after `close()` or an unload is not
728
+ cached, in a child as in any application; see
729
+ [Lifecycle and close()](#lifecycle-and-close).
730
+ - A child's `onModuleDestroy` that awaits `parent.load()` or `parent.unload()`
731
+ while that parent is unloading hangs, because the parent's compile queue is
732
+ busy. After an unload that is not followed by a reload, a stalled async tree
733
+ may construct a requester with an `undefined` dependency before it is
734
+ discarded.
735
+
736
+ ### Concurrent children
737
+
738
+ Children of the same module are independent containers with separate
739
+ singletons. Creating several at once is fine:
740
+
741
+ ```typescript
742
+ const [a, b] = await Promise.all([
743
+ app.createChild(ProfileModule.forProfile('a')),
744
+ app.createChild(ProfileModule.forProfile('b')),
745
+ ]);
746
+ ```
747
+
748
+ ### React and other UI scopes
749
+
750
+ A React `<ContainerScope>` maps onto this directly: call `createChild()` in an
751
+ effect and `close()` in its cleanup. In StrictMode effects run twice in
752
+ development, so guard against the double mount: close the first child in the
753
+ cleanup, and close a child whose creation finished after its effect was already
754
+ cleaned up. The adapter itself is not part of this package.
755
+
756
+ ### Static analysis
757
+
758
+ `@nexus-ioc/type-checker` and `@nexus-ioc/graph-analyzer` follow `children`
759
+ (direct class references and the
760
+ `lazy(() => import('...').then((m) => m.X))` shape). The type-checker resolves
761
+ each child scope's dependencies with the declaring module's visibility as the
762
+ fallback, and the graph-analyzer reports child scopes. The language service
763
+ suppresses missing-dependency errors in a child scope when the declaring module
764
+ can see the token. See [graph-analyzer](../graph-analyzer/README.md).
765
+
766
+ Known limitation: static tooling only sees what the source declares. Tokens a
767
+ child gets per instance (the `providers` of `forProfile()` or another dynamic
768
+ module, or `createChild(..., { providers })`) are invisible to it, so the
769
+ language service and the type-checker may report them as missing even though
770
+ the application runs.
771
+
98
772
  ## Testing
99
773
 
100
774
  ### Installation
@@ -118,12 +792,68 @@ describe('AppModule', () => {
118
792
  providers: [AppService],
119
793
  }).compile();
120
794
 
121
- const appService = await appModule.get<AppService>(AppService);
795
+ const appService = appModule.get<AppService>(AppService);
122
796
  expect(appService).toBeInstanceOf(AppService);
123
797
  });
124
798
  });
125
799
  ```
126
800
 
801
+ `Test.compile()` instantiates all singletons and runs `onModuleInit`, so an
802
+ error in `onModuleInit` or in a factory surfaces from `compile()`. `Test.get`
803
+ is synchronous and `Test.resolve` is async, both with `{ optional }`. The
804
+ module container that `compile()` returns has the same `get()` / `resolve()`
805
+ pair, so `moduleRef.get<X>(X)` needs no `await`.
806
+ `Test.load()` warms the loaded module.
807
+
808
+ ## Migrating to the synchronous get()
809
+
810
+ - The `InjectionToken` type is now `ProviderToken<T>`, and `InjectionToken` is
811
+ a class (see [Tokens](#tokens)). There is no alias for the old type, so
812
+ replace type-level uses of `InjectionToken` with `ProviderToken`.
813
+ - `get('string')` and `get(symbol)` now return `unknown` unless you pass `<T>`
814
+ (`get<string>('API_URL')`) or the expected type supplies it. Class tokens and
815
+ `InjectionToken<T>` are inferred.
816
+ - `ProviderToken<T>` no longer accepts arbitrary functions: a plain function,
817
+ or a class unrelated to `T` (`get<Foo>(Bar)`), is now a compile error.
818
+ - A class with a `private` or `protected` constructor can no longer be used as
819
+ a token: use an `InjectionToken` instead.
820
+ - Async factories are removed: a `useFactory` that returns a promise throws
821
+ `AsyncFactoryError`. Move the async setup into `onModuleInit` of the provided
822
+ class, or create the value before bootstrap and register it with `useValue`.
823
+ Singletons are now created in plain declaration order.
824
+ - `AsyncProviderInjectionError` is removed; it can no longer happen.
825
+ - `Scope.Scoped` is removed. Only `Singleton` and `Transient` remain (`Transient`
826
+ keeps the value 2). Use `Transient`, or a child container
827
+ ([`createChild`](#child-containers)) for per-request state. A scope value that
828
+ is not a `Scope` member (such as `1`, the old `Scoped`) throws
829
+ `InvalidScopeError` at bootstrap.
830
+ - `setParent()` and `getParent()` are removed. Replace
831
+ `NexusApplication.create(X).setParent(app).bootstrap()` by declaring `X` in
832
+ `children` of the module that owns it and calling `await app.createChild(X)`;
833
+ see [Child containers](#child-containers). A child now sees what its
834
+ declaring module sees, not the whole parent, and the parent closes its live
835
+ children.
836
+ - `get()` no longer returns a promise: drop the `await` for singletons and use
837
+ `await resolve()` for `Transient` and lazy-mode providers.
838
+ - `get()` now throws instead of returning `undefined` for unknown tokens. Pass
839
+ `{ optional: true }` to keep the old `undefined` result.
840
+ - `ModuleRef.get()` is synchronous too; the async variant is
841
+ `ModuleRef.resolve()`.
842
+ - `Test.compile()` now instantiates singletons and runs `onModuleInit`, so
843
+ errors from them surface from `compile()`.
844
+ - The module container returned by `compile()` has a synchronous `get()` and
845
+ a new async `resolve()`. Its `get()` throws `ProviderNotFoundError` for an
846
+ unknown token instead of resolving to `undefined` (pass
847
+ `{ optional: true }`), and throws `ProviderScopeError` for `Transient`
848
+ providers: use `await moduleRef.resolve(X)` for those.
849
+ - `get()` and `resolve()` called before `bootstrap()` throw
850
+ `ContainerNotCompiledError` instead of `ProviderNotFoundError`.
851
+ - `.lazy()` is deprecated: lazy modules (`lazy()` / `load()`) cover the
852
+ startup-time use case, and in `.lazy()` mode `get()` throws
853
+ `ProviderNotInstantiatedError`.
854
+ - The CLI service-spec template now generates `moduleRef.get<X>(X)` without
855
+ `await`.
856
+
127
857
  ## License
128
858
 
129
859
  This project is licensed under the MIT License. See the LICENSE file for details.