@zanobijs/core 1.2.0 → 2.0.0-beta.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.
- package/lib/cjs/exceptions/circularModuleImport.exception.js +21 -0
- package/{exceptions → lib/cjs/exceptions}/constant.message.js +10 -6
- package/{exceptions → lib/cjs/exceptions}/invalid.module.exception.js +8 -7
- package/lib/cjs/exceptions/invalidProvider.module.exception.js +20 -0
- package/lib/cjs/exceptions/missingInjectToken.exception.js +20 -0
- package/{exceptions → lib/cjs/exceptions}/resolution.exception.js +11 -10
- package/lib/cjs/factory.js +213 -0
- package/lib/cjs/index.js +30 -0
- package/lib/cjs/injector/injector.js +141 -0
- package/{injector → lib/cjs/injector}/module.js +28 -26
- package/{metadata.js → lib/cjs/metadata.js} +31 -21
- package/lib/esm/exceptions/circularModuleImport.exception.js +19 -0
- package/lib/esm/exceptions/constant.message.js +8 -0
- package/lib/esm/exceptions/invalid.module.exception.js +18 -0
- package/lib/esm/exceptions/invalidProvider.module.exception.js +18 -0
- package/lib/esm/exceptions/missingInjectToken.exception.js +18 -0
- package/lib/esm/exceptions/resolution.exception.js +37 -0
- package/lib/esm/factory.js +210 -0
- package/lib/esm/index.js +9 -0
- package/{injector → lib/esm/injector}/injector.js +30 -17
- package/lib/esm/injector/module.js +213 -0
- package/lib/esm/metadata.js +164 -0
- package/lib/esm/package.json +1 -0
- package/lib/types/exceptions/circularModuleImport.exception.d.ts +13 -0
- package/{exceptions → lib/types/exceptions}/constant.message.d.ts +2 -0
- package/lib/types/exceptions/index.d.ts +6 -0
- package/lib/types/exceptions/missingInjectToken.exception.d.ts +12 -0
- package/lib/types/factory.d.ts +106 -0
- package/lib/types/index.d.ts +4 -0
- package/{injector → lib/types/injector}/injector.d.ts +2 -0
- package/{metadata.d.ts → lib/types/metadata.d.ts} +8 -1
- package/package.json +20 -4
- package/CHANGELOG.md +0 -336
- package/LICENSE +0 -33
- package/exceptions/index.d.ts +0 -2
- package/exceptions/index.js +0 -5
- package/exceptions/invalidProvider.module.exception.js +0 -19
- package/factory.d.ts +0 -55
- package/factory.js +0 -120
- package/index.d.ts +0 -3
- package/index.js +0 -8
- package/injector/index.js +0 -5
- package/interfaces/factory.interface.js +0 -2
- package/interfaces/globals.interface.js +0 -2
- package/interfaces/index.js +0 -5
- package/tsconfig.build.json +0 -17
- /package/{exceptions → lib/types/exceptions}/invalid.module.exception.d.ts +0 -0
- /package/{exceptions → lib/types/exceptions}/invalidProvider.module.exception.d.ts +0 -0
- /package/{exceptions → lib/types/exceptions}/resolution.exception.d.ts +0 -0
- /package/{injector → lib/types/injector}/index.d.ts +0 -0
- /package/{injector → lib/types/injector}/module.d.ts +0 -0
- /package/{interfaces → lib/types/interfaces}/factory.interface.d.ts +0 -0
- /package/{interfaces → lib/types/interfaces}/globals.interface.d.ts +0 -0
- /package/{interfaces → lib/types/interfaces}/index.d.ts +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var constants = require('@zanobijs/common/utils/constants');
|
|
4
|
+
|
|
5
5
|
/**
|
|
6
6
|
* La clase `Metadata` proporciona métodos para acceder y manipular
|
|
7
7
|
* metadatos relacionados con diversos componentes y módulos.
|
|
@@ -9,11 +9,11 @@ const constants_1 = require("@zanobijs/common/utils/constants");
|
|
|
9
9
|
class Metadata {
|
|
10
10
|
static instance;
|
|
11
11
|
metadataMap = {
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
12
|
+
[constants.IS_MODULE]: 'module',
|
|
13
|
+
[constants.IS_IMPORTS]: 'import',
|
|
14
|
+
[constants.IS_CONTROLLER]: 'controller',
|
|
15
|
+
[constants.IS_SERVICE]: 'service',
|
|
16
|
+
[constants.IS_EXPORT]: 'export',
|
|
17
17
|
};
|
|
18
18
|
constructor() { }
|
|
19
19
|
/**
|
|
@@ -36,10 +36,10 @@ class Metadata {
|
|
|
36
36
|
*/
|
|
37
37
|
getMetadataModule(module) {
|
|
38
38
|
return {
|
|
39
|
-
imports: Reflect.getMetadata(
|
|
40
|
-
controllers: Reflect.getMetadata(
|
|
41
|
-
services: Reflect.getMetadata(
|
|
42
|
-
exports: Reflect.getMetadata(
|
|
39
|
+
imports: Reflect.getMetadata(constants.MODULE_IMPORTS, module),
|
|
40
|
+
controllers: Reflect.getMetadata(constants.MODULE_CONTROLLERS, module),
|
|
41
|
+
services: Reflect.getMetadata(constants.MODULE_SERVICES, module),
|
|
42
|
+
exports: Reflect.getMetadata(constants.MODULE_EXPORTS, module),
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
@@ -62,7 +62,7 @@ class Metadata {
|
|
|
62
62
|
* @returns Las dependencias de clase de la clase.
|
|
63
63
|
*/
|
|
64
64
|
getClassDependencies(target) {
|
|
65
|
-
return Reflect.getMetadata(
|
|
65
|
+
return Reflect.getMetadata(constants.DEPENDENCIES_CLASS, target);
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
68
|
* Obtiene las dependencias de parámetro asociadas con una clase.
|
|
@@ -71,7 +71,7 @@ class Metadata {
|
|
|
71
71
|
* @returns Las dependencias de parámetro de la clase.
|
|
72
72
|
*/
|
|
73
73
|
getParameterDependencies(target) {
|
|
74
|
-
return Reflect.getMetadata(
|
|
74
|
+
return Reflect.getMetadata(constants.DEPENDENCIES_PARAMETERS, target);
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
77
|
* Obtiene las dependencias a inyectar asociadas con una clase.
|
|
@@ -80,7 +80,16 @@ class Metadata {
|
|
|
80
80
|
* @returns Un Map con las dependencias a inyectar de la clase.
|
|
81
81
|
*/
|
|
82
82
|
getInjectionDependencies(target) {
|
|
83
|
-
return Reflect.getMetadata(
|
|
83
|
+
return Reflect.getMetadata(constants.DEPENDENCIES_INJECT, target) || new Map();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Obtiene el lifetime declarado en `@Injectable`/`@Controller` de una clase.
|
|
87
|
+
*
|
|
88
|
+
* @param { TClass } target - La función/clase objetivo.
|
|
89
|
+
* @returns El lifetime de la clase. `'singleton'` si no se declaró ninguno.
|
|
90
|
+
*/
|
|
91
|
+
getLifetime(target) {
|
|
92
|
+
return Reflect.getMetadata(constants.LIFETIME_CLASS, target) ?? 'singleton';
|
|
84
93
|
}
|
|
85
94
|
/**
|
|
86
95
|
* Determina el tipo de una clase basado en sus metadatos.
|
|
@@ -114,7 +123,7 @@ class Metadata {
|
|
|
114
123
|
* @returns Verdadero si el target es de tipo "module", falso en caso contrario.
|
|
115
124
|
*/
|
|
116
125
|
isTypeModule(target) {
|
|
117
|
-
return this.hasMetadata(
|
|
126
|
+
return this.hasMetadata(constants.IS_MODULE, target);
|
|
118
127
|
}
|
|
119
128
|
/**
|
|
120
129
|
* Verifica si una clase es de tipo "import".
|
|
@@ -123,7 +132,7 @@ class Metadata {
|
|
|
123
132
|
* @returns Verdadero si el target es de tipo "import", falso en caso contrario.
|
|
124
133
|
*/
|
|
125
134
|
isTypeImport(target) {
|
|
126
|
-
return this.hasMetadata(
|
|
135
|
+
return this.hasMetadata(constants.IS_IMPORTS, target);
|
|
127
136
|
}
|
|
128
137
|
/**
|
|
129
138
|
* Verifica si una clase es de tipo "controller".
|
|
@@ -132,7 +141,7 @@ class Metadata {
|
|
|
132
141
|
* @returns Verdadero si el target es de tipo "controller", falso en caso contrario.
|
|
133
142
|
*/
|
|
134
143
|
isTypeController(target) {
|
|
135
|
-
return this.hasMetadata(
|
|
144
|
+
return this.hasMetadata(constants.IS_CONTROLLER, target);
|
|
136
145
|
}
|
|
137
146
|
/**
|
|
138
147
|
* Verifica si una clase es de tipo "service".
|
|
@@ -141,7 +150,7 @@ class Metadata {
|
|
|
141
150
|
* @returns Verdadero si el target es de tipo "service", falso en caso contrario.
|
|
142
151
|
*/
|
|
143
152
|
isTypeService(target) {
|
|
144
|
-
return this.hasMetadata(
|
|
153
|
+
return this.hasMetadata(constants.IS_SERVICE, target);
|
|
145
154
|
}
|
|
146
155
|
/**
|
|
147
156
|
* Verifica si una clase es de tipo "export".
|
|
@@ -150,7 +159,8 @@ class Metadata {
|
|
|
150
159
|
* @returns Verdadero si el target es de tipo "export", falso en caso contrario.
|
|
151
160
|
*/
|
|
152
161
|
isTypeExports(target) {
|
|
153
|
-
return this.hasMetadata(
|
|
162
|
+
return this.hasMetadata(constants.IS_EXPORT, target);
|
|
154
163
|
}
|
|
155
164
|
}
|
|
165
|
+
|
|
156
166
|
exports.Metadata = Metadata;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RuntimeException } from '@zanobijs/common/exceptions/runtime.exception';
|
|
2
|
+
import { CIRCULAR_MODULE_IMPORT_ERROR } from './constant.message.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Excepción lanzada cuando el grafo de `imports` entre módulos contiene un
|
|
6
|
+
* ciclo: un módulo importa, directa o indirectamente, a otro módulo que ya
|
|
7
|
+
* lo está importando a él.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Esta clase extiende la base `RuntimeException` de @zanobijs/common
|
|
11
|
+
* para proporcionar detalles adicionales del error.
|
|
12
|
+
*/
|
|
13
|
+
class CircularModuleImportException extends RuntimeException {
|
|
14
|
+
constructor(chain) {
|
|
15
|
+
super(CIRCULAR_MODULE_IMPORT_ERROR(chain));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { CircularModuleImportException };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const MODULE_INVALID_ANNOTATION_ERROR = () => 'The class must have an annotation @Module()';
|
|
2
|
+
const CONTAINER_RESOLUTION_ERROR = (entity, resolutionError) => `${resolutionError} please review '${entity}' and its dependencies.`;
|
|
3
|
+
const CONTAINER_RESOLUTION_ENTITY_ERROR = (entity) => `Please check that the entity '${entity}' exists and is registered in @modulo`;
|
|
4
|
+
const PROVIDER_INVALID_MODULE_ERROR = (entity, moduleName) => `Please check that ${entity} located in the @module ${moduleName} exists and is @Injectable().`;
|
|
5
|
+
const MISSING_INJECT_TOKEN_ERROR = (token, targetName, moduleName) => `Cannot inject @Inject('${token}') into '${targetName}': the provider '${token}' is not registered in '${moduleName}' or any other previously loaded module.`;
|
|
6
|
+
const CIRCULAR_MODULE_IMPORT_ERROR = (chain) => `Circular module import detected: ${chain}. A module cannot import, directly or indirectly, another module that already imports it.`;
|
|
7
|
+
|
|
8
|
+
export { CIRCULAR_MODULE_IMPORT_ERROR, CONTAINER_RESOLUTION_ENTITY_ERROR, CONTAINER_RESOLUTION_ERROR, MISSING_INJECT_TOKEN_ERROR, MODULE_INVALID_ANNOTATION_ERROR, PROVIDER_INVALID_MODULE_ERROR };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { RuntimeException } from '@zanobijs/common/exceptions/runtime.exception';
|
|
2
|
+
import { MODULE_INVALID_ANNOTATION_ERROR } from './constant.message.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Excepción lanzada cuando una clase Modulo no tiene el decorador `@Module`
|
|
6
|
+
* de @zanobijs/common
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Esta clase extiende la base `RuntimeException`de @zanobijs/common
|
|
10
|
+
* para proporcionar detalles adicionales específicos a esquemas de módulos inválidos.
|
|
11
|
+
*/
|
|
12
|
+
class InvalidModuleAnnotationException extends RuntimeException {
|
|
13
|
+
constructor(detail = '') {
|
|
14
|
+
super(MODULE_INVALID_ANNOTATION_ERROR(), detail);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { InvalidModuleAnnotationException };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { RuntimeException } from '@zanobijs/common/exceptions/runtime.exception';
|
|
2
|
+
import { PROVIDER_INVALID_MODULE_ERROR } from './constant.message.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Excepción lanzada cuando una clase Modulo no tiene un provedor definido correctamente
|
|
6
|
+
* de @zanobijs/common
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Esta clase extiende la base `RuntimeException`de @zanobijs/common
|
|
10
|
+
* para proporcionar detalles adicionales específicos a esquemas de módulos inválidos.
|
|
11
|
+
*/
|
|
12
|
+
class InvalidProviderModuleException extends RuntimeException {
|
|
13
|
+
constructor(entity, moduleName, detail) {
|
|
14
|
+
super(PROVIDER_INVALID_MODULE_ERROR(entity, moduleName), detail);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { InvalidProviderModuleException };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { RuntimeException } from '@zanobijs/common/exceptions/runtime.exception';
|
|
2
|
+
import { MISSING_INJECT_TOKEN_ERROR } from './constant.message.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Excepción lanzada cuando un `@Inject(token)` no encuentra su provider
|
|
6
|
+
* registrado, ni en el módulo actual ni en ninguno de los módulos importados.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Esta clase extiende la base `RuntimeException` de @zanobijs/common
|
|
10
|
+
* para proporcionar detalles adicionales del error.
|
|
11
|
+
*/
|
|
12
|
+
class MissingInjectTokenException extends RuntimeException {
|
|
13
|
+
constructor(token, targetName, moduleName) {
|
|
14
|
+
super(MISSING_INJECT_TOKEN_ERROR(token, targetName, moduleName));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { MissingInjectTokenException };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { RuntimeException } from '@zanobijs/common/exceptions/runtime.exception';
|
|
2
|
+
import { CONTAINER_RESOLUTION_ENTITY_ERROR, CONTAINER_RESOLUTION_ERROR } from './constant.message.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Excepción lanzada cuando se intenta obtener una dependencia
|
|
6
|
+
* de un controlador o servicio y esta no fue registrada
|
|
7
|
+
* en un modulo
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Esta clase extiende la base `RuntimeException`de @zanobijs/core
|
|
11
|
+
* para proporcionar detalles adicionales del error.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* private service1: Service1
|
|
15
|
+
* @Module{... services:["otherService"]}
|
|
16
|
+
*/
|
|
17
|
+
class ContainerResolutionException extends RuntimeException {
|
|
18
|
+
constructor(entity, resolutionError, detail) {
|
|
19
|
+
super(CONTAINER_RESOLUTION_ERROR(entity, resolutionError), detail);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Excepción lanzada cuando se intenta obtener una entidad
|
|
24
|
+
* (importar, controlador, servicio o exportar) que no fue
|
|
25
|
+
* declarada en `@Module` de @zanobijs/common
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* Esta clase extiende la base `RuntimeException`de @zanobijs/core
|
|
29
|
+
* para proporcionar detalles adicionales del error.
|
|
30
|
+
*/
|
|
31
|
+
class ContainerResolutionEntityException extends RuntimeException {
|
|
32
|
+
constructor(entity, detail) {
|
|
33
|
+
super(CONTAINER_RESOLUTION_ENTITY_ERROR(entity), detail);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { ContainerResolutionEntityException, ContainerResolutionException };
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { createContainer, InjectionMode, AwilixResolutionError } from 'awilix';
|
|
3
|
+
import { Module } from './injector/module.js';
|
|
4
|
+
import { ContainerResolutionEntityException, ContainerResolutionException } from './exceptions/resolution.exception.js';
|
|
5
|
+
import { CircularModuleImportException } from './exceptions/circularModuleImport.exception.js';
|
|
6
|
+
import { Logger } from '@zanobijs/common/utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resuelve `className` desde `container` traduciendo los errores de `awilix`
|
|
10
|
+
* al vocabulario de excepciones de `ZanobiJS`. Compartido entre `Factory.get()`
|
|
11
|
+
* y `RequestScope.get()` para no duplicar el manejo de `AwilixResolutionError`.
|
|
12
|
+
* @throws {ContainerResolutionEntityException | ContainerResolutionException}
|
|
13
|
+
* Si `awilix` no logra resolver la entidad o alguna de sus dependencias.
|
|
14
|
+
* @throws Propaga sin modificar cualquier otro error (p. ej. uno lanzado por
|
|
15
|
+
* el propio constructor de la clase que se está resolviendo).
|
|
16
|
+
*/
|
|
17
|
+
function resolveEntity(container, className, logger) {
|
|
18
|
+
try {
|
|
19
|
+
return container.resolve(className);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (!(error instanceof AwilixResolutionError)) {
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
logger.info('Error resolving entity: ', error.message + '\n');
|
|
26
|
+
const resolutionError = error.message.split('\n');
|
|
27
|
+
const classNameFound = resolutionError[0].match(/'([^']+)'/);
|
|
28
|
+
if (classNameFound?.[1] === className) {
|
|
29
|
+
throw new ContainerResolutionEntityException(className, error.message);
|
|
30
|
+
}
|
|
31
|
+
throw new ContainerResolutionException(className, resolutionError[0], error.message);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Handle atado a un scope de `awilix` puntual (una copia liviana del
|
|
36
|
+
* contenedor raíz, ver `Factory.createRequestScope`). Resuelve entidades
|
|
37
|
+
* `singleton` desde el contenedor raíz (reusadas, gratis) y entidades
|
|
38
|
+
* `request` desde este scope (instancia nueva, se descarta con él).
|
|
39
|
+
*/
|
|
40
|
+
class RequestScope {
|
|
41
|
+
container;
|
|
42
|
+
logger;
|
|
43
|
+
constructor(container, logger) {
|
|
44
|
+
this.container = container;
|
|
45
|
+
this.logger = logger;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resuelve y devuelve una instancia del scope teniendo en cuenta el
|
|
49
|
+
* nombre de la entidad proporcionada.
|
|
50
|
+
* @param {string} className - Nombre de la entidad a resolver.
|
|
51
|
+
* @returns {T} - Instancia resuelta.
|
|
52
|
+
*/
|
|
53
|
+
get(className) {
|
|
54
|
+
return resolveEntity(this.container, className, this.logger);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Factory es una clase que facilita la creación y configuración de
|
|
59
|
+
* contenedores de inyección de dependencias utilizando metadatos y
|
|
60
|
+
* la librería `awilix` para registrar y resolver entidades como:
|
|
61
|
+
* controladores y servicios.
|
|
62
|
+
*/
|
|
63
|
+
class Factory {
|
|
64
|
+
moduleHandler;
|
|
65
|
+
registeredClasses = {};
|
|
66
|
+
container;
|
|
67
|
+
logger;
|
|
68
|
+
options;
|
|
69
|
+
scannedModules = new Set();
|
|
70
|
+
processedModules = new Set();
|
|
71
|
+
constructor(appModule, options = {}) {
|
|
72
|
+
this.options = options;
|
|
73
|
+
this.evaluateOptions();
|
|
74
|
+
this.logger = Logger();
|
|
75
|
+
this.moduleHandler = new Module();
|
|
76
|
+
this.scanProviderModule(appModule);
|
|
77
|
+
this.registerProviderScanedModules();
|
|
78
|
+
this.processClassModule(appModule);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Se encarga de escaenear modulo por modulo los proveedores con el fin de luego
|
|
82
|
+
* poder ser registrados e inyectados en quien depende de ese proveedor
|
|
83
|
+
* @param {TClass} module - Módulo desde el que se escanearan los proveedores.
|
|
84
|
+
* @param {TClass[]} path - Cadena de módulos ancestros en la recursión actual,
|
|
85
|
+
* usada para detectar ciclos de imports.
|
|
86
|
+
* @private
|
|
87
|
+
* @throws {CircularModuleImportException} Si `module` ya está en `path`.
|
|
88
|
+
*/
|
|
89
|
+
scanProviderModule(module, path = []) {
|
|
90
|
+
if (path.includes(module)) {
|
|
91
|
+
throw new CircularModuleImportException([...path, module].map((m) => m.name).join(' -> '));
|
|
92
|
+
}
|
|
93
|
+
/** Un mismo módulo puede llegar por varias ramas del grafo de imports
|
|
94
|
+
* (ej. un CommonModule importado por varios módulos de feature). No es
|
|
95
|
+
* un error: ya fue escaneado, no hay nada más que hacer aquí. */
|
|
96
|
+
if (this.scannedModules.has(module)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this.scannedModules.add(module);
|
|
100
|
+
this.logger.debug('Factory - Scan Module:', module.name);
|
|
101
|
+
this.moduleHandler.setup(module);
|
|
102
|
+
this.moduleHandler.scan();
|
|
103
|
+
this.logger.debug('===================================================');
|
|
104
|
+
const importedModules = this.moduleHandler.getImports();
|
|
105
|
+
if (importedModules && importedModules.length) {
|
|
106
|
+
const nextPath = [...path, module];
|
|
107
|
+
importedModules.forEach((moduleImport) => {
|
|
108
|
+
this.scanProviderModule(moduleImport, nextPath);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Se encarga tomar la lista de proveedores y registralos para luego se resueltos
|
|
114
|
+
* cuando el usuario lo solicite
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
registerProviderScanedModules() {
|
|
118
|
+
this.moduleHandler.registerAllProviders();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Procesa modulo por modulos registrando los controladores, servicios y proveedores,
|
|
122
|
+
* estos se le irá inyectando sus dependecias resueltas para ser usados por el usuario
|
|
123
|
+
* @param {TClass} module - Módulo desde el que se registrarán las clases.
|
|
124
|
+
* @private
|
|
125
|
+
*
|
|
126
|
+
* @remarks
|
|
127
|
+
* No repite la detección de ciclos: `scanProviderModule` ya recorrió este
|
|
128
|
+
* mismo grafo de imports antes (misma metadata, mismas aristas) y habría
|
|
129
|
+
* lanzado `CircularModuleImportException` si existiera uno. Solo se
|
|
130
|
+
* deduplica por módulo (`processedModules`) para el caso de diamante.
|
|
131
|
+
*/
|
|
132
|
+
processClassModule(module) {
|
|
133
|
+
if (this.processedModules.has(module)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this.processedModules.add(module);
|
|
137
|
+
this.logger.debug('Factory - Process Class Module:', module.name);
|
|
138
|
+
this.moduleHandler.setup(module);
|
|
139
|
+
this.moduleHandler.initialize();
|
|
140
|
+
Object.assign(this.registeredClasses, this.moduleHandler.getRegisterClass());
|
|
141
|
+
this.logger.success('Factory - Process Class Module - Completion!!!', module.name);
|
|
142
|
+
const importedModules = this.moduleHandler.getImports();
|
|
143
|
+
if (importedModules && importedModules.length) {
|
|
144
|
+
importedModules.forEach((moduleImport) => {
|
|
145
|
+
this.processClassModule(moduleImport);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
this.logger.debug('===================================================');
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Crea el contenedor de inyección de dependencias y registra el listado de
|
|
152
|
+
* controladores, servicio y proveedores que se ha venido creando a parti de escaneos
|
|
153
|
+
* y registros de clases.
|
|
154
|
+
* @returns {Factory} - Instancia actual de la fábrica.
|
|
155
|
+
*/
|
|
156
|
+
create() {
|
|
157
|
+
this.container = createContainer({
|
|
158
|
+
injectionMode: InjectionMode.CLASSIC,
|
|
159
|
+
strict: true,
|
|
160
|
+
});
|
|
161
|
+
this.container.register(this.registeredClasses);
|
|
162
|
+
this.logger.info('Factory - classes and providers registered in the container', Object.keys(this.registeredClasses));
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Resuelve y devuelve una instancia del contenedor
|
|
167
|
+
* teniendo en cuenta el nombre de la entidad proporcionada.
|
|
168
|
+
* @param {string} className - Nombre de la entidad a resolver.
|
|
169
|
+
* @returns {T} - Instancia resuelta.
|
|
170
|
+
* @throws {ContainerResolutionEntityException | ContainerResolutionException}
|
|
171
|
+
* Si `awilix` no logra resolver la entidad o alguna de sus dependencias.
|
|
172
|
+
* @throws Propaga sin modificar cualquier otro error (p. ej. uno lanzado por
|
|
173
|
+
* el propio constructor de la clase que se está resolviendo).
|
|
174
|
+
*/
|
|
175
|
+
get(className) {
|
|
176
|
+
return resolveEntity(this.container, className, this.logger);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Crea una copia liviana del contenedor raíz para usar durante una
|
|
180
|
+
* única invocación/petición (Lambda, Azure Function, request HTTP...).
|
|
181
|
+
*
|
|
182
|
+
* Las entidades `singleton` se reusan tal cual desde el contenedor raíz
|
|
183
|
+
* (cero costo). Las entidades con `lifetime: 'request'` (ver `@Injectable`)
|
|
184
|
+
* se resuelven de cero dentro de este scope y se descartan junto con él.
|
|
185
|
+
*
|
|
186
|
+
* @returns {RequestScope} - Handle atado a este scope puntual.
|
|
187
|
+
*/
|
|
188
|
+
createRequestScope() {
|
|
189
|
+
const scope = this.container.createScope();
|
|
190
|
+
return new RequestScope(scope, this.logger);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Se encarga de evaluar las opciones para ver si o no aplica y realizar lo correspondiente
|
|
194
|
+
*
|
|
195
|
+
* `ZANOBIJS_LOGGER`/`ZANOBIJS_LOGGER_USER` son pensadas para poder prenderse/apagarse
|
|
196
|
+
* desde la configuración de despliegue (variables de entorno de Lambda/contenedor) sin
|
|
197
|
+
* tocar código ni redeployar. Por eso, si el operador ya las configuró externamente,
|
|
198
|
+
* NO se pisan — `options` solo aplica como default cuando la variable no existe.
|
|
199
|
+
*/
|
|
200
|
+
evaluateOptions() {
|
|
201
|
+
if (process.env.ZANOBIJS_LOGGER === undefined) {
|
|
202
|
+
process.env.ZANOBIJS_LOGGER = String(this.options.activeLoggerSystem ?? false);
|
|
203
|
+
}
|
|
204
|
+
if (process.env.ZANOBIJS_LOGGER_USER === undefined) {
|
|
205
|
+
process.env.ZANOBIJS_LOGGER_USER = String(this.options.activeLoggerUser ?? false);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export { Factory, RequestScope };
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { Factory, RequestScope } from './factory.js';
|
|
2
|
+
export { Module } from './injector/module.js';
|
|
3
|
+
export { Injector } from './injector/injector.js';
|
|
4
|
+
export { CIRCULAR_MODULE_IMPORT_ERROR, CONTAINER_RESOLUTION_ENTITY_ERROR, CONTAINER_RESOLUTION_ERROR, MISSING_INJECT_TOKEN_ERROR, MODULE_INVALID_ANNOTATION_ERROR, PROVIDER_INVALID_MODULE_ERROR } from './exceptions/constant.message.js';
|
|
5
|
+
export { InvalidModuleAnnotationException } from './exceptions/invalid.module.exception.js';
|
|
6
|
+
export { InvalidProviderModuleException } from './exceptions/invalidProvider.module.exception.js';
|
|
7
|
+
export { MissingInjectTokenException } from './exceptions/missingInjectToken.exception.js';
|
|
8
|
+
export { CircularModuleImportException } from './exceptions/circularModuleImport.exception.js';
|
|
9
|
+
export { ContainerResolutionEntityException, ContainerResolutionException } from './exceptions/resolution.exception.js';
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { Metadata } from '../metadata.js';
|
|
2
|
+
import { Logger } from '@zanobijs/common/utils';
|
|
3
|
+
import { isClass, isEmpty } from '@zanobijs/common/utils/shared.utils';
|
|
4
|
+
import { asFunction, asValue, asClass } from 'awilix';
|
|
5
|
+
import { MissingInjectTokenException } from '../exceptions/missingInjectToken.exception.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Mapea el `lifetime` público de `ZanobiJS` al método fluido de `awilix`.
|
|
9
|
+
* `request` usa `Lifetime.SCOPED` de `awilix` internamente: sin un scope real
|
|
10
|
+
* creado (ver `Factory.createRequestScope`), se resuelve igual que `singleton`.
|
|
11
|
+
*/
|
|
12
|
+
const LIFETIME_METHOD = {
|
|
13
|
+
singleton: 'singleton',
|
|
14
|
+
request: 'scoped',
|
|
15
|
+
transient: 'transient',
|
|
16
|
+
};
|
|
8
17
|
/**
|
|
9
18
|
* La clase `Injector` es la encargada de manejar la inyección de dependencias
|
|
10
19
|
* solo para parametros tipo objecto { provider, useValue }
|
|
@@ -23,8 +32,8 @@ class Injector {
|
|
|
23
32
|
* para poderlo procesar.
|
|
24
33
|
*/
|
|
25
34
|
constructor(module, listProviders, listProvidersClass) {
|
|
26
|
-
this.metadata =
|
|
27
|
-
this.logger =
|
|
35
|
+
this.metadata = Metadata.getInstance();
|
|
36
|
+
this.logger = Logger();
|
|
28
37
|
this.moduleName = module.name;
|
|
29
38
|
this.module = module;
|
|
30
39
|
this.listProviders = listProviders;
|
|
@@ -46,16 +55,16 @@ class Injector {
|
|
|
46
55
|
this.listProvidersClass.set(key, service.useClass);
|
|
47
56
|
}
|
|
48
57
|
else if (service.useFactory) {
|
|
49
|
-
this.listProviders.set(key,
|
|
58
|
+
this.listProviders.set(key, asFunction(service.useFactory).scoped());
|
|
50
59
|
}
|
|
51
60
|
else {
|
|
52
|
-
this.listProviders.set(key,
|
|
61
|
+
this.listProviders.set(key, asValue(service.useValue));
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
|
-
if (typeof service === 'object' &&
|
|
64
|
+
if (typeof service === 'object' && isClass(service.provider)) {
|
|
56
65
|
let useExample = false;
|
|
57
66
|
const key = service.provider.name;
|
|
58
|
-
if (service.useClass &&
|
|
67
|
+
if (service.useClass && isClass(service.useClass)) {
|
|
59
68
|
this.listProvidersClass.set(key, service.useClass);
|
|
60
69
|
}
|
|
61
70
|
else {
|
|
@@ -79,6 +88,8 @@ class Injector {
|
|
|
79
88
|
*
|
|
80
89
|
* @param { TClass} target - La clase objetivo.
|
|
81
90
|
* @returns {object} - Objeto con datos a inyectar.
|
|
91
|
+
* @throws {MissingInjectTokenException} Si un `@Inject(token)` de `target` no tiene
|
|
92
|
+
* su provider registrado en este módulo ni en ninguno de los módulos importados.
|
|
82
93
|
*/
|
|
83
94
|
getInjectData(target) {
|
|
84
95
|
const injectData = {};
|
|
@@ -91,7 +102,7 @@ class Injector {
|
|
|
91
102
|
injectData[paramName] = providerValue.resolve();
|
|
92
103
|
}
|
|
93
104
|
else {
|
|
94
|
-
|
|
105
|
+
throw new MissingInjectTokenException(key, target.name, this.moduleName);
|
|
95
106
|
}
|
|
96
107
|
}
|
|
97
108
|
}
|
|
@@ -109,8 +120,9 @@ class Injector {
|
|
|
109
120
|
*/
|
|
110
121
|
getInjectorClass(target) {
|
|
111
122
|
const injectData = this.getInjectData(target);
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
const lifetime = this.metadata.getLifetime(target);
|
|
124
|
+
let injector = asClass(target)[LIFETIME_METHOD[lifetime]]();
|
|
125
|
+
if (!isEmpty(injectData)) {
|
|
114
126
|
this.logger.debug(`Inject - list dependencies to inject of ${target.name}:`, injectData);
|
|
115
127
|
injector = injector.inject(() => injectData);
|
|
116
128
|
}
|
|
@@ -123,4 +135,5 @@ class Injector {
|
|
|
123
135
|
return this.listProviders;
|
|
124
136
|
}
|
|
125
137
|
}
|
|
126
|
-
|
|
138
|
+
|
|
139
|
+
export { Injector };
|