jerkjs 2.2.0 → 2.3.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.
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Middleware de QueryBuilder para el framework JERK
3
+ * Implementación del componente middleware/queryBuilderMiddleware.js
4
+ * Proporciona acceso al QueryBuilder a través del sistema de hooks y middlewares
5
+ */
6
+
7
+ const QueryBuilder = require('./queryBuilder');
8
+
9
+ class QueryBuilderMiddleware {
10
+ /**
11
+ * Constructor del middleware
12
+ * @param {Object} options - Opciones de configuración
13
+ */
14
+ constructor(options = {}) {
15
+ this.options = options;
16
+ this.queryBuilders = new Map(); // Almacenar instancias de QueryBuilder por contexto
17
+ }
18
+
19
+ /**
20
+ * Middleware para inyectar el QueryBuilder en la solicitud
21
+ * @param {Object} req - Objeto de solicitud HTTP
22
+ * @param {Object} res - Objeto de respuesta HTTP
23
+ * @param {Function} next - Función para continuar con el siguiente middleware
24
+ */
25
+ injectQueryBuilder(req, res, next) {
26
+ // Si se ha almacenado un adaptador en la solicitud previamente, usarlo
27
+ if (req._dbAdapter) {
28
+ req.queryBuilder = (tableName = null) => {
29
+ const qb = new QueryBuilder(null, tableName);
30
+ qb.setAdapter(req._dbAdapter);
31
+ return qb;
32
+ };
33
+ } else {
34
+ // Si no hay adaptador en la solicitud, crear QueryBuilder sin adaptador
35
+ req.queryBuilder = (tableName = null) => new QueryBuilder(null, tableName);
36
+ }
37
+
38
+ // Agregar también un acceso directo al QueryBuilder en el objeto de solicitud
39
+ req.QueryBuilder = QueryBuilder;
40
+
41
+ next();
42
+ }
43
+
44
+ /**
45
+ * Middleware para registrar el QueryBuilder en el sistema de hooks
46
+ * @param {Object} req - Objeto de solicitud HTTP
47
+ * @param {Object} res - Objeto de respuesta HTTP
48
+ * @param {Function} next - Función para continuar con el siguiente middleware
49
+ */
50
+ registerWithHooks(req, res, next) {
51
+ // Disparar hook para permitir que otros componentes se registren con el QueryBuilder
52
+ const { hooks } = require('../../index.js');
53
+ if (hooks) {
54
+ hooks.doAction('query_builder_registered', {
55
+ request: req,
56
+ response: res,
57
+ queryBuilder: req.queryBuilder
58
+ });
59
+ }
60
+
61
+ next();
62
+ }
63
+
64
+ /**
65
+ * Middleware para configurar el QueryBuilder con un adaptador específico
66
+ * @param {Object} adapter - Adaptador de base de datos
67
+ * @returns {Function} - Middleware para configurar el QueryBuilder
68
+ */
69
+ configureWithAdapter(adapter) {
70
+ return (req, res, next) => {
71
+ // Guardar el adaptador en la solicitud para uso posterior
72
+ req._dbAdapter = adapter;
73
+
74
+ // Si queryBuilder ya existe, actualizarlo con el nuevo adaptador
75
+ if (req.queryBuilder) {
76
+ const originalQueryBuilder = req.queryBuilder;
77
+ req.queryBuilder = (tableName = null) => {
78
+ const qb = originalQueryBuilder(tableName);
79
+ qb.setAdapter(adapter);
80
+ return qb;
81
+ };
82
+ }
83
+
84
+ next();
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Middleware para inicializar el QueryBuilder con opciones específicas
90
+ * @param {Object} options - Opciones de inicialización
91
+ * @returns {Function} - Middleware para inicializar el QueryBuilder
92
+ */
93
+ initializeWithOptions(options = {}) {
94
+ return (req, res, next) => {
95
+ // Guardar las opciones en la solicitud para uso posterior
96
+ req.queryBuilderOptions = { ...this.options, ...options };
97
+
98
+ next();
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Middleware para registrar un QueryBuilder en el contexto de un modelo
104
+ * @param {Object} model - Instancia del modelo
105
+ * @returns {Function} - Middleware para registrar el QueryBuilder en el modelo
106
+ */
107
+ registerInModel(model) {
108
+ return (req, res, next) => {
109
+ // Asegurarse de que el modelo tenga acceso al QueryBuilder
110
+ if (model && typeof model.setAdapter === 'function' && model.adapter) {
111
+ // Crear una instancia de QueryBuilder con el adaptador del modelo
112
+ model.queryBuilder = (tableName = null) => new QueryBuilder(model.adapter, tableName);
113
+
114
+ // Registrar el modelo con el QueryBuilder
115
+ if (typeof model.registerQueryBuilder === 'function') {
116
+ model.registerQueryBuilder(model.queryBuilder);
117
+ }
118
+ }
119
+
120
+ next();
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Middleware para aplicar transformaciones a las consultas antes de ejecutarlas
126
+ * @param {Function} transformer - Función para transformar la consulta
127
+ * @returns {Function} - Middleware para transformar consultas
128
+ */
129
+ applyQueryTransformer(transformer) {
130
+ return (req, res, next) => {
131
+ if (typeof transformer !== 'function') {
132
+ next();
133
+ return;
134
+ }
135
+
136
+ // Guardar la función original de queryBuilder
137
+ const originalQueryBuilder = req.queryBuilder;
138
+
139
+ if (originalQueryBuilder) {
140
+ // Reemplazar la función queryBuilder con una versión que aplica el transformador
141
+ req.queryBuilder = (tableName = null) => {
142
+ const qb = originalQueryBuilder(tableName);
143
+
144
+ // Guardar la función original toSQL
145
+ const originalToSQL = qb.toSQL;
146
+
147
+ // Reemplazar toSQL para aplicar transformaciones
148
+ qb.toSQL = function() {
149
+ const result = originalToSQL.call(this);
150
+
151
+ // Aplicar el transformador si existe
152
+ if (transformer) {
153
+ const transformed = transformer(result, this);
154
+ return transformed || result;
155
+ }
156
+
157
+ return result;
158
+ };
159
+
160
+ return qb;
161
+ };
162
+ }
163
+
164
+ next();
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Middleware para habilitar el registro de consultas SQL
170
+ * @param {Object} logger - Instancia de logger
171
+ * @returns {Function} - Middleware para registrar consultas SQL
172
+ */
173
+ enableQueryLogging(logger = console) {
174
+ return (req, res, next) => {
175
+ // Guardar la función original de queryBuilder
176
+ const originalQueryBuilder = req.queryBuilder;
177
+
178
+ if (originalQueryBuilder) {
179
+ // Reemplazar la función queryBuilder con una versión que registra consultas
180
+ req.queryBuilder = (tableName = null) => {
181
+ const qb = originalQueryBuilder(tableName);
182
+
183
+ // Guardar las funciones originales de ejecución
184
+ const originalGet = qb.get;
185
+ const originalFirst = qb.first;
186
+ const originalInsert = qb.insert;
187
+ const originalUpdate = qb.update;
188
+ const originalDelete = qb.delete;
189
+
190
+ // Reemplazar get para registrar consultas
191
+ qb.get = async function() {
192
+ const { sql, params } = this.toSQL();
193
+ logger.info(`[QUERY_BUILDER] Ejecutando consulta: ${sql}`, { params });
194
+
195
+ try {
196
+ const result = await originalGet.call(this);
197
+ logger.info(`[QUERY_BUILDER] Consulta completada, resultados: ${result.length}`);
198
+ return result;
199
+ } catch (error) {
200
+ logger.error(`[QUERY_BUILDER] Error en consulta: ${error.message}`, { sql, params });
201
+ throw error;
202
+ }
203
+ };
204
+
205
+ // Reemplazar first para registrar consultas
206
+ qb.first = async function() {
207
+ const { sql, params } = this.toSQL();
208
+ logger.info(`[QUERY_BUILDER] Ejecutando consulta (first): ${sql}`, { params });
209
+
210
+ try {
211
+ const result = await originalFirst.call(this);
212
+ logger.info(`[QUERY_BUILDER] Consulta (first) completada`, { result: result !== null });
213
+ return result;
214
+ } catch (error) {
215
+ logger.error(`[QUERY_BUILDER] Error en consulta (first): ${error.message}`, { sql, params });
216
+ throw error;
217
+ }
218
+ };
219
+
220
+ // Reemplazar insert para registrar consultas
221
+ qb.insert = async function(data) {
222
+ const columns = Object.keys(data);
223
+ const placeholders = columns.map(() => '?').join(', ');
224
+ const sql = `INSERT INTO \`${this.tableName}\` (\`${columns.join('`, `')}\`) VALUES (${placeholders})`;
225
+
226
+ logger.info(`[QUERY_BUILDER] Ejecutando inserción: ${sql}`, { data });
227
+
228
+ try {
229
+ const result = await originalInsert.call(this, data);
230
+ logger.info(`[QUERY_BUILDER] Inserción completada`, { result });
231
+ return result;
232
+ } catch (error) {
233
+ logger.error(`[QUERY_BUILDER] Error en inserción: ${error.message}`, { sql, data });
234
+ throw error;
235
+ }
236
+ };
237
+
238
+ // Reemplazar update para registrar consultas
239
+ qb.update = async function(data) {
240
+ const updates = Object.keys(data).map(key => `\`${key}\` = ?`).join(', ');
241
+ const sql = `UPDATE \`${this.tableName}\` SET ${updates}`;
242
+
243
+ logger.info(`[QUERY_BUILDER] Ejecutando actualización: ${sql}`, { data });
244
+
245
+ try {
246
+ const result = await originalUpdate.call(this, data);
247
+ logger.info(`[QUERY_BUILDER] Actualización completada`, { affectedRows: result });
248
+ return result;
249
+ } catch (error) {
250
+ logger.error(`[QUERY_BUILDER] Error en actualización: ${error.message}`, { sql, data });
251
+ throw error;
252
+ }
253
+ };
254
+
255
+ // Reemplazar delete para registrar consultas
256
+ qb.delete = async function() {
257
+ const { sql, params } = this.toSQL();
258
+ logger.info(`[QUERY_BUILDER] Ejecutando eliminación: ${sql}`, { params });
259
+
260
+ try {
261
+ const result = await originalDelete.call(this);
262
+ logger.info(`[QUERY_BUILDER] Eliminación completada`, { affectedRows: result });
263
+ return result;
264
+ } catch (error) {
265
+ logger.error(`[QUERY_BUILDER] Error en eliminación: ${error.message}`, { sql, params });
266
+ throw error;
267
+ }
268
+ };
269
+
270
+ return qb;
271
+ };
272
+ }
273
+
274
+ next();
275
+ };
276
+ }
277
+
278
+ /**
279
+ * Middleware para aplicar reglas de seguridad a las consultas
280
+ * @param {Object} securityRules - Reglas de seguridad
281
+ * @returns {Function} - Middleware para aplicar reglas de seguridad
282
+ */
283
+ applySecurityRules(securityRules = {}) {
284
+ return (req, res, next) => {
285
+ // Guardar la función original de queryBuilder
286
+ const originalQueryBuilder = req.queryBuilder;
287
+
288
+ if (originalQueryBuilder) {
289
+ // Reemplazar la función queryBuilder con una versión que aplica reglas de seguridad
290
+ req.queryBuilder = (tableName = null) => {
291
+ const qb = originalQueryBuilder(tableName);
292
+
293
+ // Guardar la función original toSQL
294
+ const originalToSQL = qb.toSQL;
295
+
296
+ // Reemplazar toSQL para aplicar reglas de seguridad
297
+ qb.toSQL = function() {
298
+ // Aplicar reglas de seguridad antes de generar la consulta
299
+ if (securityRules.disallowSelectAll && this.selectFields.length === 0) {
300
+ throw new Error('SELECT * no está permitido por razones de seguridad');
301
+ }
302
+
303
+ if (securityRules.requiredWheres && this.whereConditions.length === 0 && this.tableName) {
304
+ // Verificar si la tabla requiere condiciones WHERE
305
+ if (Array.isArray(securityRules.requiredWheres) &&
306
+ securityRules.requiredWheres.includes(this.tableName)) {
307
+ throw new Error(`La tabla ${this.tableName} requiere condiciones WHERE por razones de seguridad`);
308
+ }
309
+ }
310
+
311
+ // Aplicar límites de seguridad
312
+ if (securityRules.maxLimit && this.limitValue > securityRules.maxLimit) {
313
+ this.limitValue = securityRules.maxLimit;
314
+ }
315
+
316
+ return originalToSQL.call(this);
317
+ };
318
+
319
+ return qb;
320
+ };
321
+ }
322
+
323
+ next();
324
+ };
325
+ }
326
+ }
327
+
328
+ // Exportar el middleware y el QueryBuilder
329
+ module.exports = {
330
+ QueryBuilderMiddleware,
331
+ QueryBuilder
332
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jerkjs",
3
- "version": "2.2.0",
4
- "description": "JERK Framework v2.2 - A comprehensive framework for building secure and scalable APIs with frontend support, sessions, and template engine with performance optimizations and complete MVC architecture with models",
3
+ "version": "2.3.0",
4
+ "description": "JERK Framework v2.3 - A comprehensive framework for building secure and scalable APIs with frontend support, sessions, template engine, QueryBuilder, and complete MVC architecture with models",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -0,0 +1,36 @@
1
+ #!/bin/bash
2
+
3
+ # Script to find a file's location using 'which' and show its real path with 'ls -ln'
4
+
5
+ if [ $# -eq 0 ]; then
6
+ echo "Usage: $0 <filename>"
7
+ echo "Example: $0 python"
8
+ exit 1
9
+ fi
10
+
11
+ filename="$1"
12
+
13
+ # Find the file using which
14
+ echo "Finding location of '$filename' using 'which':"
15
+ which_result=$(which "$filename")
16
+
17
+ if [ $? -eq 0 ]; then
18
+ echo "Found: $which_result"
19
+
20
+ # Get the directory containing the file
21
+ file_dir=$(dirname "$which_result")
22
+ file_basename=$(basename "$which_result")
23
+
24
+ # Change to the directory and use ls -ln to show the real path
25
+ echo ""
26
+ echo "Using 'ls -ln' to show detailed info:"
27
+ (cd "$file_dir" && ls -ln "$file_basename")
28
+
29
+ # If the file is a symlink, show the real path using readlink
30
+ echo ""
31
+ echo "Real path (using readlink -f):"
32
+ readlink -f "$which_result"
33
+ else
34
+ echo "File '$filename' not found in PATH"
35
+ exit 1
36
+ fi