@dbcube/query-builder 5.1.6 → 5.2.1

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,7 +76,10 @@ 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
+ try {
80
+ delete require2.cache[require2.resolve(pathFile)];
81
+ } catch {
82
+ }
79
83
  const triggerModule = require2(pathFile);
80
84
  const dataProcess = triggerModule.default || triggerModule;
81
85
  await dataProcess({ db: this.instance, oldData: row, newData: row });
@@ -143,7 +147,10 @@ var Database = class _Database {
143
147
  } catch (error) {
144
148
  try {
145
149
  await this.engine.rollbackTransaction(txId);
146
- } catch {
150
+ } catch (rollbackError) {
151
+ console.error(
152
+ `[dbcube] Rollback of transaction ${txId} failed: ${rollbackError?.message ?? rollbackError}`
153
+ );
147
154
  }
148
155
  throw error;
149
156
  }
@@ -168,10 +175,9 @@ var Database = class _Database {
168
175
  if (response.status != 200) {
169
176
  returnFormattedError(response.status, response.message);
170
177
  }
171
- return response.data;
172
178
  }
173
179
  async disconnect() {
174
- return this.engine.run("query_engine", [
180
+ await this.engine.run("query_engine", [
175
181
  "--action",
176
182
  "disconnect"
177
183
  ]);
@@ -232,8 +238,12 @@ var Table = class _Table {
232
238
  triggers;
233
239
  txId = null;
234
240
  relations = [];
235
- constructor(instance, databaseName, tableName, engine = null, computedFields = [], triggers = [], txId = null) {
241
+ /** Builders de grupo mutan en sitio (ver clone()) */
242
+ _mutable = false;
243
+ instance;
244
+ constructor(instance, databaseName, tableName, engine, computedFields = [], triggers = [], txId = null) {
236
245
  this.engine = engine;
246
+ this.instance = instance;
237
247
  this.computedFields = computedFields;
238
248
  this.triggers = triggers;
239
249
  this.trigger = new Trigger(instance, databaseName, triggers);
@@ -310,7 +320,8 @@ var Table = class _Table {
310
320
  */
311
321
  whereGroup(callback) {
312
322
  const clone = this.clone();
313
- const groupQuery = new _Table(this, clone.dml.database, clone.dml.table, clone.engine);
323
+ const groupQuery = new _Table(this.instance, clone.dml.database, clone.dml.table, clone.engine);
324
+ groupQuery._mutable = true;
314
325
  callback(groupQuery);
315
326
  clone.dml.where.push({
316
327
  type: clone.nextType,
@@ -857,11 +868,10 @@ var Table = class _Table {
857
868
  * console.log(user); // { id: 1, name: 'John' }
858
869
  */
859
870
  async find(value, column = "id") {
860
- const clone = this.clone();
871
+ const clone = this.clone().where(column, "=", value);
861
872
  clone.dml.type = "select";
862
873
  clone.dml.data = null;
863
874
  clone.dml.aggregation = null;
864
- clone.where(column, "=", value);
865
875
  clone.dml.limit = 1;
866
876
  try {
867
877
  const result = await clone.getResponse();
@@ -918,8 +928,7 @@ var Table = class _Table {
918
928
  }
919
929
  clone.dml.type = "update";
920
930
  clone.dml.data = data;
921
- await clone.getResponse(clone.dml, "Update");
922
- return data;
931
+ return clone.getResponse(clone.dml, "Update");
923
932
  }
924
933
  /**
925
934
  * Deletes rows from the table based on the defined conditions.
@@ -1139,12 +1148,37 @@ var Table = class _Table {
1139
1148
  throw new Error("The upsert method requires at least one conflict key column.");
1140
1149
  }
1141
1150
  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
1151
  const table = this.dml.table;
1146
1152
  const columns = Object.keys(data[0]);
1147
1153
  const targets = updateColumns ?? columns.filter((c) => !conflictKeys.includes(c));
1154
+ assertValidIdentifier(table, "table");
1155
+ for (const c of columns) assertValidIdentifier(c, "column");
1156
+ for (const c of conflictKeys) assertValidIdentifier(c, "conflict key");
1157
+ for (const c of targets) assertValidIdentifier(c, "update column");
1158
+ if (motor === "mongodb") {
1159
+ const updates = data.map((row) => {
1160
+ const q = {};
1161
+ for (const k of conflictKeys) q[k] = row[k];
1162
+ const set = {};
1163
+ for (const c of targets) if (c in row) set[c] = row[c];
1164
+ const setOnInsert = {};
1165
+ for (const c of columns) {
1166
+ if (!targets.includes(c) && !conflictKeys.includes(c)) setOnInsert[c] = row[c];
1167
+ }
1168
+ if (!("uuid" in row)) setOnInsert["uuid"] = (0, import_crypto.randomUUID)();
1169
+ const u = {};
1170
+ if (Object.keys(set).length) u["$set"] = set;
1171
+ if (Object.keys(setOnInsert).length) u["$setOnInsert"] = setOnInsert;
1172
+ return { q, u, upsert: true, multi: false };
1173
+ });
1174
+ const command = JSON.stringify({ update: table, updates });
1175
+ const response2 = await this.engine.rawQuery(command, [], this.txId ?? void 0);
1176
+ if (response2.status != 200) {
1177
+ returnFormattedError(response2.status, response2.message);
1178
+ throw new Error(response2.message);
1179
+ }
1180
+ return response2.data;
1181
+ }
1148
1182
  const quote = (id) => motor === "mysql" ? `\`${id}\`` : `"${id}"`;
1149
1183
  const params = [];
1150
1184
  const placeholder = () => motor === "postgres" || motor === "postgresql" ? `$${params.length}` : "?";
@@ -1165,6 +1199,9 @@ var Table = class _Table {
1165
1199
  const updates = targets.map((c) => `${quote(c)} = excluded.${quote(c)}`).join(", ");
1166
1200
  sql = `INSERT INTO ${quote(table)} (${colsSql}) VALUES ${rowsSql} ON CONFLICT (${conflict}) DO UPDATE SET ${updates}`;
1167
1201
  }
1202
+ if (motor === "sqlite") {
1203
+ await this.engine.rawQuery("SELECT name FROM sqlite_master LIMIT 1", [], this.txId ?? void 0);
1204
+ }
1168
1205
  const response = await this.engine.rawQuery(sql, params, this.txId ?? void 0);
1169
1206
  if (response.status != 200) {
1170
1207
  returnFormattedError(response.status, response.message);
@@ -1219,7 +1256,7 @@ var Table = class _Table {
1219
1256
  foreignKey = foreignKey ?? `${singularize(this.dml.table)}_id`;
1220
1257
  }
1221
1258
  }
1222
- const relatedQuery = new _Table(this, this.dml.database, relatedTable, this.engine, this.computedFields, this.triggers, this.txId);
1259
+ const relatedQuery = new _Table(this.instance, this.dml.database, relatedTable, this.engine, this.computedFields, this.triggers, this.txId);
1223
1260
  if (type === "one") {
1224
1261
  const fkValues = [...new Set(rows.map((r) => r[foreignKey]).filter((v) => v !== null && v !== void 0))];
1225
1262
  const related = fkValues.length > 0 ? await relatedQuery.whereIn(localKey, fkValues).get() : [];
@@ -1274,10 +1311,10 @@ var Table = class _Table {
1274
1311
  const interceptor = await this.trigger.execute("before" + type, data);
1275
1312
  const response = await this.engine.executeDml(newDML, this.txId ?? void 0);
1276
1313
  if (response.status != 200) {
1277
- interceptor.discard();
1314
+ interceptor?.discard();
1278
1315
  returnFormattedError(response.status, response.message);
1279
1316
  }
1280
- await interceptor.commit();
1317
+ await interceptor?.commit();
1281
1318
  arrayResult = response.data;
1282
1319
  }
1283
1320
  if (after) {
@@ -1286,7 +1323,7 @@ var Table = class _Table {
1286
1323
  returnFormattedError(response.status, response.message);
1287
1324
  }
1288
1325
  const interceptor = await this.trigger.execute("after" + type, data);
1289
- await interceptor.commit();
1326
+ await interceptor?.commit();
1290
1327
  }
1291
1328
  }
1292
1329
  } else {
@@ -1315,8 +1352,10 @@ var Table = class _Table {
1315
1352
  return arrayResult;
1316
1353
  }
1317
1354
  clone() {
1355
+ if (this._mutable) return this;
1318
1356
  const cloned = Object.create(Object.getPrototypeOf(this));
1319
1357
  cloned.engine = this.engine;
1358
+ cloned.instance = this.instance;
1320
1359
  cloned.nextType = this.nextType;
1321
1360
  cloned.computedFields = this.computedFields;
1322
1361
  cloned.trigger = this.trigger;
@@ -1384,6 +1423,11 @@ function loadCubeRelations() {
1384
1423
  cubeRelationsCache = result;
1385
1424
  return result;
1386
1425
  }
1426
+ function assertValidIdentifier(name, kind) {
1427
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
1428
+ throw new Error(`Invalid ${kind} name: '${name}'. Only letters, numbers and underscore are allowed.`);
1429
+ }
1430
+ }
1387
1431
  function singularize(word) {
1388
1432
  if (word.endsWith("ies")) return word.slice(0, -3) + "y";
1389
1433
  if (word.endsWith("ses")) return word.slice(0, -2);