jerkjs 2.1.6 → 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 +36 -0
- package/README.md +202 -5
- package/index.js +29 -4
- package/lib/core/server.js +328 -27
- package/lib/loader/routeLoader.js +148 -117
- package/lib/middleware/compressor.js +87 -18
- 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/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,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptador de MariaDB para modelos en el framework JERK
|
|
3
|
+
* Implementación del componente MVC MariaDBAdapter.js
|
|
4
|
+
* Proporciona acceso a bases de datos MariaDB/MySQL
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const GenericAdapter = require('../../lib/mvc/GenericAdapter');
|
|
8
|
+
const mariadb = require('mariadb');
|
|
9
|
+
|
|
10
|
+
class MariaDBAdapter extends GenericAdapter {
|
|
11
|
+
/**
|
|
12
|
+
* Constructor del adaptador MariaDB
|
|
13
|
+
* @param {Object} options - Opciones de configuración
|
|
14
|
+
*/
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
super({ ...options, type: 'mariadb' });
|
|
17
|
+
|
|
18
|
+
// Configuración de la conexión a la base de datos
|
|
19
|
+
this.dbConfig = {
|
|
20
|
+
host: options.host || 'localhost',
|
|
21
|
+
user: options.user || 'root',
|
|
22
|
+
password: options.password || '',
|
|
23
|
+
database: options.database || 'otrack_db',
|
|
24
|
+
waitForConnections: options.waitForConnections !== false,
|
|
25
|
+
connectionLimit: options.connectionLimit || 10,
|
|
26
|
+
queueLimit: options.queueLimit || 0,
|
|
27
|
+
...options
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Crear pool de conexiones
|
|
31
|
+
this.pool = mariadb.createPool(this.dbConfig);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Método para crear un registro
|
|
36
|
+
* @param {string} tableName - Nombre de la tabla
|
|
37
|
+
* @param {Object} data - Datos a crear
|
|
38
|
+
* @returns {Promise<Object>} - Promesa con el resultado
|
|
39
|
+
*/
|
|
40
|
+
async create(tableName, data) {
|
|
41
|
+
const columns = Object.keys(data).join(', ');
|
|
42
|
+
const placeholders = Object.keys(data).map(() => '?').join(', ');
|
|
43
|
+
const query = `INSERT INTO ${tableName} (${columns}) VALUES (${placeholders})`;
|
|
44
|
+
const params = Object.values(data);
|
|
45
|
+
|
|
46
|
+
const connection = await this.pool.getConnection();
|
|
47
|
+
try {
|
|
48
|
+
const result = await connection.query(query, params);
|
|
49
|
+
|
|
50
|
+
// Obtener el registro recién insertado
|
|
51
|
+
const insertedRecord = await connection.query(
|
|
52
|
+
`SELECT * FROM ${tableName} WHERE id = ?`,
|
|
53
|
+
[result.insertId]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return insertedRecord[0] || { id: result.insertId, ...data };
|
|
57
|
+
} finally {
|
|
58
|
+
connection.release();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Método para encontrar registros
|
|
64
|
+
* @param {string} tableName - Nombre de la tabla
|
|
65
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
66
|
+
* @param {Object} options - Opciones adicionales
|
|
67
|
+
* @returns {Promise<Array>} - Promesa con los resultados
|
|
68
|
+
*/
|
|
69
|
+
async find(tableName, conditions, options = {}) {
|
|
70
|
+
let query = `SELECT * FROM ${tableName}`;
|
|
71
|
+
const params = [];
|
|
72
|
+
|
|
73
|
+
// Añadir condiciones WHERE
|
|
74
|
+
if (conditions && Object.keys(conditions).length > 0) {
|
|
75
|
+
const conditionsList = [];
|
|
76
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
77
|
+
if (value !== undefined && value !== null) {
|
|
78
|
+
conditionsList.push(`${key} = ?`);
|
|
79
|
+
params.push(value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (conditionsList.length > 0) {
|
|
83
|
+
query += ' WHERE ' + conditionsList.join(' AND ');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Añadir ordenamiento
|
|
88
|
+
if (options.orderBy) {
|
|
89
|
+
query += ` ORDER BY ${options.orderBy}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Añadir paginación
|
|
93
|
+
if (options.limit) {
|
|
94
|
+
query += ` LIMIT `;
|
|
95
|
+
if (options.offset) {
|
|
96
|
+
query += `${options.offset}, `;
|
|
97
|
+
}
|
|
98
|
+
query += options.limit;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const connection = await this.pool.getConnection();
|
|
102
|
+
try {
|
|
103
|
+
return await connection.query(query, params);
|
|
104
|
+
} finally {
|
|
105
|
+
connection.release();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Método para encontrar un solo registro
|
|
111
|
+
* @param {string} tableName - Nombre de la tabla
|
|
112
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
113
|
+
* @param {Object} options - Opciones adicionales
|
|
114
|
+
* @returns {Promise<Object|null>} - Promesa con el resultado o null
|
|
115
|
+
*/
|
|
116
|
+
async findOne(tableName, conditions, options = {}) {
|
|
117
|
+
const results = await this.find(tableName, conditions, { ...options, limit: 1 });
|
|
118
|
+
return results.length > 0 ? results[0] : null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Método para actualizar registros
|
|
123
|
+
* @param {string} tableName - Nombre de la tabla
|
|
124
|
+
* @param {Object} conditions - Condiciones para seleccionar registros
|
|
125
|
+
* @param {Object} data - Datos a actualizar
|
|
126
|
+
* @returns {Promise<number>} - Promesa con el número de registros afectados
|
|
127
|
+
*/
|
|
128
|
+
async update(tableName, conditions, data) {
|
|
129
|
+
const dataEntries = Object.entries(data);
|
|
130
|
+
if (dataEntries.length === 0) {
|
|
131
|
+
return 0; // No hay nada que actualizar
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Construir la parte SET de la consulta
|
|
135
|
+
const setClause = dataEntries.map(([key]) => `${key} = ?`).join(', ');
|
|
136
|
+
const params = dataEntries.map(([, value]) => value);
|
|
137
|
+
|
|
138
|
+
// Construir la parte WHERE de la consulta
|
|
139
|
+
const whereClause = [];
|
|
140
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
141
|
+
if (value !== undefined && value !== null) {
|
|
142
|
+
whereClause.push(`${key} = ?`);
|
|
143
|
+
params.push(value);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let query = `UPDATE ${tableName} SET ${setClause}`;
|
|
148
|
+
if (whereClause.length > 0) {
|
|
149
|
+
query += ` WHERE ${whereClause.join(' AND ')}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const connection = await this.pool.getConnection();
|
|
153
|
+
try {
|
|
154
|
+
const result = await connection.query(query, params);
|
|
155
|
+
return result.affectedRows;
|
|
156
|
+
} finally {
|
|
157
|
+
connection.release();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Método para eliminar registros
|
|
163
|
+
* @param {string} tableName - Nombre de la tabla
|
|
164
|
+
* @param {Object} conditions - Condiciones para seleccionar registros
|
|
165
|
+
* @returns {Promise<number>} - Promesa con el número de registros eliminados
|
|
166
|
+
*/
|
|
167
|
+
async delete(tableName, conditions) {
|
|
168
|
+
let query = `DELETE FROM ${tableName}`;
|
|
169
|
+
const params = [];
|
|
170
|
+
|
|
171
|
+
if (conditions && Object.keys(conditions).length > 0) {
|
|
172
|
+
const conditionsList = [];
|
|
173
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
174
|
+
if (value !== undefined && value !== null) {
|
|
175
|
+
conditionsList.push(`${key} = ?`);
|
|
176
|
+
params.push(value);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (conditionsList.length > 0) {
|
|
180
|
+
query += ' WHERE ' + conditionsList.join(' AND ');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const connection = await this.pool.getConnection();
|
|
185
|
+
try {
|
|
186
|
+
const result = await connection.query(query, params);
|
|
187
|
+
return result.affectedRows;
|
|
188
|
+
} finally {
|
|
189
|
+
connection.release();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Método para contar registros
|
|
195
|
+
* @param {string} tableName - Nombre de la tabla
|
|
196
|
+
* @param {Object} conditions - Condiciones de conteo
|
|
197
|
+
* @returns {Promise<number>} - Promesa con el número de registros
|
|
198
|
+
*/
|
|
199
|
+
async count(tableName, conditions) {
|
|
200
|
+
let query = `SELECT COUNT(*) as total FROM ${tableName}`;
|
|
201
|
+
const params = [];
|
|
202
|
+
|
|
203
|
+
if (conditions && Object.keys(conditions).length > 0) {
|
|
204
|
+
const conditionsList = [];
|
|
205
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
206
|
+
if (value !== undefined && value !== null) {
|
|
207
|
+
conditionsList.push(`${key} = ?`);
|
|
208
|
+
params.push(value);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (conditionsList.length > 0) {
|
|
212
|
+
query += ' WHERE ' + conditionsList.join(' AND ');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const connection = await this.pool.getConnection();
|
|
217
|
+
try {
|
|
218
|
+
const result = await connection.query(query, params);
|
|
219
|
+
return result[0] ? result[0].total : 0;
|
|
220
|
+
} finally {
|
|
221
|
+
connection.release();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Método para ejecutar consultas personalizadas
|
|
227
|
+
* @param {string} query - Consulta personalizada
|
|
228
|
+
* @param {Array} params - Parámetros de la consulta
|
|
229
|
+
* @returns {Promise<any>} - Promesa con el resultado
|
|
230
|
+
*/
|
|
231
|
+
async query(query, params = []) {
|
|
232
|
+
const connection = await this.pool.getConnection();
|
|
233
|
+
try {
|
|
234
|
+
return await connection.query(query, params);
|
|
235
|
+
} finally {
|
|
236
|
+
connection.release();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Método para inicializar el adaptador
|
|
242
|
+
* @returns {Promise<void>}
|
|
243
|
+
*/
|
|
244
|
+
async initialize() {
|
|
245
|
+
try {
|
|
246
|
+
// Probar la conexión
|
|
247
|
+
const connection = await this.pool.getConnection();
|
|
248
|
+
connection.release();
|
|
249
|
+
this.logger.info('Conexión a MariaDB establecida correctamente');
|
|
250
|
+
} catch (error) {
|
|
251
|
+
this.logger.error('Error al inicializar la conexión a MariaDB:', error.message);
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Método para cerrar la conexión
|
|
258
|
+
* @returns {Promise<void>}
|
|
259
|
+
*/
|
|
260
|
+
async close() {
|
|
261
|
+
try {
|
|
262
|
+
await this.pool.end();
|
|
263
|
+
this.logger.info('Conexión a MariaDB cerrada correctamente');
|
|
264
|
+
} catch (error) {
|
|
265
|
+
this.logger.error('Error al cerrar la conexión a MariaDB:', error.message);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Método para verificar si el adaptador está conectado
|
|
271
|
+
* @returns {Promise<boolean>} - Verdadero si está conectado
|
|
272
|
+
*/
|
|
273
|
+
async isConnected() {
|
|
274
|
+
try {
|
|
275
|
+
const connection = await this.pool.getConnection();
|
|
276
|
+
connection.release();
|
|
277
|
+
return true;
|
|
278
|
+
} catch (error) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Método para migrar una tabla
|
|
285
|
+
* @param {string} tableName - Nombre de la tabla a migrar
|
|
286
|
+
* @returns {Promise<void>}
|
|
287
|
+
*/
|
|
288
|
+
async migrate(tableName) {
|
|
289
|
+
// En una implementación completa, aquí se ejecutarían scripts de migración
|
|
290
|
+
// Por ahora, simplemente verificamos si la tabla existe
|
|
291
|
+
try {
|
|
292
|
+
await this.query(`SELECT 1 FROM ${tableName} LIMIT 1`);
|
|
293
|
+
this.logger.info(`Tabla ${tableName} ya existe`);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
this.logger.warn(`Tabla ${tableName} no existe o no se puede acceder:`, error.message);
|
|
296
|
+
// Aquí se podría crear la tabla si no existe
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Serializa el adaptador a JSON
|
|
302
|
+
* @returns {Object} - Representación JSON del adaptador
|
|
303
|
+
*/
|
|
304
|
+
toJSON() {
|
|
305
|
+
const baseJson = super.toJSON();
|
|
306
|
+
return {
|
|
307
|
+
...baseJson,
|
|
308
|
+
host: this.dbConfig.host,
|
|
309
|
+
database: this.dbConfig.database,
|
|
310
|
+
connected: this.isConnected()
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
module.exports = MariaDBAdapter;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptador de almacenamiento en memoria para modelos en el framework JERK
|
|
3
|
+
* Implementación del componente MVC MemoryAdapter.js
|
|
4
|
+
* Proporciona almacenamiento en memoria para modelos
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const GenericAdapter = require('./GenericAdapter');
|
|
8
|
+
|
|
9
|
+
class MemoryAdapter extends GenericAdapter {
|
|
10
|
+
/**
|
|
11
|
+
* Constructor del adaptador en memoria
|
|
12
|
+
* @param {Object} options - Opciones de configuración
|
|
13
|
+
*/
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
super({ ...options, type: 'memory' });
|
|
16
|
+
|
|
17
|
+
// Almacenamiento en memoria
|
|
18
|
+
this.storage = new Map();
|
|
19
|
+
this.autoIncrementIds = new Map();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Método para crear un registro
|
|
24
|
+
* @param {string} tableName - Nombre de la tabla
|
|
25
|
+
* @param {Object} data - Datos a crear
|
|
26
|
+
* @returns {Promise<Object>} - Promesa con el resultado
|
|
27
|
+
*/
|
|
28
|
+
async create(tableName, data) {
|
|
29
|
+
// Inicializar la tabla si no existe
|
|
30
|
+
if (!this.storage.has(tableName)) {
|
|
31
|
+
this.storage.set(tableName, []);
|
|
32
|
+
this.autoIncrementIds.set(tableName, 1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const table = this.storage.get(tableName);
|
|
36
|
+
const newId = this.autoIncrementIds.get(tableName);
|
|
37
|
+
|
|
38
|
+
// Agregar ID al registro
|
|
39
|
+
const record = { id: newId, ...data, createdAt: new Date(), updatedAt: new Date() };
|
|
40
|
+
|
|
41
|
+
// Actualizar ID autoincrementable
|
|
42
|
+
this.autoIncrementIds.set(tableName, newId + 1);
|
|
43
|
+
|
|
44
|
+
// Agregar registro a la tabla
|
|
45
|
+
table.push(record);
|
|
46
|
+
|
|
47
|
+
return { ...record };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Método para encontrar registros
|
|
52
|
+
* @param {string} tableName - Nombre de la tabla
|
|
53
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
54
|
+
* @param {Object} options - Opciones adicionales
|
|
55
|
+
* @returns {Promise<Array>} - Promesa con los resultados
|
|
56
|
+
*/
|
|
57
|
+
async find(tableName, conditions, options = {}) {
|
|
58
|
+
if (!this.storage.has(tableName)) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let records = [...this.storage.get(tableName)];
|
|
63
|
+
|
|
64
|
+
// Filtrar por condiciones
|
|
65
|
+
records = this.filterRecords(records, conditions);
|
|
66
|
+
|
|
67
|
+
// Aplicar opciones
|
|
68
|
+
if (options.orderBy) {
|
|
69
|
+
records = this.sortRecords(records, options.orderBy);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (options.limit) {
|
|
73
|
+
const offset = options.offset || 0;
|
|
74
|
+
records = records.slice(offset, offset + options.limit);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return records;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Método para encontrar un solo registro
|
|
82
|
+
* @param {string} tableName - Nombre de la tabla
|
|
83
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
84
|
+
* @param {Object} options - Opciones adicionales
|
|
85
|
+
* @returns {Promise<Object|null>} - Promesa con el resultado o null
|
|
86
|
+
*/
|
|
87
|
+
async findOne(tableName, conditions, options = {}) {
|
|
88
|
+
const records = await this.find(tableName, conditions, { ...options, limit: 1 });
|
|
89
|
+
return records.length > 0 ? records[0] : null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Método para actualizar registros
|
|
94
|
+
* @param {string} tableName - Nombre de la tabla
|
|
95
|
+
* @param {Object} conditions - Condiciones para seleccionar registros
|
|
96
|
+
* @param {Object} data - Datos a actualizar
|
|
97
|
+
* @returns {Promise<number>} - Promesa con el número de registros afectados
|
|
98
|
+
*/
|
|
99
|
+
async update(tableName, conditions, data) {
|
|
100
|
+
if (!this.storage.has(tableName)) {
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const table = this.storage.get(tableName);
|
|
105
|
+
let updatedCount = 0;
|
|
106
|
+
|
|
107
|
+
// Actualizar la marca de tiempo
|
|
108
|
+
const now = new Date();
|
|
109
|
+
data.updatedAt = now;
|
|
110
|
+
|
|
111
|
+
for (let i = 0; i < table.length; i++) {
|
|
112
|
+
if (this.matchesConditions(table[i], conditions)) {
|
|
113
|
+
// Actualizar solo las propiedades proporcionadas
|
|
114
|
+
table[i] = { ...table[i], ...data };
|
|
115
|
+
updatedCount++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return updatedCount;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Método para eliminar registros
|
|
124
|
+
* @param {string} tableName - Nombre de la tabla
|
|
125
|
+
* @param {Object} conditions - Condiciones para seleccionar registros
|
|
126
|
+
* @returns {Promise<number>} - Promesa con el número de registros eliminados
|
|
127
|
+
*/
|
|
128
|
+
async delete(tableName, conditions) {
|
|
129
|
+
if (!this.storage.has(tableName)) {
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const table = this.storage.get(tableName);
|
|
134
|
+
const initialLength = table.length;
|
|
135
|
+
|
|
136
|
+
// Filtrar los registros que no coinciden con las condiciones
|
|
137
|
+
const filteredTable = table.filter(record => !this.matchesConditions(record, conditions));
|
|
138
|
+
|
|
139
|
+
// Actualizar la tabla
|
|
140
|
+
this.storage.set(tableName, filteredTable);
|
|
141
|
+
|
|
142
|
+
return initialLength - filteredTable.length;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Método para contar registros
|
|
147
|
+
* @param {string} tableName - Nombre de la tabla
|
|
148
|
+
* @param {Object} conditions - Condiciones de conteo
|
|
149
|
+
* @returns {Promise<number>} - Promesa con el número de registros
|
|
150
|
+
*/
|
|
151
|
+
async count(tableName, conditions) {
|
|
152
|
+
if (!this.storage.has(tableName)) {
|
|
153
|
+
return 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const records = this.storage.get(tableName);
|
|
157
|
+
const filteredRecords = this.filterRecords(records, conditions);
|
|
158
|
+
return filteredRecords.length;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Método para ejecutar consultas personalizadas
|
|
163
|
+
* @param {string} query - Consulta personalizada (en este caso, una función)
|
|
164
|
+
* @param {Array} params - Parámetros de la consulta
|
|
165
|
+
* @returns {Promise<any>} - Promesa con el resultado
|
|
166
|
+
*/
|
|
167
|
+
async query(query, params = []) {
|
|
168
|
+
// En el adaptador de memoria, tratamos la 'consulta' como una función
|
|
169
|
+
if (typeof query === 'function') {
|
|
170
|
+
return query(this.storage, ...params);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
throw new Error('En el adaptador de memoria, la consulta debe ser una función');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Método para filtrar registros según condiciones
|
|
178
|
+
* @param {Array} records - Array de registros
|
|
179
|
+
* @param {Object} conditions - Condiciones de filtrado
|
|
180
|
+
* @returns {Array} - Registros filtrados
|
|
181
|
+
*/
|
|
182
|
+
filterRecords(records, conditions) {
|
|
183
|
+
if (!conditions || Object.keys(conditions).length === 0) {
|
|
184
|
+
return records;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return records.filter(record => this.matchesConditions(record, conditions));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Método para verificar si un registro coincide con las condiciones
|
|
192
|
+
* @param {Object} record - Registro a verificar
|
|
193
|
+
* @param {Object} conditions - Condiciones a verificar
|
|
194
|
+
* @returns {boolean} - Verdadero si coincide
|
|
195
|
+
*/
|
|
196
|
+
matchesConditions(record, conditions) {
|
|
197
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
198
|
+
if (record[key] !== value) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Método para ordenar registros
|
|
207
|
+
* @param {Array} records - Array de registros
|
|
208
|
+
* @param {string} orderBy - Criterio de ordenamiento (ej: 'field ASC' o 'field DESC')
|
|
209
|
+
* @returns {Array} - Registros ordenados
|
|
210
|
+
*/
|
|
211
|
+
sortRecords(records, orderBy) {
|
|
212
|
+
if (!orderBy) return records;
|
|
213
|
+
|
|
214
|
+
const [field, direction] = orderBy.split(' ');
|
|
215
|
+
const isAscending = (direction || 'ASC').toUpperCase() === 'ASC';
|
|
216
|
+
|
|
217
|
+
return records.sort((a, b) => {
|
|
218
|
+
const valueA = a[field];
|
|
219
|
+
const valueB = b[field];
|
|
220
|
+
|
|
221
|
+
if (valueA < valueB) {
|
|
222
|
+
return isAscending ? -1 : 1;
|
|
223
|
+
} else if (valueA > valueB) {
|
|
224
|
+
return isAscending ? 1 : -1;
|
|
225
|
+
} else {
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Método para limpiar el almacenamiento
|
|
233
|
+
*/
|
|
234
|
+
clear() {
|
|
235
|
+
this.storage.clear();
|
|
236
|
+
this.autoIncrementIds.clear();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Método para migrar una tabla
|
|
241
|
+
* @param {string} tableName - Nombre de la tabla a migrar
|
|
242
|
+
* @returns {Promise<void>}
|
|
243
|
+
*/
|
|
244
|
+
async migrate(tableName) {
|
|
245
|
+
// En el adaptador de memoria, la migración simplemente inicializa la tabla
|
|
246
|
+
if (!this.storage.has(tableName)) {
|
|
247
|
+
this.storage.set(tableName, []);
|
|
248
|
+
this.autoIncrementIds.set(tableName, 1);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Serializa el adaptador a JSON
|
|
254
|
+
* @returns {Object} - Representación JSON del adaptador
|
|
255
|
+
*/
|
|
256
|
+
toJSON() {
|
|
257
|
+
const baseJson = super.toJSON();
|
|
258
|
+
return {
|
|
259
|
+
...baseJson,
|
|
260
|
+
tables: Array.from(this.storage.keys()),
|
|
261
|
+
recordCounts: Array.from(this.storage.entries()).reduce((acc, [tableName, records]) => {
|
|
262
|
+
acc[tableName] = records.length;
|
|
263
|
+
return acc;
|
|
264
|
+
}, {})
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
module.exports = MemoryAdapter;
|