@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.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/lib/Database.ts
2
2
  import fs from "fs";
3
3
  import path2 from "path";
4
+ import { randomUUID } from "crypto";
4
5
  import { QueryEngine, ComputedFieldProcessor, TriggerProcessor } from "@dbcube/core";
5
6
 
6
7
  // src/lib/Trigger.ts
@@ -37,11 +38,17 @@ var Trigger = class {
37
38
  const pathFile = path.resolve(process.cwd(), "dbcube", "triggers", `${trigger.database_ref}_${trigger.table_ref}_${trigger.type}.js`);
38
39
  const requireUrl = typeof __filename !== "undefined" ? __filename : process.cwd();
39
40
  const require2 = createRequire(requireUrl);
40
- delete require2.cache[require2.resolve(pathFile)];
41
- const triggerModule = require2(pathFile);
42
- const dataProcess = triggerModule.default || triggerModule;
43
- await dataProcess({ db: this.instance, oldData: row, newData: row });
44
- interceptor.restore();
41
+ try {
42
+ delete require2.cache[require2.resolve(pathFile)];
43
+ } catch {
44
+ }
45
+ try {
46
+ const triggerModule = require2(pathFile);
47
+ const dataProcess = triggerModule.default || triggerModule;
48
+ await dataProcess({ db: this.instance, oldData: row, newData: row });
49
+ } finally {
50
+ interceptor.restore();
51
+ }
45
52
  return interceptor;
46
53
  }
47
54
  return null;
@@ -105,7 +112,10 @@ var Database = class _Database {
105
112
  } catch (error) {
106
113
  try {
107
114
  await this.engine.rollbackTransaction(txId);
108
- } catch {
115
+ } catch (rollbackError) {
116
+ console.error(
117
+ `[dbcube] Rollback of transaction ${txId} failed: ${rollbackError?.message ?? rollbackError}`
118
+ );
109
119
  }
110
120
  throw error;
111
121
  }
@@ -130,10 +140,9 @@ var Database = class _Database {
130
140
  if (response.status != 200) {
131
141
  returnFormattedError(response.status, response.message);
132
142
  }
133
- return response.data;
134
143
  }
135
144
  async disconnect() {
136
- return this.engine.run("query_engine", [
145
+ await this.engine.run("query_engine", [
137
146
  "--action",
138
147
  "disconnect"
139
148
  ]);
@@ -194,8 +203,12 @@ var Table = class _Table {
194
203
  triggers;
195
204
  txId = null;
196
205
  relations = [];
197
- constructor(instance, databaseName, tableName, engine = null, computedFields = [], triggers = [], txId = null) {
206
+ /** Builders de grupo mutan en sitio (ver clone()) */
207
+ _mutable = false;
208
+ instance;
209
+ constructor(instance, databaseName, tableName, engine, computedFields = [], triggers = [], txId = null) {
198
210
  this.engine = engine;
211
+ this.instance = instance;
199
212
  this.computedFields = computedFields;
200
213
  this.triggers = triggers;
201
214
  this.trigger = new Trigger(instance, databaseName, triggers);
@@ -272,7 +285,8 @@ var Table = class _Table {
272
285
  */
273
286
  whereGroup(callback) {
274
287
  const clone = this.clone();
275
- const groupQuery = new _Table(this, clone.dml.database, clone.dml.table, clone.engine);
288
+ const groupQuery = new _Table(this.instance, clone.dml.database, clone.dml.table, clone.engine);
289
+ groupQuery._mutable = true;
276
290
  callback(groupQuery);
277
291
  clone.dml.where.push({
278
292
  type: clone.nextType,
@@ -534,6 +548,7 @@ var Table = class _Table {
534
548
  column,
535
549
  alias: "count"
536
550
  };
551
+ clone.dml.orderBy = [];
537
552
  clone.dml.columns = [`COUNT(${column}) AS count`];
538
553
  clone.dml.data = null;
539
554
  clone.dml.limit = null;
@@ -567,6 +582,7 @@ var Table = class _Table {
567
582
  column,
568
583
  alias: "sum"
569
584
  };
585
+ clone.dml.orderBy = [];
570
586
  clone.dml.columns = [`SUM(${column}) AS sum`];
571
587
  clone.dml.data = null;
572
588
  clone.dml.limit = 1;
@@ -599,6 +615,7 @@ var Table = class _Table {
599
615
  column,
600
616
  alias: "avg"
601
617
  };
618
+ clone.dml.orderBy = [];
602
619
  clone.dml.columns = [`AVG(${column}) AS avg`];
603
620
  clone.dml.data = null;
604
621
  clone.dml.limit = 1;
@@ -631,6 +648,7 @@ var Table = class _Table {
631
648
  column,
632
649
  alias: "max"
633
650
  };
651
+ clone.dml.orderBy = [];
634
652
  clone.dml.columns = [`MAX(${column}) AS max`];
635
653
  clone.dml.data = null;
636
654
  clone.dml.limit = 1;
@@ -663,6 +681,7 @@ var Table = class _Table {
663
681
  column,
664
682
  alias: "min"
665
683
  };
684
+ clone.dml.orderBy = [];
666
685
  clone.dml.columns = [`MIN(${column}) AS min`];
667
686
  clone.dml.data = null;
668
687
  clone.dml.limit = 1;
@@ -819,11 +838,10 @@ var Table = class _Table {
819
838
  * console.log(user); // { id: 1, name: 'John' }
820
839
  */
821
840
  async find(value, column = "id") {
822
- const clone = this.clone();
841
+ const clone = this.clone().where(column, "=", value);
823
842
  clone.dml.type = "select";
824
843
  clone.dml.data = null;
825
844
  clone.dml.aggregation = null;
826
- clone.where(column, "=", value);
827
845
  clone.dml.limit = 1;
828
846
  try {
829
847
  const result = await clone.getResponse();
@@ -880,8 +898,7 @@ var Table = class _Table {
880
898
  }
881
899
  clone.dml.type = "update";
882
900
  clone.dml.data = data;
883
- await clone.getResponse(clone.dml, "Update");
884
- return data;
901
+ return clone.getResponse(clone.dml, "Update");
885
902
  }
886
903
  /**
887
904
  * Deletes rows from the table based on the defined conditions.
@@ -1101,12 +1118,37 @@ var Table = class _Table {
1101
1118
  throw new Error("The upsert method requires at least one conflict key column.");
1102
1119
  }
1103
1120
  const motor = this.engine?.getConfig?.()?.type ?? "mysql";
1104
- if (motor === "mongodb") {
1105
- throw new Error("upsert() is not supported for MongoDB yet. Use update() + insert() or raw commands.");
1106
- }
1107
1121
  const table = this.dml.table;
1108
1122
  const columns = Object.keys(data[0]);
1109
1123
  const targets = updateColumns ?? columns.filter((c) => !conflictKeys.includes(c));
1124
+ assertValidIdentifier(table, "table");
1125
+ for (const c of columns) assertValidIdentifier(c, "column");
1126
+ for (const c of conflictKeys) assertValidIdentifier(c, "conflict key");
1127
+ for (const c of targets) assertValidIdentifier(c, "update column");
1128
+ if (motor === "mongodb") {
1129
+ const updates = data.map((row) => {
1130
+ const q = {};
1131
+ for (const k of conflictKeys) q[k] = row[k];
1132
+ const set = {};
1133
+ for (const c of targets) if (c in row) set[c] = row[c];
1134
+ const setOnInsert = {};
1135
+ for (const c of columns) {
1136
+ if (!targets.includes(c) && !conflictKeys.includes(c)) setOnInsert[c] = row[c];
1137
+ }
1138
+ if (!("uuid" in row)) setOnInsert["uuid"] = randomUUID();
1139
+ const u = {};
1140
+ if (Object.keys(set).length) u["$set"] = set;
1141
+ if (Object.keys(setOnInsert).length) u["$setOnInsert"] = setOnInsert;
1142
+ return { q, u, upsert: true, multi: false };
1143
+ });
1144
+ const command = JSON.stringify({ update: table, updates });
1145
+ const response2 = await this.engine.rawQuery(command, [], this.txId ?? void 0);
1146
+ if (response2.status != 200) {
1147
+ returnFormattedError(response2.status, response2.message);
1148
+ throw new Error(response2.message);
1149
+ }
1150
+ return response2.data;
1151
+ }
1110
1152
  const quote = (id) => motor === "mysql" ? `\`${id}\`` : `"${id}"`;
1111
1153
  const params = [];
1112
1154
  const placeholder = () => motor === "postgres" || motor === "postgresql" ? `$${params.length}` : "?";
@@ -1127,6 +1169,9 @@ var Table = class _Table {
1127
1169
  const updates = targets.map((c) => `${quote(c)} = excluded.${quote(c)}`).join(", ");
1128
1170
  sql = `INSERT INTO ${quote(table)} (${colsSql}) VALUES ${rowsSql} ON CONFLICT (${conflict}) DO UPDATE SET ${updates}`;
1129
1171
  }
1172
+ if (motor === "sqlite") {
1173
+ await this.engine.rawQuery("SELECT name FROM sqlite_master LIMIT 1", [], this.txId ?? void 0);
1174
+ }
1130
1175
  const response = await this.engine.rawQuery(sql, params, this.txId ?? void 0);
1131
1176
  if (response.status != 200) {
1132
1177
  returnFormattedError(response.status, response.message);
@@ -1181,7 +1226,7 @@ var Table = class _Table {
1181
1226
  foreignKey = foreignKey ?? `${singularize(this.dml.table)}_id`;
1182
1227
  }
1183
1228
  }
1184
- const relatedQuery = new _Table(this, this.dml.database, relatedTable, this.engine, this.computedFields, this.triggers, this.txId);
1229
+ const relatedQuery = new _Table(this.instance, this.dml.database, relatedTable, this.engine, this.computedFields, this.triggers, this.txId);
1185
1230
  if (type === "one") {
1186
1231
  const fkValues = [...new Set(rows.map((r) => r[foreignKey]).filter((v) => v !== null && v !== void 0))];
1187
1232
  const related = fkValues.length > 0 ? await relatedQuery.whereIn(localKey, fkValues).get() : [];
@@ -1209,6 +1254,7 @@ var Table = class _Table {
1209
1254
  const localDML = dml ? dml : this.dml;
1210
1255
  const computedFieldsNeeded = [];
1211
1256
  let dependeciesArrray = [];
1257
+ const requestedColumns = new Set(localDML.columns ?? []);
1212
1258
  if (this.computedFields.length > 0) {
1213
1259
  let columns = localDML.columns;
1214
1260
  for (const field of localDML.columns) {
@@ -1236,25 +1282,28 @@ var Table = class _Table {
1236
1282
  const interceptor = await this.trigger.execute("before" + type, data);
1237
1283
  const response = await this.engine.executeDml(newDML, this.txId ?? void 0);
1238
1284
  if (response.status != 200) {
1239
- interceptor.discard();
1285
+ interceptor?.discard();
1240
1286
  returnFormattedError(response.status, response.message);
1287
+ throw new Error(String(response.message));
1241
1288
  }
1242
- await interceptor.commit();
1289
+ await interceptor?.commit();
1243
1290
  arrayResult = response.data;
1244
1291
  }
1245
1292
  if (after) {
1246
1293
  const response = await this.engine.executeDml(newDML, this.txId ?? void 0);
1247
1294
  if (response.status != 200) {
1248
1295
  returnFormattedError(response.status, response.message);
1296
+ throw new Error(String(response.message));
1249
1297
  }
1250
1298
  const interceptor = await this.trigger.execute("after" + type, data);
1251
- await interceptor.commit();
1299
+ await interceptor?.commit();
1252
1300
  }
1253
1301
  }
1254
1302
  } else {
1255
1303
  const response = await this.engine.executeDml(localDML, this.txId ?? void 0);
1256
1304
  if (response.status != 200) {
1257
1305
  returnFormattedError(response.status, response.message);
1306
+ throw new Error(String(response.message));
1258
1307
  }
1259
1308
  arrayResult = response.data;
1260
1309
  }
@@ -1262,14 +1311,16 @@ var Table = class _Table {
1262
1311
  const response = await this.engine.executeDml(localDML, this.txId ?? void 0);
1263
1312
  if (response.status != 200) {
1264
1313
  returnFormattedError(response.status, response.message);
1314
+ throw new Error(String(response.message));
1265
1315
  }
1266
1316
  arrayResult = response.data;
1267
1317
  }
1268
1318
  if (computedFieldsNeeded.length > 0) {
1269
1319
  let newDataset = ComputedFieldProcessor.computedFields(arrayResult, computedFieldsNeeded);
1320
+ const toRemove = dependeciesArrray.filter((key) => !requestedColumns.has(key));
1270
1321
  const result = newDataset.map((obj) => {
1271
1322
  const newObj = { ...obj };
1272
- dependeciesArrray.forEach((key) => delete newObj[key]);
1323
+ toRemove.forEach((key) => delete newObj[key]);
1273
1324
  return newObj;
1274
1325
  });
1275
1326
  return result;
@@ -1277,8 +1328,10 @@ var Table = class _Table {
1277
1328
  return arrayResult;
1278
1329
  }
1279
1330
  clone() {
1331
+ if (this._mutable) return this;
1280
1332
  const cloned = Object.create(Object.getPrototypeOf(this));
1281
1333
  cloned.engine = this.engine;
1334
+ cloned.instance = this.instance;
1282
1335
  cloned.nextType = this.nextType;
1283
1336
  cloned.computedFields = this.computedFields;
1284
1337
  cloned.trigger = this.trigger;
@@ -1346,6 +1399,11 @@ function loadCubeRelations() {
1346
1399
  cubeRelationsCache = result;
1347
1400
  return result;
1348
1401
  }
1402
+ function assertValidIdentifier(name, kind) {
1403
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
1404
+ throw new Error(`Invalid ${kind} name: '${name}'. Only letters, numbers and underscore are allowed.`);
1405
+ }
1406
+ }
1349
1407
  function singularize(word) {
1350
1408
  if (word.endsWith("ies")) return word.slice(0, -3) + "y";
1351
1409
  if (word.endsWith("ses")) return word.slice(0, -2);