jerkjs 2.1.7 → 2.2.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/CHANGELOG.md +30 -0
- package/README.md +201 -4
- package/index.js +29 -4
- package/lib/core/server.js +328 -27
- package/lib/loader/routeLoader.js +148 -117
- package/lib/mvc/GenericAdapter.js +136 -0
- package/lib/mvc/MariaDBAdapter.js +315 -0
- package/lib/mvc/MemoryAdapter.js +269 -0
- package/lib/mvc/ModelControllerExample.js +285 -0
- package/lib/mvc/controllerBase.js +60 -0
- package/lib/mvc/modelBase.js +383 -0
- package/lib/mvc/modelManager.js +284 -0
- package/lib/mvc/userModel.js +265 -0
- package/lib/mvc/viewEngine.js +32 -1
- package/lib/utils/mimeType.js +62 -0
- package/package.json +5 -3
- package/BUG_REPORTE_COMPRESION.txt +0 -72
- package/JERK_FRAMEWORK_DIAGRAM.txt +0 -492
- package/JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd +0 -124
- package/JERK_FRAMEWORK_DOCUMENTATION.md +0 -527
- package/LICENSE +0 -201
- package/README_EN.md +0 -230
- package/README_PT.md +0 -230
- package/docs/ARQUITECTURA_ROUTES.md +0 -140
- package/docs/EXTENSION_MANUAL.md +0 -955
- package/docs/FIREWALL_MANUAL.md +0 -416
- package/docs/HOOK-2.0.md +0 -512
- package/docs/HOOKS_REFERENCE_IMPROVED.md +0 -596
- package/docs/MANUAL_API_SDK.md +0 -536
- package/docs/MARIADB_TOKENS_IMPLEMENTATION.md +0 -110
- package/docs/MIDDLEWARE_MANUAL.md +0 -518
- package/docs/OAUTH2_GOOGLE_MANUAL.md +0 -405
- package/docs/ROUTING_WITHOUT_JSON_GUIDE.md +0 -454
- package/docs/frontend-and-sessions.md +0 -353
- package/docs/guia_inicio_rapido_jerkjs.md +0 -113
- package/examples/examples.arj +0 -0
- package/standard/CompressionTestController.js +0 -56
- package/standard/HealthController.js +0 -16
- package/standard/HomeController.js +0 -12
- package/standard/ProductController.js +0 -18
- package/standard/README.md +0 -47
- package/standard/UserController.js +0 -23
- package/standard/package.json +0 -22
- package/standard/routes.json +0 -65
- package/standard/server.js +0 -140
- package/standardA/controllers/AuthController.js +0 -82
- package/standardA/controllers/HomeController.js +0 -19
- package/standardA/controllers/UserController.js +0 -41
- package/standardA/server.js +0 -311
- package/standardA/views/auth/dashboard.html +0 -51
- package/standardA/views/auth/login.html +0 -47
- package/standardA/views/index.html +0 -32
- package/standardA/views/users/detail.html +0 -28
- package/standardA/views/users/list.html +0 -36
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clase base para modelos en el framework JERK
|
|
3
|
+
* Implementación del componente MVC modelBase.js
|
|
4
|
+
* Permite la comunicación bidireccional con controladores y la interacción con componentes del framework
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Importar hooks dinámicamente para evitar dependencias circulares
|
|
8
|
+
let _hooks = null;
|
|
9
|
+
const getHooks = () => {
|
|
10
|
+
if (!_hooks) {
|
|
11
|
+
_hooks = require('../../index.js').hooks;
|
|
12
|
+
}
|
|
13
|
+
return _hooks;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
class ModelBase {
|
|
17
|
+
/**
|
|
18
|
+
* Constructor del modelo base
|
|
19
|
+
* @param {Object} options - Opciones de configuración del modelo
|
|
20
|
+
* @param {string} options.tableName - Nombre de la tabla en la base de datos
|
|
21
|
+
* @param {Object} options.adapter - Adaptador de base de datos a utilizar
|
|
22
|
+
* @param {Object} options.logger - Instancia de logger para auditoría
|
|
23
|
+
*/
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.tableName = options.tableName || this.constructor.name.toLowerCase();
|
|
26
|
+
this.adapter = options.adapter || null;
|
|
27
|
+
this.logger = options.logger || console;
|
|
28
|
+
|
|
29
|
+
// Almacenar referencias a controladores asociados
|
|
30
|
+
this.controllers = new Set();
|
|
31
|
+
|
|
32
|
+
// Inicializar hooks para el modelo
|
|
33
|
+
this.initializeHooks();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Inicializa los hooks específicos para el modelo
|
|
38
|
+
*/
|
|
39
|
+
initializeHooks() {
|
|
40
|
+
// Disparar hook cuando se crea una instancia del modelo
|
|
41
|
+
const hooks = getHooks();
|
|
42
|
+
if (hooks) {
|
|
43
|
+
hooks.doAction('model_created', this.constructor.name, this);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Establece el adaptador de base de datos para el modelo
|
|
49
|
+
* @param {Object} adapter - Adaptador de base de datos
|
|
50
|
+
*/
|
|
51
|
+
setAdapter(adapter) {
|
|
52
|
+
this.adapter = adapter;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Registra un controlador que puede interactuar con este modelo
|
|
57
|
+
* @param {Object} controller - Instancia del controlador
|
|
58
|
+
*/
|
|
59
|
+
registerController(controller) {
|
|
60
|
+
this.controllers.add(controller);
|
|
61
|
+
|
|
62
|
+
// Disparar hook cuando se registra un controlador
|
|
63
|
+
const hooks = getHooks();
|
|
64
|
+
if (hooks) {
|
|
65
|
+
hooks.doAction('controller_registered_with_model', controller.constructor.name, this.constructor.name);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Elimina un controlador registrado
|
|
71
|
+
* @param {Object} controller - Instancia del controlador
|
|
72
|
+
*/
|
|
73
|
+
unregisterController(controller) {
|
|
74
|
+
this.controllers.delete(controller);
|
|
75
|
+
|
|
76
|
+
// Disparar hook cuando se elimina un controlador
|
|
77
|
+
const hooks = getHooks();
|
|
78
|
+
if (hooks) {
|
|
79
|
+
hooks.doAction('controller_unregistered_from_model', controller.constructor.name, this.constructor.name);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Obtiene todos los controladores registrados
|
|
85
|
+
* @returns {Set} - Conjunto de controladores registrados
|
|
86
|
+
*/
|
|
87
|
+
getControllers() {
|
|
88
|
+
return this.controllers;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Método para crear un nuevo registro
|
|
93
|
+
* @param {Object} data - Datos del nuevo registro
|
|
94
|
+
* @returns {Promise<Object>} - Promesa con el resultado de la operación
|
|
95
|
+
*/
|
|
96
|
+
async create(data) {
|
|
97
|
+
if (!this.adapter) {
|
|
98
|
+
throw new Error(`No se ha configurado un adaptador para el modelo ${this.constructor.name}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Disparar hook antes de crear
|
|
102
|
+
const hooks = getHooks();
|
|
103
|
+
if (hooks) {
|
|
104
|
+
data = hooks.applyFilters('model_before_create', data, this.constructor.name);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const result = await this.adapter.create(this.tableName, data);
|
|
109
|
+
|
|
110
|
+
// Disparar hook después de crear
|
|
111
|
+
if (hooks) {
|
|
112
|
+
hooks.doAction('model_after_create', result, this.constructor.name);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
this.logger.error(`Error creando registro en ${this.tableName}:`, error.message);
|
|
118
|
+
|
|
119
|
+
// Disparar hook en caso de error
|
|
120
|
+
if (hooks) {
|
|
121
|
+
hooks.doAction('model_create_error', error, this.constructor.name);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Método para obtener registros
|
|
130
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
131
|
+
* @param {Object} options - Opciones adicionales (orden, límites, etc.)
|
|
132
|
+
* @returns {Promise<Array>} - Promesa con los resultados
|
|
133
|
+
*/
|
|
134
|
+
async find(conditions = {}, options = {}) {
|
|
135
|
+
if (!this.adapter) {
|
|
136
|
+
throw new Error(`No se ha configurado un adaptador para el modelo ${this.constructor.name}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Disparar hook antes de buscar
|
|
140
|
+
const hooks = getHooks();
|
|
141
|
+
if (hooks) {
|
|
142
|
+
conditions = hooks.applyFilters('model_before_find', conditions, this.constructor.name);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const results = await this.adapter.find(this.tableName, conditions, options);
|
|
147
|
+
|
|
148
|
+
// Disparar hook después de buscar
|
|
149
|
+
if (hooks) {
|
|
150
|
+
hooks.doAction('model_after_find', results, this.constructor.name);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return results;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
this.logger.error(`Error buscando registros en ${this.tableName}:`, error.message);
|
|
156
|
+
|
|
157
|
+
// Disparar hook en caso de error
|
|
158
|
+
if (hooks) {
|
|
159
|
+
hooks.doAction('model_find_error', error, this.constructor.name);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Método para obtener un solo registro
|
|
168
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
169
|
+
* @param {Object} options - Opciones adicionales
|
|
170
|
+
* @returns {Promise<Object|null>} - Promesa con el resultado o null
|
|
171
|
+
*/
|
|
172
|
+
async findOne(conditions = {}, options = {}) {
|
|
173
|
+
if (!this.adapter) {
|
|
174
|
+
throw new Error(`No se ha configurado un adaptador para el modelo ${this.constructor.name}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Disparar hook antes de buscar uno
|
|
178
|
+
const hooks = getHooks();
|
|
179
|
+
if (hooks) {
|
|
180
|
+
conditions = hooks.applyFilters('model_before_find_one', conditions, this.constructor.name);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const result = await this.adapter.findOne(this.tableName, conditions, options);
|
|
185
|
+
|
|
186
|
+
// Disparar hook después de buscar uno
|
|
187
|
+
if (hooks) {
|
|
188
|
+
hooks.doAction('model_after_find_one', result, this.constructor.name);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return result;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
this.logger.error(`Error buscando un registro en ${this.tableName}:`, error.message);
|
|
194
|
+
|
|
195
|
+
// Disparar hook en caso de error
|
|
196
|
+
if (hooks) {
|
|
197
|
+
hooks.doAction('model_find_one_error', error, this.constructor.name);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Método para actualizar registros
|
|
206
|
+
* @param {Object} conditions - Condiciones para seleccionar registros a actualizar
|
|
207
|
+
* @param {Object} data - Datos a actualizar
|
|
208
|
+
* @returns {Promise<number>} - Número de registros afectados
|
|
209
|
+
*/
|
|
210
|
+
async update(conditions, data) {
|
|
211
|
+
if (!this.adapter) {
|
|
212
|
+
throw new Error(`No se ha configurado un adaptador para el modelo ${this.constructor.name}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Disparar hook antes de actualizar
|
|
216
|
+
const hooks = getHooks();
|
|
217
|
+
if (hooks) {
|
|
218
|
+
const hookResult = hooks.applyFilters('model_before_update', { conditions, data }, this.constructor.name);
|
|
219
|
+
conditions = hookResult.conditions;
|
|
220
|
+
data = hookResult.data;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const result = await this.adapter.update(this.tableName, conditions, data);
|
|
225
|
+
|
|
226
|
+
// Disparar hook después de actualizar
|
|
227
|
+
if (hooks) {
|
|
228
|
+
hooks.doAction('model_after_update', result, data, this.constructor.name);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return result;
|
|
232
|
+
} catch (error) {
|
|
233
|
+
this.logger.error(`Error actualizando registros en ${this.tableName}:`, error.message);
|
|
234
|
+
|
|
235
|
+
// Disparar hook en caso de error
|
|
236
|
+
if (hooks) {
|
|
237
|
+
hooks.doAction('model_update_error', error, this.constructor.name);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Método para eliminar registros
|
|
246
|
+
* @param {Object} conditions - Condiciones para seleccionar registros a eliminar
|
|
247
|
+
* @returns {Promise<number>} - Número de registros eliminados
|
|
248
|
+
*/
|
|
249
|
+
async delete(conditions) {
|
|
250
|
+
if (!this.adapter) {
|
|
251
|
+
throw new Error(`No se ha configurado un adaptador para el modelo ${this.constructor.name}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Disparar hook antes de eliminar
|
|
255
|
+
const hooks = getHooks();
|
|
256
|
+
if (hooks) {
|
|
257
|
+
conditions = hooks.applyFilters('model_before_delete', conditions, this.constructor.name);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const result = await this.adapter.delete(this.tableName, conditions);
|
|
262
|
+
|
|
263
|
+
// Disparar hook después de eliminar
|
|
264
|
+
if (hooks) {
|
|
265
|
+
hooks.doAction('model_after_delete', result, this.constructor.name);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return result;
|
|
269
|
+
} catch (error) {
|
|
270
|
+
this.logger.error(`Error eliminando registros en ${this.tableName}:`, error.message);
|
|
271
|
+
|
|
272
|
+
// Disparar hook en caso de error
|
|
273
|
+
if (hooks) {
|
|
274
|
+
hooks.doAction('model_delete_error', error, this.constructor.name);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Método para contar registros
|
|
283
|
+
* @param {Object} conditions - Condiciones de conteo
|
|
284
|
+
* @returns {Promise<number>} - Número de registros
|
|
285
|
+
*/
|
|
286
|
+
async count(conditions = {}) {
|
|
287
|
+
if (!this.adapter) {
|
|
288
|
+
throw new Error(`No se ha configurado un adaptador para el modelo ${this.constructor.name}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const hooks = getHooks();
|
|
292
|
+
try {
|
|
293
|
+
const result = await this.adapter.count(this.tableName, conditions);
|
|
294
|
+
|
|
295
|
+
// Disparar hook después de contar
|
|
296
|
+
if (hooks) {
|
|
297
|
+
hooks.doAction('model_count_result', result, this.constructor.name);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return result;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
this.logger.error(`Error contando registros en ${this.tableName}:`, error.message);
|
|
303
|
+
|
|
304
|
+
// Disparar hook en caso de error
|
|
305
|
+
if (hooks) {
|
|
306
|
+
hooks.doAction('model_count_error', error, this.constructor.name);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Método para ejecutar consultas personalizadas
|
|
315
|
+
* @param {string} query - Consulta personalizada
|
|
316
|
+
* @param {Array} params - Parámetros de la consulta
|
|
317
|
+
* @returns {Promise<any>} - Resultado de la consulta
|
|
318
|
+
*/
|
|
319
|
+
async query(query, params = []) {
|
|
320
|
+
if (!this.adapter) {
|
|
321
|
+
throw new Error(`No se ha configurado un adaptador para el modelo ${this.constructor.name}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const hooks = getHooks();
|
|
325
|
+
try {
|
|
326
|
+
const result = await this.adapter.query(query, params);
|
|
327
|
+
|
|
328
|
+
// Disparar hook después de la consulta
|
|
329
|
+
if (hooks) {
|
|
330
|
+
hooks.doAction('model_custom_query_result', result, query, this.constructor.name);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return result;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
this.logger.error(`Error ejecutando consulta personalizada en ${this.tableName}:`, error.message);
|
|
336
|
+
|
|
337
|
+
// Disparar hook en caso de error
|
|
338
|
+
if (hooks) {
|
|
339
|
+
hooks.doAction('model_custom_query_error', error, query, this.constructor.name);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Método para validar datos antes de operaciones CRUD
|
|
348
|
+
* @param {string} operation - Operación a realizar (create, update, etc.)
|
|
349
|
+
* @param {Object} data - Datos a validar
|
|
350
|
+
* @returns {boolean} - Verdadero si los datos son válidos
|
|
351
|
+
*/
|
|
352
|
+
validate(operation, data) {
|
|
353
|
+
// Disparar hook para validación
|
|
354
|
+
const hooks = getHooks();
|
|
355
|
+
if (hooks) {
|
|
356
|
+
const validationResult = hooks.applyFilters(
|
|
357
|
+
'model_validate',
|
|
358
|
+
{ isValid: true, errors: [] },
|
|
359
|
+
operation,
|
|
360
|
+
data,
|
|
361
|
+
this.constructor.name
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
return validationResult;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return { isValid: true, errors: [] };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Método para serializar el modelo a JSON
|
|
372
|
+
* @returns {Object} - Representación JSON del modelo
|
|
373
|
+
*/
|
|
374
|
+
toJSON() {
|
|
375
|
+
return {
|
|
376
|
+
tableName: this.tableName,
|
|
377
|
+
hasAdapter: !!this.adapter,
|
|
378
|
+
registeredControllersCount: this.controllers.size
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
module.exports = ModelBase;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gestor de modelos para el framework JERK
|
|
3
|
+
* Implementación del componente MVC modelManager.js
|
|
4
|
+
* Administra instancias de modelos y su interacción con componentes del framework
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const ModelBase = require('./modelBase');
|
|
8
|
+
|
|
9
|
+
// Importar hooks dinámicamente para evitar dependencias circulares
|
|
10
|
+
let _hooks = null;
|
|
11
|
+
const getHooks = () => {
|
|
12
|
+
if (!_hooks) {
|
|
13
|
+
_hooks = require('../../index.js').hooks;
|
|
14
|
+
}
|
|
15
|
+
return _hooks;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
class ModelManager {
|
|
19
|
+
/**
|
|
20
|
+
* Constructor del gestor de modelos
|
|
21
|
+
* @param {Object} options - Opciones de configuración
|
|
22
|
+
*/
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.models = new Map(); // Almacena instancias de modelos
|
|
25
|
+
this.adapters = new Map(); // Almacena adaptadores disponibles
|
|
26
|
+
this.logger = options.logger || console;
|
|
27
|
+
|
|
28
|
+
// Inicializar hooks para el gestor de modelos
|
|
29
|
+
this.initializeHooks();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Inicializa los hooks específicos para el gestor de modelos
|
|
34
|
+
*/
|
|
35
|
+
initializeHooks() {
|
|
36
|
+
// Disparar hook cuando se crea una instancia del gestor de modelos
|
|
37
|
+
const hooks = getHooks();
|
|
38
|
+
if (hooks) {
|
|
39
|
+
hooks.doAction('model_manager_created', this);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Registra un adaptador de base de datos
|
|
45
|
+
* @param {string} name - Nombre del adaptador
|
|
46
|
+
* @param {Object} adapter - Instancia del adaptador
|
|
47
|
+
*/
|
|
48
|
+
registerAdapter(name, adapter) {
|
|
49
|
+
this.adapters.set(name, adapter);
|
|
50
|
+
|
|
51
|
+
// Disparar hook cuando se registra un adaptador
|
|
52
|
+
const hooks = getHooks();
|
|
53
|
+
if (hooks) {
|
|
54
|
+
hooks.doAction('adapter_registered', name, adapter);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Obtiene un adaptador registrado
|
|
60
|
+
* @param {string} name - Nombre del adaptador
|
|
61
|
+
* @returns {Object|null} - Instancia del adaptador o null
|
|
62
|
+
*/
|
|
63
|
+
getAdapter(name) {
|
|
64
|
+
return this.adapters.get(name) || null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Registra un modelo en el gestor
|
|
69
|
+
* @param {string} name - Nombre del modelo
|
|
70
|
+
* @param {ModelBase} model - Instancia del modelo
|
|
71
|
+
*/
|
|
72
|
+
registerModel(name, model) {
|
|
73
|
+
if (!(model instanceof ModelBase)) {
|
|
74
|
+
throw new Error('El modelo debe ser una instancia de ModelBase');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.models.set(name, model);
|
|
78
|
+
|
|
79
|
+
// Disparar hook cuando se registra un modelo
|
|
80
|
+
const hooks = getHooks();
|
|
81
|
+
if (hooks) {
|
|
82
|
+
hooks.doAction('model_registered', name, model);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Obtiene un modelo registrado
|
|
88
|
+
* @param {string} name - Nombre del modelo
|
|
89
|
+
* @returns {ModelBase|null} - Instancia del modelo o null
|
|
90
|
+
*/
|
|
91
|
+
getModel(name) {
|
|
92
|
+
return this.models.get(name) || null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Crea una instancia de modelo con un adaptador específico
|
|
97
|
+
* @param {string} modelName - Nombre del modelo
|
|
98
|
+
* @param {Object} options - Opciones para la creación del modelo
|
|
99
|
+
* @param {string} options.adapterName - Nombre del adaptador a usar
|
|
100
|
+
* @param {Object} options.modelOptions - Opciones específicas del modelo
|
|
101
|
+
* @returns {ModelBase} - Instancia del modelo creado
|
|
102
|
+
*/
|
|
103
|
+
createModel(modelName, options = {}) {
|
|
104
|
+
const { adapterName, modelOptions = {} } = options;
|
|
105
|
+
|
|
106
|
+
// Obtener el adaptador si se especificó
|
|
107
|
+
let adapter = null;
|
|
108
|
+
if (adapterName) {
|
|
109
|
+
adapter = this.getAdapter(adapterName);
|
|
110
|
+
if (!adapter) {
|
|
111
|
+
throw new Error(`Adaptador '${adapterName}' no encontrado para el modelo '${modelName}'`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Crear una clase modelo dinámicamente si no existe
|
|
116
|
+
if (!this.models.has(modelName)) {
|
|
117
|
+
// Creamos una clase modelo específica para este nombre
|
|
118
|
+
class DynamicModel extends ModelBase {
|
|
119
|
+
constructor(opts) {
|
|
120
|
+
super(opts);
|
|
121
|
+
this.modelName = modelName;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Instanciar el modelo con el adaptador
|
|
126
|
+
const modelInstance = new DynamicModel({
|
|
127
|
+
...modelOptions,
|
|
128
|
+
adapter: adapter,
|
|
129
|
+
logger: this.logger
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Registrar el modelo
|
|
133
|
+
this.registerModel(modelName, modelInstance);
|
|
134
|
+
|
|
135
|
+
return modelInstance;
|
|
136
|
+
} else {
|
|
137
|
+
// Si ya existe, actualizar el adaptador si se proporcionó
|
|
138
|
+
const existingModel = this.getModel(modelName);
|
|
139
|
+
if (adapter) {
|
|
140
|
+
existingModel.setAdapter(adapter);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return existingModel;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Asocia un modelo con un controlador para permitir comunicación bidireccional
|
|
149
|
+
* @param {string} modelName - Nombre del modelo
|
|
150
|
+
* @param {Object} controller - Instancia del controlador
|
|
151
|
+
*/
|
|
152
|
+
associateModelWithController(modelName, controller) {
|
|
153
|
+
const model = this.getModel(modelName);
|
|
154
|
+
if (!model) {
|
|
155
|
+
throw new Error(`Modelo '${modelName}' no encontrado`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Registrar el controlador en el modelo
|
|
159
|
+
model.registerController(controller);
|
|
160
|
+
|
|
161
|
+
// Disparar hook cuando se asocia un modelo con un controlador
|
|
162
|
+
const hooks = getHooks();
|
|
163
|
+
if (hooks) {
|
|
164
|
+
hooks.doAction('model_controller_associated', modelName, controller.constructor.name);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Obtiene todos los modelos registrados
|
|
170
|
+
* @returns {Map} - Mapa de modelos registrados
|
|
171
|
+
*/
|
|
172
|
+
getModels() {
|
|
173
|
+
return this.models;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Obtiene todos los adaptadores registrados
|
|
178
|
+
* @returns {Map} - Mapa de adaptadores registrados
|
|
179
|
+
*/
|
|
180
|
+
getAdapters() {
|
|
181
|
+
return this.adapters;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Inicializa todos los modelos con un adaptador por defecto
|
|
186
|
+
* @param {string} adapterName - Nombre del adaptador por defecto
|
|
187
|
+
*/
|
|
188
|
+
async initializeAllModels(adapterName) {
|
|
189
|
+
const adapter = this.getAdapter(adapterName);
|
|
190
|
+
if (!adapter) {
|
|
191
|
+
throw new Error(`Adaptador '${adapterName}' no encontrado`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Configurar el adaptador para todos los modelos que no lo tengan
|
|
195
|
+
for (const [modelName, model] of this.models) {
|
|
196
|
+
if (!model.adapter) {
|
|
197
|
+
model.setAdapter(adapter);
|
|
198
|
+
|
|
199
|
+
// Disparar hook cuando se inicializa un modelo con un adaptador
|
|
200
|
+
const hooks = getHooks();
|
|
201
|
+
if (hooks) {
|
|
202
|
+
hooks.doAction('model_initialized_with_adapter', modelName, adapterName);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Método para migrar todas las tablas de los modelos
|
|
210
|
+
* @param {string} adapterName - Nombre del adaptador a usar para la migración
|
|
211
|
+
*/
|
|
212
|
+
async migrateAll(adapterName) {
|
|
213
|
+
const adapter = this.getAdapter(adapterName);
|
|
214
|
+
if (!adapter) {
|
|
215
|
+
throw new Error(`Adaptador '${adapterName}' no encontrado`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Verificar si el adaptador tiene método de migración
|
|
219
|
+
if (typeof adapter.migrate !== 'function') {
|
|
220
|
+
throw new Error(`El adaptador '${adapterName}' no soporta migraciones`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Realizar migración para cada modelo
|
|
224
|
+
for (const [modelName, model] of this.models) {
|
|
225
|
+
try {
|
|
226
|
+
await adapter.migrate(model.tableName);
|
|
227
|
+
|
|
228
|
+
// Disparar hook cuando se migra una tabla
|
|
229
|
+
const hooks = getHooks();
|
|
230
|
+
if (hooks) {
|
|
231
|
+
hooks.doAction('model_table_migrated', modelName, model.tableName);
|
|
232
|
+
}
|
|
233
|
+
} catch (error) {
|
|
234
|
+
this.logger.error(`Error migrando tabla para el modelo '${modelName}':`, error.message);
|
|
235
|
+
|
|
236
|
+
// Disparar hook en caso de error de migración
|
|
237
|
+
const hooks = getHooks();
|
|
238
|
+
if (hooks) {
|
|
239
|
+
hooks.doAction('model_migration_error', modelName, error);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Método para limpiar (eliminar) todos los modelos del gestor
|
|
249
|
+
*/
|
|
250
|
+
clear() {
|
|
251
|
+
this.models.clear();
|
|
252
|
+
|
|
253
|
+
// Disparar hook cuando se limpia el gestor de modelos
|
|
254
|
+
const hooks = getHooks();
|
|
255
|
+
if (hooks) {
|
|
256
|
+
hooks.doAction('model_manager_cleared', this);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Serializa el estado del gestor de modelos a JSON
|
|
262
|
+
* @returns {Object} - Representación JSON del gestor de modelos
|
|
263
|
+
*/
|
|
264
|
+
toJSON() {
|
|
265
|
+
const modelsInfo = {};
|
|
266
|
+
for (const [name, model] of this.models) {
|
|
267
|
+
modelsInfo[name] = model.toJSON();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const adaptersInfo = {};
|
|
271
|
+
for (const [name, adapter] of this.adapters) {
|
|
272
|
+
adaptersInfo[name] = { registered: true };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
registeredModels: Object.keys(modelsInfo),
|
|
277
|
+
registeredAdapters: Object.keys(adaptersInfo),
|
|
278
|
+
modelsInfo,
|
|
279
|
+
adaptersInfo
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
module.exports = ModelManager;
|