@tsed/di 8.37.0 → 8.38.0-beta.1

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.
@@ -13,15 +13,15 @@ import { Provider } from "./Provider.js";
13
13
  * const container = new Container();
14
14
  * container.add(MyService, {scope: ProviderScope.SINGLETON});
15
15
  *
16
- * const provider = container.getProvider(MyService);
17
- * const controllers = container.getProviders(ProviderType.CONTROLLER);
16
+ * const provider = container.get(MyService);
17
+ * const controllers = container.getMany(ProviderType.CONTROLLER);
18
18
  * ```
19
19
  *
20
20
  * @public
21
21
  */
22
22
  export class Container extends Map {
23
23
  /**
24
- *
24
+ * Add a provider to the
25
25
  * @param token
26
26
  * @param settings
27
27
  */
@@ -30,35 +30,7 @@ export class Container extends Map {
30
30
  Object.assign(provider, settings);
31
31
  return super.set(token, provider);
32
32
  }
33
- /**
34
- * Add a provider to the
35
- * @param token
36
- * @param settings
37
- */
38
- addProvider(token, settings = {}) {
39
- return this.add(token, settings);
40
- }
41
- /**
42
- *
43
- * @param token
44
- */
45
- hasProvider(token) {
46
- return super.has(token);
47
- }
48
- /**
49
- * Add a provider to the
50
- * @param token
51
- * @param provider
52
- */
53
- setProvider(token, provider) {
54
- return super.set(token, provider);
55
- }
56
- /**
57
- * The getProvider() method returns a specified element from a Map object.
58
- * @returns {T} Returns the element associated with the specified key or undefined if the key can't be found in the Map object.
59
- * @param token
60
- */
61
- getProvider(token) {
33
+ get(token) {
62
34
  return super.get(token);
63
35
  }
64
36
  /**
@@ -67,7 +39,7 @@ export class Container extends Map {
67
39
  * @param type Filter the list by the given ProviderType.
68
40
  * @returns {[TokenProvider , Provider<any>][]}
69
41
  */
70
- getProviders(type) {
42
+ getMany(type) {
71
43
  if (!type) {
72
44
  return [...this.values()];
73
45
  }
@@ -80,10 +52,10 @@ export class Container extends Map {
80
52
  }
81
53
  return providers;
82
54
  }
83
- addProviders(container) {
55
+ merge(container) {
84
56
  container.forEach((provider) => {
85
- if (!this.hasProvider(provider.token)) {
86
- this.setProvider(provider.token, provider.clone());
57
+ if (!super.has(provider.token)) {
58
+ super.set(provider.token, provider.clone());
87
59
  }
88
60
  });
89
61
  }
@@ -37,6 +37,9 @@ export class ContextLogger {
37
37
  this.level = (LEVELS[(level || this.#logger.level || "").toUpperCase()] || LEVELS.ALL);
38
38
  this.maxStackSize = maxStackSize;
39
39
  }
40
+ get level() {
41
+ return this.#level;
42
+ }
40
43
  set level(level) {
41
44
  if (typeof level === "string") {
42
45
  this.#level = LEVELS[level.toUpperCase()];
@@ -45,9 +48,6 @@ export class ContextLogger {
45
48
  this.#level = level;
46
49
  }
47
50
  }
48
- get level() {
49
- return this.#level;
50
- }
51
51
  get hooks() {
52
52
  return (this.#hooks = this.#hooks || new Hooks());
53
53
  }
@@ -27,11 +27,17 @@ import { setValue } from "@tsed/core/utils/setValue.js";
27
27
  * @public
28
28
  */
29
29
  export class DIConfiguration {
30
+ /**
31
+ * Creates a configuration service with the built-in defaults merged with the supplied initial properties.
32
+ *
33
+ * @param initialProps Initial configuration values that override built-in defaults.
34
+ */
30
35
  constructor(initialProps = {}) {
31
36
  this.default = new Map();
32
37
  this.map = new Map();
33
38
  Object.entries({
34
39
  imports: [],
40
+ lazyProviders: false,
35
41
  routes: [],
36
42
  mount: {},
37
43
  logger: {},
@@ -40,59 +46,138 @@ export class DIConfiguration {
40
46
  this.default.set(key, value);
41
47
  });
42
48
  }
49
+ /**
50
+ * Gets the application version.
51
+ */
43
52
  get version() {
44
53
  return this.get("version");
45
54
  }
55
+ /**
56
+ * Sets the application version.
57
+ *
58
+ * @param v Version to store in the configuration.
59
+ */
46
60
  set version(v) {
47
61
  this.map.set("version", v);
48
62
  }
63
+ /**
64
+ * Gets the application's root directory.
65
+ */
49
66
  get rootDir() {
50
67
  return this.get("rootDir");
51
68
  }
69
+ /**
70
+ * Sets the application's root directory.
71
+ *
72
+ * @param value Root directory to store in the configuration.
73
+ */
52
74
  set rootDir(value) {
53
75
  this.map.set("rootDir", value);
54
76
  }
77
+ /**
78
+ * Gets the active runtime environment.
79
+ */
55
80
  get env() {
56
81
  return this.map.get("env");
57
82
  }
83
+ /**
84
+ * Sets the active runtime environment.
85
+ *
86
+ * @param value Environment to store in the configuration.
87
+ */
58
88
  set env(value) {
59
89
  this.map.set("env", value);
60
90
  }
91
+ /**
92
+ * Gets the providers and modules imported by the application.
93
+ */
61
94
  get imports() {
62
95
  return this.get("imports");
63
96
  }
97
+ /**
98
+ * Sets the providers and modules imported by the application.
99
+ *
100
+ * @param imports Provider tokens or import options to configure.
101
+ */
64
102
  set imports(imports) {
65
103
  this.map.set("imports", imports);
66
104
  }
105
+ /**
106
+ * Indicates whether synchronous singleton providers without hooks are deferred until first use.
107
+ */
108
+ get lazyProviders() {
109
+ return this.get("lazyProviders");
110
+ }
111
+ /**
112
+ * Enables or disables deferred initialization of synchronous singleton providers without hooks.
113
+ *
114
+ * @param lazyProviders Whether to defer eligible providers.
115
+ */
116
+ set lazyProviders(lazyProviders) {
117
+ this.map.set("lazyProviders", lazyProviders);
118
+ }
119
+ /**
120
+ * Gets the route providers registered by the application.
121
+ */
67
122
  get routes() {
68
123
  return this.get("routes");
69
124
  }
125
+ /**
126
+ * Sets the route providers registered by the application.
127
+ *
128
+ * @param routes Route providers to configure.
129
+ */
70
130
  set routes(routes) {
71
131
  this.map.set("routes", routes);
72
132
  }
133
+ /**
134
+ * Gets the logger options.
135
+ */
73
136
  get logger() {
74
137
  return this.get("logger");
75
138
  }
139
+ /**
140
+ * Merges logger options into the current logger configuration.
141
+ *
142
+ * @param value Logger options to merge.
143
+ */
76
144
  set logger(value) {
77
145
  const logger = { ...this.logger, ...value };
78
146
  this.map.set("logger", logger);
79
147
  }
148
+ /**
149
+ * Indicates whether the logger is configured at the `debug` level.
150
+ */
80
151
  get debug() {
81
152
  return this.logger.level === "debug";
82
153
  }
154
+ /**
155
+ * Enables or disables debug-level logging.
156
+ *
157
+ * @param debug `true` to set the logger level to `debug`; `false` to set it to `info`.
158
+ */
83
159
  set debug(debug) {
84
160
  this.logger = { ...this.logger, level: debug ? "debug" : "info" };
85
161
  }
162
+ /**
163
+ * Gets route providers grouped by mount path.
164
+ */
86
165
  get mount() {
87
166
  return this.get("mount");
88
167
  }
168
+ /**
169
+ * Sets route providers grouped by mount path.
170
+ *
171
+ * @param value Route providers indexed by mount path.
172
+ */
89
173
  set mount(value) {
90
174
  this.setRaw("mount", value);
91
175
  }
92
176
  /**
177
+ * Invokes a callback once for each configured key, including default and overridden values.
93
178
  *
94
- * @param callbackfn
95
- * @param thisArg
179
+ * @param callbackfn Callback invoked with the resolved value, key, and override map.
180
+ * @param thisArg Value to use as `this` when invoking the callback.
96
181
  */
97
182
  forEach(callbackfn, thisArg) {
98
183
  return new Set([...Array.from(this.default.keys()), ...Array.from(this.map.keys())]).forEach((key) => {
@@ -117,19 +202,34 @@ export class DIConfiguration {
117
202
  }
118
203
  return this;
119
204
  }
205
+ /**
206
+ * Sets a configuration value directly in the override map, supporting nested property paths.
207
+ *
208
+ * @param propertyKey Configuration key or nested property path.
209
+ * @param value Value to store.
210
+ * @returns This configuration instance for chaining.
211
+ */
120
212
  setRaw(propertyKey, value) {
121
213
  setValue(this.map, propertyKey, value);
122
214
  return this;
123
215
  }
124
216
  /**
217
+ * Gets a resolved configuration value, preferring explicit overrides over default values.
125
218
  *
126
- * @param propertyKey
127
- * @param defaultValue
128
- * @returns {undefined|any}
219
+ * @param propertyKey Configuration key or nested property path.
220
+ * @param defaultValue Value to return when neither the overrides nor defaults define the key.
221
+ * @returns The resolved configuration value.
129
222
  */
130
223
  get(propertyKey, defaultValue) {
131
224
  return this.getRaw(propertyKey, defaultValue);
132
225
  }
226
+ /**
227
+ * Defines a custom configuration member when a member with the same key does not already exist.
228
+ *
229
+ * @param key Name of the member to define.
230
+ * @param value Function or property descriptor to assign to the configuration prototype.
231
+ * @returns This configuration instance when the member already exists.
232
+ */
133
233
  decorate(key, value) {
134
234
  if (key in this) {
135
235
  return this;
@@ -140,6 +240,13 @@ export class DIConfiguration {
140
240
  }
141
241
  : value);
142
242
  }
243
+ /**
244
+ * Resolves a configuration value from overrides, defaults, or the supplied fallback in that order.
245
+ *
246
+ * @param propertyKey Configuration key or nested property path.
247
+ * @param defaultValue Value to return when no configured value exists.
248
+ * @returns The resolved configuration value.
249
+ */
143
250
  getRaw(propertyKey, defaultValue) {
144
251
  const value = getValue(this.map, propertyKey);
145
252
  if (value !== undefined) {
@@ -43,23 +43,88 @@ const EXCLUDED_CONFIGURATION_KEYS = ["mount", "imports"];
43
43
  *
44
44
  * @public
45
45
  */
46
- export class InjectorService extends Container {
46
+ export class InjectorService {
47
+ /**
48
+ * Providers containers
49
+ * @private
50
+ */
51
+ #providers;
52
+ /**
53
+ * Cached instance
54
+ * @private
55
+ */
47
56
  #cache;
48
57
  #loaded;
58
+ #hooksReady;
59
+ #registeredSingletonHooks;
60
+ #resolvingProviders;
49
61
  constructor() {
50
- super();
51
62
  this.logger = console;
52
63
  /*readonly */
53
64
  this.settings = new DIConfiguration();
54
- this.resolvedConfiguration = false;
65
+ /**
66
+ * Providers containers
67
+ * @private
68
+ */
69
+ this.#providers = new Container();
70
+ /**
71
+ * Cached instance
72
+ * @private
73
+ */
55
74
  this.#cache = new LocalsContainer();
75
+ this.resolvedConfiguration = false;
56
76
  this.#loaded = false;
77
+ this.#hooksReady = false;
78
+ this.#registeredSingletonHooks = new Set();
79
+ this.#resolvingProviders = new Set();
57
80
  this.#cache.set(InjectorService, this);
58
81
  this.#cache.set(DIConfiguration, this.settings);
59
82
  }
83
+ get providers() {
84
+ return this.#providers;
85
+ }
60
86
  isLoaded() {
61
87
  return this.#loaded;
62
88
  }
89
+ add(token, settings = {}) {
90
+ this.#providers.add(token, settings);
91
+ this.setProvider(token, this.#providers.get(token));
92
+ return this;
93
+ }
94
+ /**
95
+ * @alias InjectorService.add
96
+ */
97
+ addProvider(token, settings = {}) {
98
+ return this.add(token, settings);
99
+ }
100
+ setProvider(token, provider) {
101
+ this.#providers.set(token, provider);
102
+ if (this.#registeredSingletonHooks.delete(token)) {
103
+ $off(token);
104
+ }
105
+ if (provider.scope === ProviderScope.SINGLETON && (!this.#loaded || this.#hooksReady)) {
106
+ this.registerHooks(provider, {});
107
+ }
108
+ return this;
109
+ }
110
+ /**
111
+ * Get a provider definition from the provider registry.
112
+ *
113
+ * Unlike `get()`, this method does not resolve the provider instance.
114
+ * @deprecated Since v8. Use injector().providers.get() instead
115
+ */
116
+ getProvider(token) {
117
+ return this.#providers.get(token);
118
+ }
119
+ /**
120
+ * Get provider definitions matching a type.
121
+ *
122
+ * Unlike `getMany()`, this method does not resolve provider instances.
123
+ * @deprecated Since v8. Use injector().providers.getMany() instead.
124
+ */
125
+ getProviders(type) {
126
+ return this.#providers.getMany(type);
127
+ }
63
128
  /**
64
129
  * Return a list of all instances built by the injector.
65
130
  *
@@ -107,7 +172,7 @@ export class InjectorService extends Container {
107
172
  * @returns Array of resolved instances matching the type
108
173
  */
109
174
  getMany(type, options) {
110
- return this.getProviders(type).map((provider) => {
175
+ return this.#providers.getMany(type).map((provider) => {
111
176
  return this.resolve(provider.token, options);
112
177
  });
113
178
  }
@@ -173,6 +238,9 @@ export class InjectorService extends Container {
173
238
  return instance;
174
239
  }
175
240
  const provider = this.ensureProvider(token);
241
+ if (provider && provider.scope === ProviderScope.SINGLETON && !options.rebuild && !this.#registeredSingletonHooks.has(provider.token)) {
242
+ this.registerHooks(provider, options);
243
+ }
176
244
  // maybe not necessary
177
245
  if (!provider || options.rebuild) {
178
246
  instance = this.invokeToken(token, options);
@@ -184,9 +252,6 @@ export class InjectorService extends Container {
184
252
  instance = this.invokeToken(token, options);
185
253
  switch (provider.scope) {
186
254
  case ProviderScope.SINGLETON:
187
- if (!options.rebuild) {
188
- this.registerHooks(provider, options);
189
- }
190
255
  return this.setToCache(provider, instance);
191
256
  case ProviderScope.REQUEST:
192
257
  if (options.locals) {
@@ -231,8 +296,11 @@ export class InjectorService extends Container {
231
296
  * Called automatically during the `load()` process.
232
297
  */
233
298
  async loadAsync() {
234
- for (const [, provider] of this) {
235
- if (!this.has(provider.token) && provider.isAsync()) {
299
+ for (const [, provider] of this.#providers) {
300
+ if (!provider.isAsync()) {
301
+ continue;
302
+ }
303
+ if (!this.has(provider.token)) {
236
304
  await this.resolve(provider.token);
237
305
  }
238
306
  }
@@ -244,9 +312,14 @@ export class InjectorService extends Container {
244
312
  * Called automatically during the `load()` process after async providers.
245
313
  */
246
314
  loadSync() {
247
- for (const [, provider] of this) {
248
- // TODO try to lazy provider instead initiate all providers (&& provider.hasRegisteredHooks())
249
- if (!this.has(provider.token) && provider.scope === ProviderScope.SINGLETON) {
315
+ if (this.settings.lazyProviders) {
316
+ return;
317
+ }
318
+ for (const [, provider] of this.#providers) {
319
+ if (provider.scope !== ProviderScope.SINGLETON || provider.isAsync()) {
320
+ continue;
321
+ }
322
+ if (!this.has(provider.token)) {
250
323
  this.resolve(provider.token);
251
324
  }
252
325
  }
@@ -271,6 +344,7 @@ export class InjectorService extends Container {
271
344
  // avoid provider registration in the GlobalContainer during the loading phase
272
345
  // using injectable() or providerBuilder()
273
346
  this.#loaded = true;
347
+ this.#hooksReady = true;
274
348
  await $asyncEmit("$beforeInit");
275
349
  await this.bootstrap(container);
276
350
  // build async and sync provider
@@ -291,7 +365,7 @@ export class InjectorService extends Container {
291
365
  return;
292
366
  }
293
367
  const mergedConfiguration = new Map();
294
- super.forEach((provider) => {
368
+ this.#providers.forEach((provider) => {
295
369
  if (provider.configuration && provider.type !== "server:module") {
296
370
  Object.entries(provider.configuration).forEach(([key, value]) => {
297
371
  if (!EXCLUDED_CONFIGURATION_KEYS.includes(key)) {
@@ -345,6 +419,11 @@ export class InjectorService extends Container {
345
419
  this.#cache.forEach((_, token) => {
346
420
  $off(token);
347
421
  });
422
+ this.#registeredSingletonHooks.forEach((token) => {
423
+ $off(token);
424
+ });
425
+ this.#registeredSingletonHooks.clear();
426
+ this.#hooksReady = false;
348
427
  }
349
428
  /**
350
429
  * Bootstrap injector from container, resolve configuration and providers.
@@ -353,7 +432,7 @@ export class InjectorService extends Container {
353
432
  */
354
433
  async bootstrap(container = createContainer()) {
355
434
  // Clone all providers in the container
356
- this.addProviders(container);
435
+ this.mergeProviders(container);
357
436
  // Resolve all configuration
358
437
  await this.resolveConfiguration();
359
438
  // allow mocking or changing provider instance before loading injector
@@ -361,10 +440,10 @@ export class InjectorService extends Container {
361
440
  return this;
362
441
  }
363
442
  ensureProvider(token, force = false) {
364
- if (!this.hasProvider(token) && (Provider.Registry.has(token) || force)) {
365
- this.addProvider(token);
443
+ if (!this.#providers.has(token) && (Provider.Registry.has(token) || force)) {
444
+ this.add(token);
366
445
  }
367
- return this.getProvider(token);
446
+ return this.#providers.get(token);
368
447
  }
369
448
  /**
370
449
  * Invoke a class method and inject service.
@@ -388,44 +467,57 @@ export class InjectorService extends Container {
388
467
  return undefined;
389
468
  }
390
469
  const { token, deps, construct, imports, provider } = resolvedOpts;
391
- $emit("$beforeInvoke", token, [resolvedOpts]);
392
- $emit(`$beforeInvoke:${String(provider.type)}`, [resolvedOpts]);
393
- let instance;
394
- let currentDependency = false;
470
+ this.#resolvingProviders.add(provider.token);
395
471
  try {
396
- const invokeDependency = (parent) => (token, index) => {
397
- currentDependency = { token, index, deps };
398
- if (isArray(token)) {
399
- return this.getMany(token[0], options);
400
- }
401
- return isInheritedFrom(token, Provider, 1)
402
- ? provider
403
- : this.resolve(token, {
404
- parent,
405
- locals: options.locals,
406
- useOpts: provider?.getArgOpts(index) || options.useOpts
407
- });
408
- };
409
- // Invoke manually imported providers
410
- imports.forEach(invokeDependency());
411
- // Inject dependencies
412
- const services = deps.map(invokeDependency(token));
413
- currentDependency = false;
414
- instance = construct(services);
415
- }
416
- catch (error) {
417
- InjectionError.throwInjectorError(token, currentDependency, error);
418
- }
419
- if (instance === undefined) {
420
- throw new InjectionError(token, `Unable to create new instance from undefined value. Check your provider declaration for ${nameOf(token)}`);
472
+ $emit("$beforeInvoke", token, [resolvedOpts]);
473
+ $emit(`$beforeInvoke:${String(provider.type)}`, [resolvedOpts]);
474
+ let instance;
475
+ let currentDependency = false;
476
+ try {
477
+ const invokeDependency = (parent) => (token, index) => {
478
+ currentDependency = { token, index, deps };
479
+ if (isArray(token)) {
480
+ return this.getMany(token[0], options);
481
+ }
482
+ return isInheritedFrom(token, Provider, 1)
483
+ ? provider
484
+ : this.resolve(token, {
485
+ parent,
486
+ locals: options.locals,
487
+ useOpts: provider?.getArgOpts(index) || options.useOpts
488
+ });
489
+ };
490
+ // Invoke manually imported providers
491
+ imports.forEach(invokeDependency());
492
+ // Inject dependencies
493
+ const services = deps.map(invokeDependency(token));
494
+ currentDependency = false;
495
+ instance = construct(services);
496
+ }
497
+ catch (error) {
498
+ InjectionError.throwInjectorError(token, currentDependency, error);
499
+ }
500
+ if (instance === undefined) {
501
+ throw new InjectionError(token, `Unable to create new instance from undefined value. Check your provider declaration for ${nameOf(token)}`);
502
+ }
503
+ if (instance && isClass(classOf(instance))) {
504
+ Reflect.defineProperty(instance, DI_INVOKE_OPTIONS, {
505
+ get: () => ({ rebuild: options.rebuild, locals: options.locals })
506
+ });
507
+ }
508
+ $emit("$afterInvoke", token, [instance, resolvedOpts]);
509
+ return instance;
421
510
  }
422
- if (instance && isClass(classOf(instance))) {
423
- Reflect.defineProperty(instance, DI_INVOKE_OPTIONS, {
424
- get: () => ({ rebuild: options.rebuild, locals: options.locals })
425
- });
511
+ finally {
512
+ this.#resolvingProviders.delete(provider.token);
426
513
  }
427
- $emit("$afterInvoke", token, [instance, resolvedOpts]);
428
- return instance;
514
+ }
515
+ mergeProviders(container) {
516
+ container.forEach((provider) => {
517
+ if (!this.#providers.has(provider.token)) {
518
+ this.setProvider(provider.token, provider.clone());
519
+ }
520
+ });
429
521
  }
430
522
  resolveImportsProviders() {
431
523
  const imports = this.settings.imports;
@@ -444,18 +536,22 @@ export class InjectorService extends Container {
444
536
  provider.useFactory = undefined;
445
537
  if ("useClass" in props) {
446
538
  provider.useClass = props.useClass;
539
+ this.setProvider(token, provider);
447
540
  continue;
448
541
  }
449
542
  if ("useFactory" in props) {
450
543
  provider.useFactory = props.useFactory;
544
+ this.setProvider(token, provider);
451
545
  continue;
452
546
  }
453
547
  if ("useAsyncFactory" in props) {
454
548
  provider.useAsyncFactory = props.useAsyncFactory;
549
+ this.setProvider(token, provider);
455
550
  continue;
456
551
  }
457
552
  if ("use" in props) {
458
553
  provider.useValue = props.use;
554
+ this.setProvider(token, provider);
459
555
  continue;
460
556
  }
461
557
  resolvedImports.push(meta);
@@ -477,11 +573,11 @@ export class InjectorService extends Container {
477
573
  throw new Error("Given token is undefined. Could mean a circular dependency problem. Try to use @Inject(() => Token) to solve it.");
478
574
  }
479
575
  let provider;
480
- if (!this.hasProvider(token)) {
576
+ if (!this.providers.get(token)) {
481
577
  provider = new Provider(token);
482
578
  }
483
579
  else {
484
- provider = this.getProvider(token);
580
+ provider = this.providers.get(token);
485
581
  }
486
582
  deps = deps || provider.deps;
487
583
  imports = imports || provider.imports;
@@ -525,9 +621,20 @@ export class InjectorService extends Container {
525
621
  }
526
622
  return;
527
623
  }
624
+ if (this.#registeredSingletonHooks.has(provider.token)) {
625
+ return;
626
+ }
627
+ this.#registeredSingletonHooks.add(provider.token);
528
628
  Object.entries(provider.hooks).forEach(([event, cb]) => {
529
629
  const callback = (...args) => {
530
- return cb(this.#cache.get(provider.token), ...args);
630
+ let instance = this.#cache.get(provider.token);
631
+ if (instance === undefined) {
632
+ if (event === "$onDestroy" || this.#resolvingProviders.has(provider.token)) {
633
+ return;
634
+ }
635
+ instance = this.resolve(provider.token);
636
+ }
637
+ return cb(instance, ...args);
531
638
  };
532
639
  $on(event, provider.token, callback);
533
640
  });
@@ -1,4 +1,4 @@
1
- import { DI_INJECTABLE_PROPS, createContainer, destroyInjector, inject, injector } from "../../common/index.js";
1
+ import { createContainer, destroyInjector, DI_INJECTABLE_PROPS, inject, injector } from "../../common/index.js";
2
2
  import { cleanAllLocalsContainer, detachLocalsContainer, localsContainer } from "../../common/fn/localsContainer.js";
3
3
  import { $log } from "@tsed/logger";
4
4
  import { DIContext } from "../../common/domain/DIContext.js";
@@ -16,48 +16,26 @@ import type { TokenProvider } from "../interfaces/TokenProvider.js";
16
16
  * const container = new Container();
17
17
  * container.add(MyService, {scope: ProviderScope.SINGLETON});
18
18
  *
19
- * const provider = container.getProvider(MyService);
20
- * const controllers = container.getProviders(ProviderType.CONTROLLER);
19
+ * const provider = container.get(MyService);
20
+ * const controllers = container.getMany(ProviderType.CONTROLLER);
21
21
  * ```
22
22
  *
23
23
  * @public
24
24
  */
25
25
  export declare class Container extends Map<TokenProvider, Provider> {
26
- /**
27
- *
28
- * @param token
29
- * @param settings
30
- */
31
- add(token: TokenProvider, settings?: Partial<ProviderOpts>): this;
32
26
  /**
33
27
  * Add a provider to the
34
28
  * @param token
35
29
  * @param settings
36
30
  */
37
- addProvider(token: TokenProvider, settings?: Partial<ProviderOpts>): this;
38
- /**
39
- *
40
- * @param token
41
- */
42
- hasProvider(token: TokenProvider): boolean;
43
- /**
44
- * Add a provider to the
45
- * @param token
46
- * @param provider
47
- */
48
- setProvider(token: TokenProvider, provider: Provider): this;
49
- /**
50
- * The getProvider() method returns a specified element from a Map object.
51
- * @returns {T} Returns the element associated with the specified key or undefined if the key can't be found in the Map object.
52
- * @param token
53
- */
54
- getProvider<T extends Provider = Provider>(token: TokenProvider | undefined): T | undefined;
31
+ add(token: TokenProvider, settings?: Partial<ProviderOpts>): this;
32
+ get<T extends Provider = Provider>(token: TokenProvider | undefined): T | undefined;
55
33
  /**
56
34
  * Get all providers registered in the injector container.
57
35
  *
58
36
  * @param type Filter the list by the given ProviderType.
59
37
  * @returns {[TokenProvider , Provider<any>][]}
60
38
  */
61
- getProviders(type?: TokenProvider | ProviderType | string | string[]): Provider[];
62
- addProviders(container: Map<TokenProvider, Provider>): void;
39
+ getMany(type?: TokenProvider | ProviderType | string | string[]): Provider[];
40
+ merge(container: Map<TokenProvider, Provider>): void;
63
41
  }
@@ -1,5 +1,5 @@
1
1
  import { DILogger } from "../../common/index.js";
2
- import { LogLevel } from "@tsed/logger";
2
+ import type { LogLevel } from "@tsed/logger";
3
3
  import { Hooks } from "@tsed/hooks";
4
4
  /**
5
5
  * Options for creating a context logger instance.
@@ -40,8 +40,8 @@ export declare class ContextLogger {
40
40
  readonly id: string;
41
41
  maxStackSize: number;
42
42
  constructor({ id, logger, dateStart, level, maxStackSize, additionalProps }: ContextLoggerOptions);
43
- set level(level: "debug" | "info" | "warn" | "error" | "off" | "all" | LogLevel);
44
43
  get level(): "debug" | "info" | "warn" | "error" | "off" | "all" | LogLevel;
44
+ set level(level: "debug" | "info" | "warn" | "error" | "off" | "all" | LogLevel);
45
45
  get hooks(): Hooks;
46
46
  private get stack();
47
47
  alterLog(cb: (data: any, level: "debug" | "info" | "warn" | "error" | "all", withRequest: boolean) => any): Hooks;
@@ -9,6 +9,11 @@ declare global {
9
9
  scopes: {
10
10
  [key: string]: ProviderScope;
11
11
  };
12
+ /**
13
+ * Defer construction of synchronous singleton providers without lifecycle hooks until first use.
14
+ * @default false
15
+ */
16
+ lazyProviders?: boolean;
12
17
  /**
13
18
  * Define dependencies to build the provider
14
19
  */
@@ -30,39 +30,150 @@ import type { TokenRoute } from "../interfaces/TokenRoute.js";
30
30
  export declare class DIConfiguration {
31
31
  readonly default: Map<string, any>;
32
32
  protected map: Map<string, any>;
33
+ /**
34
+ * Creates a configuration service with the built-in defaults merged with the supplied initial properties.
35
+ *
36
+ * @param initialProps Initial configuration values that override built-in defaults.
37
+ */
33
38
  constructor(initialProps?: {});
39
+ /**
40
+ * Gets the application version.
41
+ */
34
42
  get version(): string;
43
+ /**
44
+ * Sets the application version.
45
+ *
46
+ * @param v Version to store in the configuration.
47
+ */
35
48
  set version(v: string);
49
+ /**
50
+ * Gets the application's root directory.
51
+ */
36
52
  get rootDir(): string;
53
+ /**
54
+ * Sets the application's root directory.
55
+ *
56
+ * @param value Root directory to store in the configuration.
57
+ */
37
58
  set rootDir(value: string);
59
+ /**
60
+ * Gets the active runtime environment.
61
+ */
38
62
  get env(): Env;
63
+ /**
64
+ * Sets the active runtime environment.
65
+ *
66
+ * @param value Environment to store in the configuration.
67
+ */
39
68
  set env(value: Env);
69
+ /**
70
+ * Gets the providers and modules imported by the application.
71
+ */
40
72
  get imports(): (TokenProvider | ImportTokenProviderOpts)[];
73
+ /**
74
+ * Sets the providers and modules imported by the application.
75
+ *
76
+ * @param imports Provider tokens or import options to configure.
77
+ */
41
78
  set imports(imports: (TokenProvider | ImportTokenProviderOpts)[]);
79
+ /**
80
+ * Indicates whether synchronous singleton providers without hooks are deferred until first use.
81
+ */
82
+ get lazyProviders(): boolean;
83
+ /**
84
+ * Enables or disables deferred initialization of synchronous singleton providers without hooks.
85
+ *
86
+ * @param lazyProviders Whether to defer eligible providers.
87
+ */
88
+ set lazyProviders(lazyProviders: boolean);
89
+ /**
90
+ * Gets the route providers registered by the application.
91
+ */
42
92
  get routes(): TokenRoute[];
93
+ /**
94
+ * Sets the route providers registered by the application.
95
+ *
96
+ * @param routes Route providers to configure.
97
+ */
43
98
  set routes(routes: TokenRoute[]);
99
+ /**
100
+ * Gets the logger options.
101
+ */
44
102
  get logger(): Partial<DILoggerOptions>;
103
+ /**
104
+ * Merges logger options into the current logger configuration.
105
+ *
106
+ * @param value Logger options to merge.
107
+ */
45
108
  set logger(value: Partial<DILoggerOptions>);
109
+ /**
110
+ * Indicates whether the logger is configured at the `debug` level.
111
+ */
46
112
  get debug(): boolean;
113
+ /**
114
+ * Enables or disables debug-level logging.
115
+ *
116
+ * @param debug `true` to set the logger level to `debug`; `false` to set it to `info`.
117
+ */
47
118
  set debug(debug: boolean);
119
+ /**
120
+ * Gets route providers grouped by mount path.
121
+ */
48
122
  get mount(): Record<string, TokenProvider[]>;
123
+ /**
124
+ * Sets route providers grouped by mount path.
125
+ *
126
+ * @param value Route providers indexed by mount path.
127
+ */
49
128
  set mount(value: Record<string, TokenProvider[]>);
50
129
  /**
130
+ * Invokes a callback once for each configured key, including default and overridden values.
51
131
  *
52
- * @param callbackfn
53
- * @param thisArg
132
+ * @param callbackfn Callback invoked with the resolved value, key, and override map.
133
+ * @param thisArg Value to use as `this` when invoking the callback.
54
134
  */
55
135
  forEach(callbackfn: (value: any, index: string, map: Map<string, any>) => void, thisArg?: any): void;
136
+ /**
137
+ * Sets one or more configuration values.
138
+ *
139
+ * Property names matching a configuration accessor are delegated to that accessor; other keys support nested paths.
140
+ *
141
+ * @param propertyKey Configuration key and value, or an object containing multiple values.
142
+ * @param value Value to assign when `propertyKey` is a string.
143
+ * @returns This configuration instance for chaining.
144
+ */
56
145
  set(obj: Partial<TsED.Configuration>): this;
57
146
  set(propertyKey: string, value?: unknown): this;
147
+ /**
148
+ * Sets a configuration value directly in the override map, supporting nested property paths.
149
+ *
150
+ * @param propertyKey Configuration key or nested property path.
151
+ * @param value Value to store.
152
+ * @returns This configuration instance for chaining.
153
+ */
58
154
  setRaw(propertyKey: string, value: any): this;
59
155
  /**
156
+ * Gets a resolved configuration value, preferring explicit overrides over default values.
60
157
  *
61
- * @param propertyKey
62
- * @param defaultValue
63
- * @returns {undefined|any}
158
+ * @param propertyKey Configuration key or nested property path.
159
+ * @param defaultValue Value to return when neither the overrides nor defaults define the key.
160
+ * @returns The resolved configuration value.
64
161
  */
65
162
  get<T = any>(propertyKey: string, defaultValue?: T): T;
163
+ /**
164
+ * Defines a custom configuration member when a member with the same key does not already exist.
165
+ *
166
+ * @param key Name of the member to define.
167
+ * @param value Function or property descriptor to assign to the configuration prototype.
168
+ * @returns This configuration instance when the member already exists.
169
+ */
66
170
  decorate(key: string, value: ((...args: unknown[]) => unknown) | PropertyDescriptor): this | undefined;
171
+ /**
172
+ * Resolves a configuration value from overrides, defaults, or the supplied fallback in that order.
173
+ *
174
+ * @param propertyKey Configuration key or nested property path.
175
+ * @param defaultValue Value to return when no configured value exists.
176
+ * @returns The resolved configuration value.
177
+ */
67
178
  protected getRaw(propertyKey: string, defaultValue?: any): any;
68
179
  }
@@ -1,9 +1,11 @@
1
1
  import { Container } from "../domain/Container.js";
2
2
  import { DIConfiguration } from "./DIConfiguration.js";
3
3
  import type { DILogger } from "../interfaces/DILogger.js";
4
+ import type { ProviderOpts } from "../interfaces/ProviderOpts.js";
4
5
  import type { InvokeOptions } from "../interfaces/InvokeOptions.js";
5
6
  import { Provider } from "../domain/Provider.js";
6
7
  import type { TokenProvider } from "../interfaces/TokenProvider.js";
8
+ import type { ProviderType } from "../domain/ProviderType.js";
7
9
  /**
8
10
  * Core dependency injection orchestrator managing provider lifecycle and resolution.
9
11
  *
@@ -28,13 +30,34 @@ import type { TokenProvider } from "../interfaces/TokenProvider.js";
28
30
  *
29
31
  * @public
30
32
  */
31
- export declare class InjectorService extends Container {
33
+ export declare class InjectorService {
32
34
  #private;
33
35
  logger: DILogger;
34
36
  settings: DIConfiguration;
35
37
  private resolvedConfiguration;
36
38
  constructor();
39
+ get providers(): Pick<Container, "get" | "has" | "getMany">;
37
40
  isLoaded(): boolean;
41
+ add(token: TokenProvider, settings?: Partial<ProviderOpts>): this;
42
+ /**
43
+ * @alias InjectorService.add
44
+ */
45
+ addProvider(token: TokenProvider, settings?: Partial<ProviderOpts>): this;
46
+ setProvider(token: TokenProvider, provider: Provider): this;
47
+ /**
48
+ * Get a provider definition from the provider registry.
49
+ *
50
+ * Unlike `get()`, this method does not resolve the provider instance.
51
+ * @deprecated Since v8. Use injector().providers.get() instead
52
+ */
53
+ getProvider<T extends Provider = Provider>(token: TokenProvider | undefined): T | undefined;
54
+ /**
55
+ * Get provider definitions matching a type.
56
+ *
57
+ * Unlike `getMany()`, this method does not resolve provider instances.
58
+ * @deprecated Since v8. Use injector().providers.getMany() instead.
59
+ */
60
+ getProviders(type?: TokenProvider | ProviderType | string | string[]): Provider[];
38
61
  /**
39
62
  * Return a list of all instances built by the injector.
40
63
  *
@@ -237,6 +260,7 @@ export declare class InjectorService extends Container {
237
260
  * @private
238
261
  */
239
262
  protected invokeToken<T>(target: TokenProvider, options?: Partial<InvokeOptions>): T | Promise<T>;
263
+ private mergeProviders;
240
264
  private resolveImportsProviders;
241
265
  /**
242
266
  * Create options to invoke a provider or class.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsed/di",
3
- "version": "8.37.0",
3
+ "version": "8.38.0-beta.1",
4
4
  "description": "DI module for Ts.ED Framework",
5
5
  "source": "./src/index.ts",
6
6
  "type": "module",
@@ -29,11 +29,11 @@
29
29
  "uuid": "^14.0.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@tsed/barrels": "8.37.0",
33
- "@tsed/core": "8.37.0",
34
- "@tsed/hooks": "8.37.0",
32
+ "@tsed/barrels": "8.38.0-beta.1",
33
+ "@tsed/core": "8.38.0-beta.1",
34
+ "@tsed/hooks": "8.38.0-beta.1",
35
35
  "@tsed/logger": "^8.0.0",
36
- "@tsed/typescript": "8.37.0",
36
+ "@tsed/typescript": "8.38.0-beta.1",
37
37
  "cross-env": "7.0.3",
38
38
  "typescript": "6.0.2",
39
39
  "vite": "7.3.2",
@@ -61,5 +61,8 @@
61
61
  },
62
62
  "homepage": "https://github.com/tsedio/tsed/tree/production/packages/di",
63
63
  "author": "Romain Lenzotti",
64
- "license": "MIT"
64
+ "license": "MIT",
65
+ "publishConfig": {
66
+ "tag": "beta"
67
+ }
65
68
  }