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