jerkjs 2.3.1 → 2.4.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/README.md +2 -2
- package/index.js +6 -11
- package/jerk.webp +0 -0
- package/package.json +2 -1
- package/README_EN.md +0 -230
- package/README_PT.md +0 -230
- package/jerk-qbuilder/CHANGELOG.md +0 -71
- package/jerk-qbuilder/HOWTO.md +0 -325
- package/jerk-qbuilder/README.md +0 -52
- package/jerk.jpg +0 -0
- package/lib/query/MariaDBAdapter.js +0 -78
- package/lib/query/consoleAdapter.js +0 -184
- package/lib/query/queryBuilder.js +0 -953
- package/lib/query/queryBuilderHooks.js +0 -455
- package/lib/query/queryBuilderMiddleware.js +0 -332
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sistema de Hooks y Filters para QueryBuilder en el framework JERK
|
|
3
|
-
* Implementación del componente query/queryBuilderHooks.js
|
|
4
|
-
* Proporciona integración completa con el sistema de hooks y filters de JERK
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Función para inicializar los hooks del QueryBuilder
|
|
8
|
-
function initializeQueryBuilderHooks(hooks) {
|
|
9
|
-
if (!hooks) {
|
|
10
|
-
console.error('No se proporcionó un sistema de hooks para el QueryBuilder');
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Hook para antes de crear una instancia del QueryBuilder
|
|
15
|
-
hooks.addAction('query_builder_before_create', (config) => {
|
|
16
|
-
console.log('[HOOK] QueryBuilder antes de crear instancia', config);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
// Hook para después de crear una instancia del QueryBuilder
|
|
20
|
-
hooks.addAction('query_builder_after_create', (queryBuilder, config) => {
|
|
21
|
-
console.log('[HOOK] QueryBuilder instancia creada', { tableName: queryBuilder.tableName });
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// Filter para modificar la configuración antes de crear el QueryBuilder
|
|
25
|
-
hooks.addFilter('query_builder_config', (config) => {
|
|
26
|
-
// Añadir valores por defecto si no están presentes
|
|
27
|
-
return {
|
|
28
|
-
logQueries: false,
|
|
29
|
-
cacheResults: false,
|
|
30
|
-
...config
|
|
31
|
-
};
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// Hook para antes de ejecutar una consulta SELECT
|
|
35
|
-
hooks.addAction('query_builder_before_select', (sql, params, queryBuilder) => {
|
|
36
|
-
console.log('[HOOK] QueryBuilder antes de SELECT', { sql, params });
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// Hook para después de ejecutar una consulta SELECT
|
|
40
|
-
hooks.addAction('query_builder_after_select', (results, sql, params, queryBuilder) => {
|
|
41
|
-
console.log('[HOOK] QueryBuilder después de SELECT', { rowCount: results.length });
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// Hook para antes de ejecutar una inserción
|
|
45
|
-
hooks.addAction('query_builder_before_insert', (sql, data, queryBuilder) => {
|
|
46
|
-
console.log('[HOOK] QueryBuilder antes de INSERT', { sql, dataKeys: Object.keys(data) });
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// Hook para después de ejecutar una inserción
|
|
50
|
-
hooks.addAction('query_builder_after_insert', (result, sql, data, queryBuilder) => {
|
|
51
|
-
console.log('[HOOK] QueryBuilder después de INSERT', { insertId: result.insertId });
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// Hook para antes de ejecutar una actualización
|
|
55
|
-
hooks.addAction('query_builder_before_update', (sql, data, queryBuilder) => {
|
|
56
|
-
console.log('[HOOK] QueryBuilder antes de UPDATE', { sql, dataKeys: Object.keys(data) });
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// Hook para después de ejecutar una actualización
|
|
60
|
-
hooks.addAction('query_builder_after_update', (result, sql, data, queryBuilder) => {
|
|
61
|
-
console.log('[HOOK] QueryBuilder después de UPDATE', { affectedRows: result });
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// Hook para antes de ejecutar una eliminación
|
|
65
|
-
hooks.addAction('query_builder_before_delete', (sql, params, queryBuilder) => {
|
|
66
|
-
console.log('[HOOK] QueryBuilder antes de DELETE', { sql, params });
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// Hook para después de ejecutar una eliminación
|
|
70
|
-
hooks.addAction('query_builder_after_delete', (result, sql, params, queryBuilder) => {
|
|
71
|
-
console.log('[HOOK] QueryBuilder después de DELETE', { affectedRows: result });
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// Filter para modificar la consulta antes de ejecutarla
|
|
75
|
-
hooks.addFilter('query_builder_modify_query', (queryData, operation) => {
|
|
76
|
-
const { sql, params } = queryData;
|
|
77
|
-
|
|
78
|
-
// Ejemplo: añadir comentario con la operación realizada
|
|
79
|
-
const modifiedSql = `${sql} /* JERK QueryBuilder: ${operation} */`;
|
|
80
|
-
|
|
81
|
-
return { sql: modifiedSql, params };
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// Filter para transformar resultados antes de devolverlos
|
|
85
|
-
hooks.addFilter('query_builder_transform_results', (results, operation, queryBuilder) => {
|
|
86
|
-
// Añadir metadatos a los resultados si es una operación de selección
|
|
87
|
-
if (operation === 'select' && Array.isArray(results)) {
|
|
88
|
-
return {
|
|
89
|
-
data: results,
|
|
90
|
-
metadata: {
|
|
91
|
-
count: results.length,
|
|
92
|
-
operation: operation,
|
|
93
|
-
timestamp: new Date().toISOString()
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return results;
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Hook para cuando ocurre un error en una consulta
|
|
102
|
-
hooks.addAction('query_builder_error', (error, sql, params, queryBuilder, operation) => {
|
|
103
|
-
console.error('[HOOK] QueryBuilder error en operación', {
|
|
104
|
-
operation,
|
|
105
|
-
error: error.message,
|
|
106
|
-
sql,
|
|
107
|
-
params
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Hook para auditoría de consultas
|
|
112
|
-
hooks.addAction('query_builder_audit', (operation, sql, params, queryBuilder, executionTime) => {
|
|
113
|
-
console.log('[AUDIT] QueryBuilder operación auditada', {
|
|
114
|
-
operation,
|
|
115
|
-
executionTime: `${executionTime}ms`,
|
|
116
|
-
sqlPreview: sql.substring(0, 100) + (sql.length > 100 ? '...' : '')
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// Filter para aplicar reglas de seguridad a las consultas
|
|
121
|
-
hooks.addFilter('query_builder_security_check', (queryData, operation, queryBuilder) => {
|
|
122
|
-
const { sql, params } = queryData;
|
|
123
|
-
|
|
124
|
-
// Verificar si la consulta contiene patrones potencialmente peligrosos
|
|
125
|
-
const dangerousPatterns = ['DROP ', 'TRUNCATE ', 'ALTER ', '--', '/*'];
|
|
126
|
-
|
|
127
|
-
for (const pattern of dangerousPatterns) {
|
|
128
|
-
if (sql.toUpperCase().includes(pattern)) {
|
|
129
|
-
throw new Error(`Consulta bloqueada por seguridad: contiene '${pattern}'`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return queryData;
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Filter para aplicar límites de rendimiento
|
|
137
|
-
hooks.addFilter('query_builder_performance_limit', (queryData, operation, queryBuilder) => {
|
|
138
|
-
// Este filter podría ser usado para aplicar límites de rendimiento
|
|
139
|
-
// basados en la complejidad de la consulta
|
|
140
|
-
return queryData;
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Función para extender el QueryBuilder con funcionalidad de hooks
|
|
145
|
-
function extendQueryBuilderWithHooks(QueryBuilder, hooks) {
|
|
146
|
-
if (!QueryBuilder || !hooks) {
|
|
147
|
-
throw new Error('Se requieren QueryBuilder y hooks para extender funcionalidad');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Guardar métodos originales
|
|
151
|
-
const originalGet = QueryBuilder.prototype.get;
|
|
152
|
-
const originalFirst = QueryBuilder.prototype.first;
|
|
153
|
-
const originalInsert = QueryBuilder.prototype.insert;
|
|
154
|
-
const originalUpdate = QueryBuilder.prototype.update;
|
|
155
|
-
const originalDelete = QueryBuilder.prototype.delete;
|
|
156
|
-
|
|
157
|
-
// Extender el método get con hooks
|
|
158
|
-
QueryBuilder.prototype.get = async function() {
|
|
159
|
-
const startTime = Date.now();
|
|
160
|
-
|
|
161
|
-
// Disparar hook antes de la consulta
|
|
162
|
-
hooks.doAction('query_builder_before_select', this.toSQL().sql, this.toSQL().params, this);
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
// Modificar la consulta si es necesario usando filters
|
|
166
|
-
const originalSQLData = this.toSQL();
|
|
167
|
-
const modifiedSQLData = hooks.applyFilters(
|
|
168
|
-
'query_builder_modify_query',
|
|
169
|
-
originalSQLData,
|
|
170
|
-
'select'
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
// Ejecutar la consulta original
|
|
174
|
-
const results = await originalGet.call(this);
|
|
175
|
-
|
|
176
|
-
// Transformar resultados si es necesario
|
|
177
|
-
const transformedResults = hooks.applyFilters(
|
|
178
|
-
'query_builder_transform_results',
|
|
179
|
-
results,
|
|
180
|
-
'select',
|
|
181
|
-
this
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
// Calcular tiempo de ejecución
|
|
185
|
-
const executionTime = Date.now() - startTime;
|
|
186
|
-
|
|
187
|
-
// Disparar hook después de la consulta
|
|
188
|
-
hooks.doAction(
|
|
189
|
-
'query_builder_after_select',
|
|
190
|
-
results,
|
|
191
|
-
modifiedSQLData.sql,
|
|
192
|
-
modifiedSQLData.params,
|
|
193
|
-
this
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
// Disparar hook de auditoría
|
|
197
|
-
hooks.doAction(
|
|
198
|
-
'query_builder_audit',
|
|
199
|
-
'select',
|
|
200
|
-
modifiedSQLData.sql,
|
|
201
|
-
modifiedSQLData.params,
|
|
202
|
-
this,
|
|
203
|
-
executionTime
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
return transformedResults;
|
|
207
|
-
} catch (error) {
|
|
208
|
-
// Disparar hook de error
|
|
209
|
-
hooks.doAction(
|
|
210
|
-
'query_builder_error',
|
|
211
|
-
error,
|
|
212
|
-
this.toSQL().sql,
|
|
213
|
-
this.toSQL().params,
|
|
214
|
-
this,
|
|
215
|
-
'select'
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
throw error;
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
// Extender el método first con hooks
|
|
223
|
-
QueryBuilder.prototype.first = async function() {
|
|
224
|
-
const startTime = Date.now();
|
|
225
|
-
|
|
226
|
-
// Disparar hook antes de la consulta
|
|
227
|
-
hooks.doAction('query_builder_before_select', this.toSQL().sql, this.toSQL().params, this);
|
|
228
|
-
|
|
229
|
-
try {
|
|
230
|
-
// Modificar la consulta si es necesario usando filters
|
|
231
|
-
const originalSQLData = this.toSQL();
|
|
232
|
-
const modifiedSQLData = hooks.applyFilters(
|
|
233
|
-
'query_builder_modify_query',
|
|
234
|
-
originalSQLData,
|
|
235
|
-
'select_first'
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
// Ejecutar la consulta original
|
|
239
|
-
const result = await originalFirst.call(this);
|
|
240
|
-
|
|
241
|
-
// Transformar resultados si es necesario
|
|
242
|
-
const transformedResult = hooks.applyFilters(
|
|
243
|
-
'query_builder_transform_results',
|
|
244
|
-
result,
|
|
245
|
-
'select_first',
|
|
246
|
-
this
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
// Calcular tiempo de ejecución
|
|
250
|
-
const executionTime = Date.now() - startTime;
|
|
251
|
-
|
|
252
|
-
// Disparar hook después de la consulta
|
|
253
|
-
hooks.doAction(
|
|
254
|
-
'query_builder_after_select',
|
|
255
|
-
[result],
|
|
256
|
-
modifiedSQLData.sql,
|
|
257
|
-
modifiedSQLData.params,
|
|
258
|
-
this
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
// Disparar hook de auditoría
|
|
262
|
-
hooks.doAction(
|
|
263
|
-
'query_builder_audit',
|
|
264
|
-
'select_first',
|
|
265
|
-
modifiedSQLData.sql,
|
|
266
|
-
modifiedSQLData.params,
|
|
267
|
-
this,
|
|
268
|
-
executionTime
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
return transformedResult;
|
|
272
|
-
} catch (error) {
|
|
273
|
-
// Disparar hook de error
|
|
274
|
-
hooks.doAction(
|
|
275
|
-
'query_builder_error',
|
|
276
|
-
error,
|
|
277
|
-
this.toSQL().sql,
|
|
278
|
-
this.toSQL().params,
|
|
279
|
-
this,
|
|
280
|
-
'select_first'
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
throw error;
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
// Extender el método insert con hooks
|
|
288
|
-
QueryBuilder.prototype.insert = async function(data) {
|
|
289
|
-
const startTime = Date.now();
|
|
290
|
-
|
|
291
|
-
// Aplicar chequeo de seguridad
|
|
292
|
-
const queryData = {
|
|
293
|
-
sql: `INSERT INTO \`${this.tableName}\``,
|
|
294
|
-
params: Object.values(data)
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
const secureQueryData = hooks.applyFilters(
|
|
298
|
-
'query_builder_security_check',
|
|
299
|
-
queryData,
|
|
300
|
-
'insert',
|
|
301
|
-
this
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
// Disparar hook antes de la inserción
|
|
305
|
-
hooks.doAction('query_builder_before_insert', secureQueryData.sql, data, this);
|
|
306
|
-
|
|
307
|
-
try {
|
|
308
|
-
// Ejecutar la inserción original
|
|
309
|
-
const result = await originalInsert.call(this, data);
|
|
310
|
-
|
|
311
|
-
// Calcular tiempo de ejecución
|
|
312
|
-
const executionTime = Date.now() - startTime;
|
|
313
|
-
|
|
314
|
-
// Disparar hook después de la inserción
|
|
315
|
-
hooks.doAction('query_builder_after_insert', result, secureQueryData.sql, data, this);
|
|
316
|
-
|
|
317
|
-
// Disparar hook de auditoría
|
|
318
|
-
hooks.doAction(
|
|
319
|
-
'query_builder_audit',
|
|
320
|
-
'insert',
|
|
321
|
-
secureQueryData.sql,
|
|
322
|
-
secureQueryData.params,
|
|
323
|
-
this,
|
|
324
|
-
executionTime
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
return result;
|
|
328
|
-
} catch (error) {
|
|
329
|
-
// Disparar hook de error
|
|
330
|
-
hooks.doAction(
|
|
331
|
-
'query_builder_error',
|
|
332
|
-
error,
|
|
333
|
-
secureQueryData.sql,
|
|
334
|
-
secureQueryData.params,
|
|
335
|
-
this,
|
|
336
|
-
'insert'
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
throw error;
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
// Extender el método update con hooks
|
|
344
|
-
QueryBuilder.prototype.update = async function(data) {
|
|
345
|
-
const startTime = Date.now();
|
|
346
|
-
|
|
347
|
-
// Aplicar chequeo de seguridad
|
|
348
|
-
const queryData = {
|
|
349
|
-
sql: `UPDATE \`${this.tableName}\``,
|
|
350
|
-
params: [...Object.values(data), ...this.parameters]
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
const secureQueryData = hooks.applyFilters(
|
|
354
|
-
'query_builder_security_check',
|
|
355
|
-
queryData,
|
|
356
|
-
'update',
|
|
357
|
-
this
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
// Disparar hook antes de la actualización
|
|
361
|
-
hooks.doAction('query_builder_before_update', secureQueryData.sql, data, this);
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
// Ejecutar la actualización original
|
|
365
|
-
const result = await originalUpdate.call(this, data);
|
|
366
|
-
|
|
367
|
-
// Calcular tiempo de ejecución
|
|
368
|
-
const executionTime = Date.now() - startTime;
|
|
369
|
-
|
|
370
|
-
// Disparar hook después de la actualización
|
|
371
|
-
hooks.doAction('query_builder_after_update', result, secureQueryData.sql, data, this);
|
|
372
|
-
|
|
373
|
-
// Disparar hook de auditoría
|
|
374
|
-
hooks.doAction(
|
|
375
|
-
'query_builder_audit',
|
|
376
|
-
'update',
|
|
377
|
-
secureQueryData.sql,
|
|
378
|
-
secureQueryData.params,
|
|
379
|
-
this,
|
|
380
|
-
executionTime
|
|
381
|
-
);
|
|
382
|
-
|
|
383
|
-
return result;
|
|
384
|
-
} catch (error) {
|
|
385
|
-
// Disparar hook de error
|
|
386
|
-
hooks.doAction(
|
|
387
|
-
'query_builder_error',
|
|
388
|
-
error,
|
|
389
|
-
secureQueryData.sql,
|
|
390
|
-
secureQueryData.params,
|
|
391
|
-
this,
|
|
392
|
-
'update'
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
throw error;
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
// Extender el método delete con hooks
|
|
400
|
-
QueryBuilder.prototype.delete = async function() {
|
|
401
|
-
const startTime = Date.now();
|
|
402
|
-
|
|
403
|
-
// Aplicar chequeo de seguridad
|
|
404
|
-
const originalSQLData = this.toSQL();
|
|
405
|
-
const secureQueryData = hooks.applyFilters(
|
|
406
|
-
'query_builder_security_check',
|
|
407
|
-
originalSQLData,
|
|
408
|
-
'delete',
|
|
409
|
-
this
|
|
410
|
-
);
|
|
411
|
-
|
|
412
|
-
// Disparar hook antes de la eliminación
|
|
413
|
-
hooks.doAction('query_builder_before_delete', secureQueryData.sql, secureQueryData.params, this);
|
|
414
|
-
|
|
415
|
-
try {
|
|
416
|
-
// Ejecutar la eliminación original
|
|
417
|
-
const result = await originalDelete.call(this);
|
|
418
|
-
|
|
419
|
-
// Calcular tiempo de ejecución
|
|
420
|
-
const executionTime = Date.now() - startTime;
|
|
421
|
-
|
|
422
|
-
// Disparar hook después de la eliminación
|
|
423
|
-
hooks.doAction('query_builder_after_delete', result, secureQueryData.sql, secureQueryData.params, this);
|
|
424
|
-
|
|
425
|
-
// Disparar hook de auditoría
|
|
426
|
-
hooks.doAction(
|
|
427
|
-
'query_builder_audit',
|
|
428
|
-
'delete',
|
|
429
|
-
secureQueryData.sql,
|
|
430
|
-
secureQueryData.params,
|
|
431
|
-
this,
|
|
432
|
-
executionTime
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
return result;
|
|
436
|
-
} catch (error) {
|
|
437
|
-
// Disparar hook de error
|
|
438
|
-
hooks.doAction(
|
|
439
|
-
'query_builder_error',
|
|
440
|
-
error,
|
|
441
|
-
secureQueryData.sql,
|
|
442
|
-
secureQueryData.params,
|
|
443
|
-
this,
|
|
444
|
-
'delete'
|
|
445
|
-
);
|
|
446
|
-
|
|
447
|
-
throw error;
|
|
448
|
-
}
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
module.exports = {
|
|
453
|
-
initializeQueryBuilderHooks,
|
|
454
|
-
extendQueryBuilderWithHooks
|
|
455
|
-
};
|