@dbcube/query-builder 5.1.6 → 5.2.2

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/dist/index.cjs CHANGED
@@ -39,6 +39,7 @@ module.exports = __toCommonJS(index_exports);
39
39
  // src/lib/Database.ts
40
40
  var import_fs = __toESM(require("fs"));
41
41
  var import_path2 = __toESM(require("path"));
42
+ var import_crypto = require("crypto");
42
43
  var import_core2 = require("@dbcube/core");
43
44
 
44
45
  // src/lib/Trigger.ts
@@ -75,11 +76,17 @@ var Trigger = class {
75
76
  const pathFile = import_path.default.resolve(process.cwd(), "dbcube", "triggers", `${trigger.database_ref}_${trigger.table_ref}_${trigger.type}.js`);
76
77
  const requireUrl = typeof __filename !== "undefined" ? __filename : process.cwd();
77
78
  const require2 = (0, import_module.createRequire)(requireUrl);
78
- delete require2.cache[require2.resolve(pathFile)];
79
- const triggerModule = require2(pathFile);
80
- const dataProcess = triggerModule.default || triggerModule;
81
- await dataProcess({ db: this.instance, oldData: row, newData: row });
82
- interceptor.restore();
79
+ try {
80
+ delete require2.cache[require2.resolve(pathFile)];
81
+ } catch {
82
+ }
83
+ try {
84
+ const triggerModule = require2(pathFile);
85
+ const dataProcess = triggerModule.default || triggerModule;
86
+ await dataProcess({ db: this.instance, oldData: row, newData: row });
87
+ } finally {
88
+ interceptor.restore();
89
+ }
83
90
  return interceptor;
84
91
  }
85
92
  return null;
@@ -143,7 +150,10 @@ var Database = class _Database {
143
150
  } catch (error) {
144
151
  try {
145
152
  await this.engine.rollbackTransaction(txId);
146
- } catch {
153
+ } catch (rollbackError) {
154
+ console.error(
155
+ `[dbcube] Rollback of transaction ${txId} failed: ${rollbackError?.message ?? rollbackError}`
156
+ );
147
157
  }
148
158
  throw error;
149
159
  }
@@ -168,10 +178,9 @@ var Database = class _Database {
168
178
  if (response.status != 200) {
169
179
  returnFormattedError(response.status, response.message);
170
180
  }
171
- return response.data;
172
181
  }
173
182
  async disconnect() {
174
- return this.engine.run("query_engine", [
183
+ await this.engine.run("query_engine", [
175
184
  "--action",
176
185
  "disconnect"
177
186
  ]);
@@ -232,8 +241,12 @@ var Table = class _Table {
232
241
  triggers;
233
242
  txId = null;
234
243
  relations = [];
235
- constructor(instance, databaseName, tableName, engine = null, computedFields = [], triggers = [], txId = null) {
244
+ /** Builders de grupo mutan en sitio (ver clone()) */
245
+ _mutable = false;
246
+ instance;
247
+ constructor(instance, databaseName, tableName, engine, computedFields = [], triggers = [], txId = null) {
236
248
  this.engine = engine;
249
+ this.instance = instance;
237
250
  this.computedFields = computedFields;
238
251
  this.triggers = triggers;
239
252
  this.trigger = new Trigger(instance, databaseName, triggers);
@@ -310,7 +323,8 @@ var Table = class _Table {
310
323
  */
311
324
  whereGroup(callback) {
312
325
  const clone = this.clone();
313
- const groupQuery = new _Table(this, clone.dml.database, clone.dml.table, clone.engine);
326
+ const groupQuery = new _Table(this.instance, clone.dml.database, clone.dml.table, clone.engine);
327
+ groupQuery._mutable = true;
314
328
  callback(groupQuery);
315
329
  clone.dml.where.push({
316
330
  type: clone.nextType,
@@ -572,6 +586,7 @@ var Table = class _Table {
572
586
  column,
573
587
  alias: "count"
574
588
  };
589
+ clone.dml.orderBy = [];
575
590
  clone.dml.columns = [`COUNT(${column}) AS count`];
576
591
  clone.dml.data = null;
577
592
  clone.dml.limit = null;
@@ -605,6 +620,7 @@ var Table = class _Table {
605
620
  column,
606
621
  alias: "sum"
607
622
  };
623
+ clone.dml.orderBy = [];
608
624
  clone.dml.columns = [`SUM(${column}) AS sum`];
609
625
  clone.dml.data = null;
610
626
  clone.dml.limit = 1;
@@ -637,6 +653,7 @@ var Table = class _Table {
637
653
  column,
638
654
  alias: "avg"
639
655
  };
656
+ clone.dml.orderBy = [];
640
657
  clone.dml.columns = [`AVG(${column}) AS avg`];
641
658
  clone.dml.data = null;
642
659
  clone.dml.limit = 1;
@@ -669,6 +686,7 @@ var Table = class _Table {
669
686
  column,
670
687
  alias: "max"
671
688
  };
689
+ clone.dml.orderBy = [];
672
690
  clone.dml.columns = [`MAX(${column}) AS max`];
673
691
  clone.dml.data = null;
674
692
  clone.dml.limit = 1;
@@ -701,6 +719,7 @@ var Table = class _Table {
701
719
  column,
702
720
  alias: "min"
703
721
  };
722
+ clone.dml.orderBy = [];
704
723
  clone.dml.columns = [`MIN(${column}) AS min`];
705
724
  clone.dml.data = null;
706
725
  clone.dml.limit = 1;
@@ -857,11 +876,10 @@ var Table = class _Table {
857
876
  * console.log(user); // { id: 1, name: 'John' }
858
877
  */
859
878
  async find(value, column = "id") {
860
- const clone = this.clone();
879
+ const clone = this.clone().where(column, "=", value);
861
880
  clone.dml.type = "select";
862
881
  clone.dml.data = null;
863
882
  clone.dml.aggregation = null;
864
- clone.where(column, "=", value);
865
883
  clone.dml.limit = 1;
866
884
  try {
867
885
  const result = await clone.getResponse();
@@ -918,8 +936,7 @@ var Table = class _Table {
918
936
  }
919
937
  clone.dml.type = "update";
920
938
  clone.dml.data = data;
921
- await clone.getResponse(clone.dml, "Update");
922
- return data;
939
+ return clone.getResponse(clone.dml, "Update");
923
940
  }
924
941
  /**
925
942
  * Deletes rows from the table based on the defined conditions.
@@ -1139,12 +1156,37 @@ var Table = class _Table {
1139
1156
  throw new Error("The upsert method requires at least one conflict key column.");
1140
1157
  }
1141
1158
  const motor = this.engine?.getConfig?.()?.type ?? "mysql";
1142
- if (motor === "mongodb") {
1143
- throw new Error("upsert() is not supported for MongoDB yet. Use update() + insert() or raw commands.");
1144
- }
1145
1159
  const table = this.dml.table;
1146
1160
  const columns = Object.keys(data[0]);
1147
1161
  const targets = updateColumns ?? columns.filter((c) => !conflictKeys.includes(c));
1162
+ assertValidIdentifier(table, "table");
1163
+ for (const c of columns) assertValidIdentifier(c, "column");
1164
+ for (const c of conflictKeys) assertValidIdentifier(c, "conflict key");
1165
+ for (const c of targets) assertValidIdentifier(c, "update column");
1166
+ if (motor === "mongodb") {
1167
+ const updates = data.map((row) => {
1168
+ const q = {};
1169
+ for (const k of conflictKeys) q[k] = row[k];
1170
+ const set = {};
1171
+ for (const c of targets) if (c in row) set[c] = row[c];
1172
+ const setOnInsert = {};
1173
+ for (const c of columns) {
1174
+ if (!targets.includes(c) && !conflictKeys.includes(c)) setOnInsert[c] = row[c];
1175
+ }
1176
+ if (!("uuid" in row)) setOnInsert["uuid"] = (0, import_crypto.randomUUID)();
1177
+ const u = {};
1178
+ if (Object.keys(set).length) u["$set"] = set;
1179
+ if (Object.keys(setOnInsert).length) u["$setOnInsert"] = setOnInsert;
1180
+ return { q, u, upsert: true, multi: false };
1181
+ });
1182
+ const command = JSON.stringify({ update: table, updates });
1183
+ const response2 = await this.engine.rawQuery(command, [], this.txId ?? void 0);
1184
+ if (response2.status != 200) {
1185
+ returnFormattedError(response2.status, response2.message);
1186
+ throw new Error(response2.message);
1187
+ }
1188
+ return response2.data;
1189
+ }
1148
1190
  const quote = (id) => motor === "mysql" ? `\`${id}\`` : `"${id}"`;
1149
1191
  const params = [];
1150
1192
  const placeholder = () => motor === "postgres" || motor === "postgresql" ? `$${params.length}` : "?";
@@ -1165,6 +1207,9 @@ var Table = class _Table {
1165
1207
  const updates = targets.map((c) => `${quote(c)} = excluded.${quote(c)}`).join(", ");
1166
1208
  sql = `INSERT INTO ${quote(table)} (${colsSql}) VALUES ${rowsSql} ON CONFLICT (${conflict}) DO UPDATE SET ${updates}`;
1167
1209
  }
1210
+ if (motor === "sqlite") {
1211
+ await this.engine.rawQuery("SELECT name FROM sqlite_master LIMIT 1", [], this.txId ?? void 0);
1212
+ }
1168
1213
  const response = await this.engine.rawQuery(sql, params, this.txId ?? void 0);
1169
1214
  if (response.status != 200) {
1170
1215
  returnFormattedError(response.status, response.message);
@@ -1219,7 +1264,7 @@ var Table = class _Table {
1219
1264
  foreignKey = foreignKey ?? `${singularize(this.dml.table)}_id`;
1220
1265
  }
1221
1266
  }
1222
- const relatedQuery = new _Table(this, this.dml.database, relatedTable, this.engine, this.computedFields, this.triggers, this.txId);
1267
+ const relatedQuery = new _Table(this.instance, this.dml.database, relatedTable, this.engine, this.computedFields, this.triggers, this.txId);
1223
1268
  if (type === "one") {
1224
1269
  const fkValues = [...new Set(rows.map((r) => r[foreignKey]).filter((v) => v !== null && v !== void 0))];
1225
1270
  const related = fkValues.length > 0 ? await relatedQuery.whereIn(localKey, fkValues).get() : [];
@@ -1247,6 +1292,7 @@ var Table = class _Table {
1247
1292
  const localDML = dml ? dml : this.dml;
1248
1293
  const computedFieldsNeeded = [];
1249
1294
  let dependeciesArrray = [];
1295
+ const requestedColumns = new Set(localDML.columns ?? []);
1250
1296
  if (this.computedFields.length > 0) {
1251
1297
  let columns = localDML.columns;
1252
1298
  for (const field of localDML.columns) {
@@ -1274,25 +1320,28 @@ var Table = class _Table {
1274
1320
  const interceptor = await this.trigger.execute("before" + type, data);
1275
1321
  const response = await this.engine.executeDml(newDML, this.txId ?? void 0);
1276
1322
  if (response.status != 200) {
1277
- interceptor.discard();
1323
+ interceptor?.discard();
1278
1324
  returnFormattedError(response.status, response.message);
1325
+ throw new Error(String(response.message));
1279
1326
  }
1280
- await interceptor.commit();
1327
+ await interceptor?.commit();
1281
1328
  arrayResult = response.data;
1282
1329
  }
1283
1330
  if (after) {
1284
1331
  const response = await this.engine.executeDml(newDML, this.txId ?? void 0);
1285
1332
  if (response.status != 200) {
1286
1333
  returnFormattedError(response.status, response.message);
1334
+ throw new Error(String(response.message));
1287
1335
  }
1288
1336
  const interceptor = await this.trigger.execute("after" + type, data);
1289
- await interceptor.commit();
1337
+ await interceptor?.commit();
1290
1338
  }
1291
1339
  }
1292
1340
  } else {
1293
1341
  const response = await this.engine.executeDml(localDML, this.txId ?? void 0);
1294
1342
  if (response.status != 200) {
1295
1343
  returnFormattedError(response.status, response.message);
1344
+ throw new Error(String(response.message));
1296
1345
  }
1297
1346
  arrayResult = response.data;
1298
1347
  }
@@ -1300,14 +1349,16 @@ var Table = class _Table {
1300
1349
  const response = await this.engine.executeDml(localDML, this.txId ?? void 0);
1301
1350
  if (response.status != 200) {
1302
1351
  returnFormattedError(response.status, response.message);
1352
+ throw new Error(String(response.message));
1303
1353
  }
1304
1354
  arrayResult = response.data;
1305
1355
  }
1306
1356
  if (computedFieldsNeeded.length > 0) {
1307
1357
  let newDataset = import_core2.ComputedFieldProcessor.computedFields(arrayResult, computedFieldsNeeded);
1358
+ const toRemove = dependeciesArrray.filter((key) => !requestedColumns.has(key));
1308
1359
  const result = newDataset.map((obj) => {
1309
1360
  const newObj = { ...obj };
1310
- dependeciesArrray.forEach((key) => delete newObj[key]);
1361
+ toRemove.forEach((key) => delete newObj[key]);
1311
1362
  return newObj;
1312
1363
  });
1313
1364
  return result;
@@ -1315,8 +1366,10 @@ var Table = class _Table {
1315
1366
  return arrayResult;
1316
1367
  }
1317
1368
  clone() {
1369
+ if (this._mutable) return this;
1318
1370
  const cloned = Object.create(Object.getPrototypeOf(this));
1319
1371
  cloned.engine = this.engine;
1372
+ cloned.instance = this.instance;
1320
1373
  cloned.nextType = this.nextType;
1321
1374
  cloned.computedFields = this.computedFields;
1322
1375
  cloned.trigger = this.trigger;
@@ -1384,6 +1437,11 @@ function loadCubeRelations() {
1384
1437
  cubeRelationsCache = result;
1385
1438
  return result;
1386
1439
  }
1440
+ function assertValidIdentifier(name, kind) {
1441
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
1442
+ throw new Error(`Invalid ${kind} name: '${name}'. Only letters, numbers and underscore are allowed.`);
1443
+ }
1444
+ }
1387
1445
  function singularize(word) {
1388
1446
  if (word.endsWith("ies")) return word.slice(0, -3) + "y";
1389
1447
  if (word.endsWith("ses")) return word.slice(0, -2);