@tsed/di 8.16.0-rc.1 → 8.16.0-rc.3

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.
@@ -1,5 +1,4 @@
1
- import { ProviderType } from "../domain/ProviderType.js";
2
- import { Injectable } from "./injectable.js";
1
+ import { interceptor } from "../fn/injectable.js";
3
2
  /**
4
3
  * The decorators `@Interceptor()` declare a new service can be injected in other service or controller on there `constructor`.
5
4
  * All services annotated with `@Interceptor()` are constructed one time.
@@ -10,7 +9,7 @@ import { Injectable } from "./injectable.js";
10
9
  * @decorator
11
10
  */
12
11
  export function Interceptor() {
13
- return Injectable({
14
- type: ProviderType.INTERCEPTOR
15
- });
12
+ return (target) => {
13
+ interceptor(target);
14
+ };
16
15
  }
@@ -1,6 +1,7 @@
1
1
  import { getValue } from "@tsed/core/utils/getValue.js";
2
+ import { isFunction } from "@tsed/core/utils/isFunction.js";
2
3
  import { setValue } from "@tsed/core/utils/setValue.js";
3
- export const CONFIGURATION = Symbol.for("CONFIGURATION");
4
+ import { $alter } from "@tsed/hooks";
4
5
  export class DIConfiguration {
5
6
  constructor(initialProps = {}) {
6
7
  this.default = new Map();
@@ -8,6 +9,7 @@ export class DIConfiguration {
8
9
  Object.entries({
9
10
  imports: [],
10
11
  routes: [],
12
+ mount: {},
11
13
  logger: {},
12
14
  ...initialProps
13
15
  }).forEach(([key, value]) => {
@@ -57,6 +59,12 @@ export class DIConfiguration {
57
59
  set debug(debug) {
58
60
  this.logger = { ...this.logger, level: debug ? "debug" : "info" };
59
61
  }
62
+ get mount() {
63
+ return this.get("mount");
64
+ }
65
+ set mount(value) {
66
+ this.setRaw("mount", value);
67
+ }
60
68
  /**
61
69
  *
62
70
  * @param callbackfn
@@ -67,13 +75,9 @@ export class DIConfiguration {
67
75
  callbackfn(this.getRaw(key), key, this.map);
68
76
  }, thisArg);
69
77
  }
70
- /**
71
- *
72
- * @param propertyKey
73
- * @param value
74
- */
75
78
  set(propertyKey, value) {
76
79
  if (typeof propertyKey === "string") {
80
+ value = $alter(`$alterConfig:${propertyKey}`, value);
77
81
  if (Reflect.has(this, propertyKey)) {
78
82
  // @ts-ignore
79
83
  this[propertyKey] = value;
@@ -102,6 +106,16 @@ export class DIConfiguration {
102
106
  get(propertyKey, defaultValue) {
103
107
  return this.getRaw(propertyKey, defaultValue);
104
108
  }
109
+ decorate(key, value) {
110
+ if (key in this) {
111
+ return this;
112
+ }
113
+ Object.defineProperty(DIConfiguration.prototype, key, isFunction(value)
114
+ ? {
115
+ value
116
+ }
117
+ : value);
118
+ }
105
119
  getRaw(propertyKey, defaultValue) {
106
120
  const value = getValue(this.map, propertyKey);
107
121
  if (value !== undefined) {
@@ -46,17 +46,13 @@ export class InjectorService extends Container {
46
46
  constructor() {
47
47
  super();
48
48
  this.logger = console;
49
+ /*readonly */
50
+ this.settings = new DIConfiguration();
49
51
  this.resolvedConfiguration = false;
50
52
  this.#cache = new LocalsContainer();
51
53
  this.#loaded = false;
52
54
  this.#cache.set(InjectorService, this);
53
- this.#cache.set(DIConfiguration, new DIConfiguration());
54
- }
55
- get settings() {
56
- return this.#cache.get(DIConfiguration);
57
- }
58
- set settings(settings) {
59
- this.#cache.set(DIConfiguration, settings);
55
+ this.#cache.set(DIConfiguration, this.settings);
60
56
  }
61
57
  isLoaded() {
62
58
  return this.#loaded;
@@ -6,4 +6,4 @@ import { DIConfiguration } from "../services/DIConfiguration.js";
6
6
  */
7
7
  export declare function Configuration(): any;
8
8
  export declare function Configuration(settings: Partial<TsED.Configuration>): ClassDecorator;
9
- export type Configuration = TsED.DIConfiguration & DIConfiguration;
9
+ export type Configuration = TsED.Configuration & DIConfiguration;
@@ -7,4 +7,4 @@
7
7
  * @returns {Function}
8
8
  * @decorator
9
9
  */
10
- export declare function Interceptor(): Function;
10
+ export declare function Interceptor(): ClassDecorator;
@@ -1,5 +1,5 @@
1
1
  import type { TokenProvider } from "../interfaces/TokenProvider.js";
2
2
  import { DIConfiguration } from "../services/DIConfiguration.js";
3
- export declare function configuration(): TsED.DIConfiguration & DIConfiguration;
3
+ export declare function configuration(): TsED.Configuration & DIConfiguration;
4
4
  export declare function configuration(token: TokenProvider): Partial<TsED.Configuration>;
5
5
  export declare function configuration(token: TokenProvider, configuration: Partial<TsED.Configuration>): Partial<TsED.Configuration>;
@@ -5,11 +5,6 @@ declare global {
5
5
  namespace TsED {
6
6
  interface Context {
7
7
  }
8
- /**
9
- * Here to allow extension on DIConfiguration base service
10
- */
11
- interface DIConfiguration {
12
- }
13
8
  interface Configuration extends Record<string, any> {
14
9
  scopes: {
15
10
  [key: string]: ProviderScope;
@@ -25,5 +20,3 @@ declare global {
25
20
  }
26
21
  }
27
22
  }
28
- export interface DIConfigurationOptions extends TsED.Configuration {
29
- }
@@ -3,7 +3,6 @@ import type { DILoggerOptions } from "../interfaces/DILoggerOptions.js";
3
3
  import type { ImportTokenProviderOpts } from "../interfaces/ImportTokenProviderOpts.js";
4
4
  import type { TokenProvider } from "../interfaces/TokenProvider.js";
5
5
  import type { TokenRoute } from "../interfaces/TokenRoute.js";
6
- export declare const CONFIGURATION: unique symbol;
7
6
  export declare class DIConfiguration {
8
7
  readonly default: Map<string, any>;
9
8
  protected map: Map<string, any>;
@@ -22,18 +21,16 @@ export declare class DIConfiguration {
22
21
  set logger(value: Partial<DILoggerOptions>);
23
22
  get debug(): boolean;
24
23
  set debug(debug: boolean);
24
+ get mount(): Record<string, TokenProvider[]>;
25
+ set mount(value: Record<string, TokenProvider[]>);
25
26
  /**
26
27
  *
27
28
  * @param callbackfn
28
29
  * @param thisArg
29
30
  */
30
31
  forEach(callbackfn: (value: any, index: string, map: Map<string, any>) => void, thisArg?: any): void;
31
- /**
32
- *
33
- * @param propertyKey
34
- * @param value
35
- */
36
- set(propertyKey: string | Partial<TsED.Configuration>, value?: any): this;
32
+ set(obj: Partial<TsED.Configuration>): this;
33
+ set(propertyKey: string, value?: unknown): this;
37
34
  setRaw(propertyKey: string, value: any): this;
38
35
  /**
39
36
  *
@@ -42,5 +39,6 @@ export declare class DIConfiguration {
42
39
  * @returns {undefined|any}
43
40
  */
44
41
  get<T = any>(propertyKey: string, defaultValue?: T): T;
42
+ decorate(key: string, value: ((...args: unknown[]) => unknown) | PropertyDescriptor): this | undefined;
45
43
  protected getRaw(propertyKey: string, defaultValue?: any): any;
46
44
  }
@@ -27,10 +27,9 @@ import { DIConfiguration } from "./DIConfiguration.js";
27
27
  export declare class InjectorService extends Container {
28
28
  #private;
29
29
  logger: DILogger;
30
+ settings: DIConfiguration;
30
31
  private resolvedConfiguration;
31
32
  constructor();
32
- get settings(): DIConfiguration;
33
- set settings(settings: DIConfiguration);
34
33
  isLoaded(): boolean;
35
34
  /**
36
35
  * Return a list of instance build by the injector.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tsed/di",
3
3
  "description": "DI module for Ts.ED Framework",
4
4
  "type": "module",
5
- "version": "8.16.0-rc.1",
5
+ "version": "8.16.0-rc.3",
6
6
  "source": "./src/index.ts",
7
7
  "main": "./lib/esm/index.js",
8
8
  "module": "./lib/esm/index.js",
@@ -29,11 +29,11 @@
29
29
  "uuid": "^10.0.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@tsed/barrels": "8.16.0-rc.1",
33
- "@tsed/core": "8.16.0-rc.1",
34
- "@tsed/hooks": "8.16.0-rc.1",
35
- "@tsed/logger": "^8.0.0-rc.1",
36
- "@tsed/typescript": "8.16.0-rc.1",
32
+ "@tsed/barrels": "8.16.0-rc.3",
33
+ "@tsed/core": "8.16.0-rc.3",
34
+ "@tsed/hooks": "8.16.0-rc.3",
35
+ "@tsed/logger": "^8.0.0",
36
+ "@tsed/typescript": "8.16.0-rc.3",
37
37
  "cross-env": "7.0.3",
38
38
  "eslint": "9.12.0",
39
39
  "typescript": "5.4.5",
@@ -43,7 +43,7 @@
43
43
  "peerDependencies": {
44
44
  "@tsed/core": ">=8.0.0",
45
45
  "@tsed/hooks": ">=8.0.0",
46
- "@tsed/logger": ">=8.0.0-rc.1"
46
+ "@tsed/logger": ">=8.0.0"
47
47
  },
48
48
  "peerDependenciesMeta": {
49
49
  "@tsed/core": {
package/readme.md CHANGED
@@ -36,7 +36,9 @@
36
36
  <code><img height="20" src="https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/chrome/chrome.png"></code>
37
37
  <code><img height="20" src="https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/react/react.png"></code>
38
38
 
39
- A powerful and flexible Dependency Injection (DI) toolkit inspired by Angular, designed for both TypeScript and pure JavaScript applications. Use it standalone or as the foundation of Ts.ED. Supports both decorator-based and functional (decorator-less) APIs for maximum compatibility, even in non-TypeScript or pure JS projects.
39
+ A powerful and flexible Dependency Injection (DI) toolkit inspired by Angular, designed for both TypeScript and pure
40
+ JavaScript applications. Use it standalone or as the foundation of Ts.ED. Supports both decorator-based and functional (
41
+ decorator-less) APIs for maximum compatibility, even in non-TypeScript or pure JS projects.
40
42
 
41
43
  ---
42
44
 
@@ -59,15 +61,26 @@ npm install --save @tsed/di @tsed/core @tsed/hooks @tsed/logger
59
61
 
60
62
  ## Features
61
63
 
62
- - **[Providers](https://tsed.dev/docs/providers.html):** Register and manage services, factories, values, interceptors, and more.
63
- - **[Functional API](https://tsed.dev/docs/providers.html#injectable):** Register providers and factories without decorators (for pure JS or decorator-less code).
64
- - **[Async Factories](https://tsed.dev/docs/custom-providers.html#register-async-factory):** Create providers that resolve asynchronously (e.g., database connections).
65
- - **[Constants Injection](https://tsed.dev/docs/providers.html#constant):** Use `@Constant()` or `constant()` to inject configuration or static values.
66
- - **[Value Injection](https://tsed.dev/docs/providers.html#value-refvalue):** Use `@Value()` or `refValue()` to inject resolved provider values.
67
- - **[InjectContext](https://tsed.dev/docs/providers.html#inject-context):** Access the injection context and advanced DI features.
68
- - **[Config Sources](https://tsed.dev/docs/configuration/configuration-sources.html):** Load and merge configuration from multiple sources (files, env, remote, etc.).
69
- - **[Lifecycle hooks](https://tsed.dev/docs/hooks.html):** Manage provider lifecycle with hooks like `$onInit`, `$onDestroy`.
70
- - **[Lazy loading](https://tsed.dev/docs/providers.html#lazy-load-provider):** Lazily load Node.js ESM modules. The lazy loading feature allows you to declare a provider that is only instantiated when it is first injected. The DI system will dynamically import the ESM module, discover injectable services within it, and instantiate them on demand—enabling efficient code splitting and reducing startup time.
64
+ - **[Providers](https://tsed.dev/docs/providers.html):** Register and manage services, factories, values, interceptors,
65
+ and more.
66
+ - **[Functional API](https://tsed.dev/docs/providers.html#injectable):** Register providers and factories without
67
+ decorators (for pure JS or decorator-less code).
68
+ - **[Async Factories](https://tsed.dev/docs/custom-providers.html#register-async-factory):** Create providers that
69
+ resolve asynchronously (e.g., database connections).
70
+ - **[Constants Injection](https://tsed.dev/docs/providers.html#constant):** Use `@Constant()` or `constant()` to inject
71
+ configuration or static values.
72
+ - **[Value Injection](https://tsed.dev/docs/providers.html#value-refvalue):** Use `@Value()` or `refValue()` to inject
73
+ resolved provider values.
74
+ - **[InjectContext](https://tsed.dev/docs/providers.html#inject-context):** Access the injection context and advanced DI
75
+ features.
76
+ - **[Config Sources](https://tsed.dev/docs/configuration/configuration-sources.html):** Load and merge configuration
77
+ from multiple sources (files, env, remote, etc.).
78
+ - **[Lifecycle hooks](https://tsed.dev/docs/hooks.html):** Manage provider lifecycle with hooks like `$onInit`,
79
+ `$onDestroy`.
80
+ - **[Lazy loading](https://tsed.dev/docs/providers.html#lazy-load-provider):** Lazily load Node.js ESM modules. The lazy
81
+ loading feature allows you to declare a provider that is only instantiated when it is first injected. The DI system
82
+ will dynamically import the ESM module, discover injectable services within it, and instantiate them on
83
+ demand—enabling efficient code splitting and reducing startup time.
71
84
  - **Full TypeScript support:** Type inference with `inject()` and advanced tooling for type-safe DI.
72
85
 
73
86
  ---
@@ -118,7 +131,8 @@ You can use it in any JS/TS project (web, CLI, backend, etc) without Ts.ED.
118
131
 
119
132
  ## Stable & Recommended Initialization Example
120
133
 
121
- This is the recommended and most stable way to initialize and use the injector, especially when working with advanced scenarios (settings, async providers, custom loggers, etc):
134
+ This is the recommended and most stable way to initialize and use the injector, especially when working with advanced
135
+ scenarios (settings, async providers, custom loggers, etc):
122
136
 
123
137
  ```typescript
124
138
  import {injector, attachLogger, inject} from "@tsed/di";
@@ -127,12 +141,6 @@ import {CalendarCtrl} from "./CalendarCtrl.js";
127
141
 
128
142
  // Create a new InjectorService instance
129
143
  const inj = injector();
130
-
131
- // Register your configuration provider
132
- inj.addProvider(PlatformConfiguration);
133
-
134
- // Optionally invoke and set configuration
135
- inj.settings = inj.invoke(PlatformConfiguration);
136
144
  inj.settings.set(settings);
137
145
 
138
146
  // Attach a custom logger (optional)
@@ -198,11 +206,6 @@ import "./services/GetAllowerUsers.js"; // just add import is enough to discover
198
206
  // Create a new InjectorService instance
199
207
  const inj = injector();
200
208
 
201
- // Register your configuration provider
202
- inj.addProvider(PlatformConfiguration);
203
-
204
- // Optionally invoke and set configuration
205
- inj.settings = inj.invoke(PlatformConfiguration);
206
209
  inj.settings.set(settings);
207
210
 
208
211
  // Attach a custom logger (optional)
@@ -213,9 +216,11 @@ attachLogger($log); // Overriding the default logger is not recommended
213
216
  await inj.load();
214
217
  ```
215
218
 
216
- The example above is the main point to start the DI system. It should be placed in the main entry file of your application.
219
+ The example above is the main point to start the DI system. It should be placed in the main entry file of your
220
+ application.
217
221
 
218
- Now, you can use the `inject()` function to retrieve the instance of the class, injectable provider, or pure JavaScript function.
222
+ Now, you can use the `inject()` function to retrieve the instance of the class, injectable provider, or pure JavaScript
223
+ function.
219
224
 
220
225
  For example, if you want to use the `GET_ALLOWED_USERS` factory in your application, you can do it like this:
221
226
 
@@ -246,7 +251,8 @@ In summary:
246
251
 
247
252
  ## Async Factories
248
253
 
249
- You can register **async factories** to provide values/services that require asynchronous initialization (e.g., database connections):
254
+ You can register **async factories** to provide values/services that require asynchronous initialization (e.g., database
255
+ connections):
250
256
 
251
257
  ```typescript
252
258
  import {injectable, inject} from "@tsed/di";
@@ -269,7 +275,8 @@ db.query("SELECT * FROM users");
269
275
 
270
276
  ## Injecting Constants with `@Constant()` and `@Value()`
271
277
 
272
- You can inject constant values or configuration using the `@Constant()` decorator (or `constant()` function in the Functional API):
278
+ You can inject constant values or configuration using the `@Constant()` decorator (or `constant()` function in the
279
+ Functional API):
273
280
 
274
281
  ```typescript
275
282
  import {Injectable, Constant} from "@tsed/di";
@@ -284,7 +291,8 @@ export class MyService {
284
291
  }
285
292
  ```
286
293
 
287
- You can also use `@Value()` to inject the resolved value of a provider (by token), or `refValue()` for the functional API:
294
+ You can also use `@Value()` to inject the resolved value of a provider (by token), or `refValue()` for the functional
295
+ API:
288
296
 
289
297
  ```typescript
290
298
  import {Injectable, Value} from "@tsed/di";
@@ -331,6 +339,77 @@ See [Providers documentation](https://tsed.dev/docs/providers.html).
331
339
 
332
340
  ---
333
341
 
342
+ ## Extending DIConfiguration
343
+
344
+ The `DIConfiguration` class provides a `decorate` method that allows you to extend its functionality by adding new methods or properties. You can access this method through the `configuration()` function.
345
+
346
+ ```typescript
347
+ import {configuration} from "@tsed/di";
348
+
349
+ // Add a custom method to DIConfiguration
350
+ configuration().decorate("myCustomMethod", function () {
351
+ // Your custom logic here
352
+ return "Custom result";
353
+ });
354
+
355
+ // Usage
356
+ const result = configuration().myCustomMethod(); // "Custom result"
357
+ ```
358
+
359
+ You can also add a property with a custom getter/setter:
360
+
361
+ ```typescript
362
+ configuration().decorate("customProperty", {
363
+ get() {
364
+ return this.get("someInternalValue");
365
+ },
366
+ set(value) {
367
+ this.set("someInternalValue", value);
368
+ }
369
+ });
370
+ ```
371
+
372
+ This is particularly useful for plugin authors who want to extend the configuration capabilities without modifying the core code.
373
+
374
+ ---
375
+
376
+ ## Using the $alterConfig:propertyKey Hook
377
+
378
+ The `$alterConfig:propertyKey` hook allows you to intercept and modify configuration values before they are assigned to a property. This is useful when you need to transform, validate, or augment configuration values dynamically.
379
+
380
+ When a value is set in the configuration using the `set()` method, the DIConfiguration class internally calls the `$alter` hook with the pattern `$alterConfig:${propertyKey}`, passing the value as the second argument.
381
+
382
+ Here's how to use this hook:
383
+
384
+ ```typescript
385
+ import {$on} from "@tsed/hooks";
386
+
387
+ // Register a hook to intercept and modify the 'jsonMapper' configuration
388
+ $on("$alterConfig:jsonMapper", (options) => {
389
+ // Modify the options before they are assigned
390
+ options.strictGroups = Boolean(options.strictGroups);
391
+ options.disableUnsecureConstructor = Boolean(options.disableUnsecureConstructor);
392
+ options.additionalProperties = Boolean(
393
+ isBoolean(options.additionalProperties) ? options.additionalProperties : options.additionalProperties === "accept"
394
+ );
395
+
396
+ // Return the modified options
397
+ return options;
398
+ });
399
+ ```
400
+
401
+ This hook is particularly useful for:
402
+
403
+ - Normalizing configuration values
404
+ - Applying default values
405
+ - Validating configuration before it's applied
406
+ - Transforming configuration formats
407
+ - Implementing cross-cutting concerns for configuration properties
408
+
409
+ The hook is executed whenever the corresponding property is set, whether through direct assignment or through the `configuration().set()` method.
410
+
411
+ ---
412
+
334
413
  ## Documentation & Resources
335
414
 
336
415
  - **[Providers](https://tsed.dev/docs/providers.html)**
@@ -354,7 +433,8 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com
354
433
 
355
434
  ## Sponsors
356
435
 
357
- Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/tsed#sponsor)]
436
+ Support this project by becoming a sponsor. Your logo will show up here with a link to your
437
+ website. [[Become a sponsor](https://opencollective.com/tsed#sponsor)]
358
438
 
359
439
  ## License
360
440
 
@@ -362,8 +442,15 @@ The MIT License (MIT)
362
442
 
363
443
  Copyright (c) 2016 - 2022 Romain Lenzotti
364
444
 
365
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
445
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
446
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
447
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
448
+ persons to whom the Software is furnished to do so, subject to the following conditions:
366
449
 
367
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
450
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
451
+ Software.
368
452
 
369
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
453
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
454
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
455
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
456
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.