@dbcube/query-builder 5.2.1 → 5.2.3

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.d.ts CHANGED
@@ -1,5 +1,54 @@
1
1
  import { QueryEngine } from '@dbcube/core';
2
2
 
3
+ export interface JoinCondition {
4
+ column1: string;
5
+ operator: string;
6
+ column2: string;
7
+ }
8
+ export interface Join {
9
+ type: "INNER" | "LEFT" | "RIGHT";
10
+ table: string;
11
+ on: JoinCondition;
12
+ }
13
+ export interface WhereCondition {
14
+ column: string;
15
+ operator: string;
16
+ value?: any;
17
+ type: "AND" | "OR";
18
+ isGroup: boolean;
19
+ }
20
+ export interface GroupedWhereCondition {
21
+ type: "AND" | "OR";
22
+ isGroup: true;
23
+ conditions: WhereCondition[];
24
+ }
25
+ export interface OrderBy {
26
+ column: string;
27
+ direction: "ASC" | "DESC";
28
+ }
29
+ export interface Aggregation {
30
+ type: "COUNT" | "SUM" | "AVG" | "MAX" | "MIN";
31
+ column: string;
32
+ alias: string;
33
+ }
34
+ export interface DML {
35
+ type: "select" | "insert" | "update" | "delete" | "columns";
36
+ database: string;
37
+ table: string;
38
+ columns: string[];
39
+ requestedFields?: string[];
40
+ computedFieldsNeeded?: any[];
41
+ distinct: boolean;
42
+ joins: Join[];
43
+ where: (WhereCondition | GroupedWhereCondition)[];
44
+ orderBy: OrderBy[];
45
+ groupBy: string[];
46
+ limit: number | null;
47
+ offset: number | null;
48
+ data: any[] | Record<string, any> | null;
49
+ aggregation: Aggregation | null;
50
+ having?: (WhereCondition | GroupedWhereCondition)[];
51
+ }
3
52
  export interface PaginatedResult<T = DatabaseRecord> {
4
53
  items: T[];
5
54
  page: number;
@@ -80,6 +129,24 @@ export declare class Database {
80
129
  * });
81
130
  */
82
131
  transaction<T>(callback: (trx: Database) => Promise<T>): Promise<T>;
132
+ /**
133
+ * Atomic batch: every write queued in the callback runs inside ONE
134
+ * transaction with a SINGLE engine round-trip (begin/commit included).
135
+ * The callback is synchronous — writes are collected, not awaited.
136
+ * Any failure rolls back everything.
137
+ *
138
+ * This is the fastest way to run a sequence of writes atomically.
139
+ * For transactions that need to READ intermediate results, use
140
+ * `transaction()` (interactive) instead. Triggers/computed fields do
141
+ * not run in batch mode.
142
+ *
143
+ * @example
144
+ * await db.batch(b => {
145
+ * b.table('accounts').where('id', '=', 1).decrement('balance', 200);
146
+ * b.table('accounts').where('id', '=', 2).increment('balance', 200);
147
+ * });
148
+ */
149
+ batch(builder: (b: Database) => void): Promise<MutationInfo[][]>;
83
150
  useComputes(): Promise<Database>;
84
151
  useTriggers(): Promise<Database>;
85
152
  connect(): Promise<void>;
@@ -143,6 +210,9 @@ export declare class Table<T extends DatabaseRecord = DatabaseRecord> {
143
210
  private relations;
144
211
  /** Builders de grupo mutan en sitio (ver clone()) */
145
212
  private _mutable;
213
+ /** En modo batch (db.batch) las escrituras se encolan aquí en vez de
214
+ * ejecutarse: toda la transacción viaja al engine en UN solo cruce. */
215
+ _batchSink: DML[] | null;
146
216
  private instance;
147
217
  constructor(instance: Database, databaseName: string, tableName: string, engine: QueryEngine, computedFields?: ComputedFieldConfig[], triggers?: TriggerConfig[], txId?: string | null);
148
218
  /**
package/dist/index.js CHANGED
@@ -42,10 +42,13 @@ var Trigger = class {
42
42
  delete require2.cache[require2.resolve(pathFile)];
43
43
  } catch {
44
44
  }
45
- const triggerModule = require2(pathFile);
46
- const dataProcess = triggerModule.default || triggerModule;
47
- await dataProcess({ db: this.instance, oldData: row, newData: row });
48
- interceptor.restore();
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
+ }
49
52
  return interceptor;
50
53
  }
51
54
  return null;
@@ -117,6 +120,39 @@ var Database = class _Database {
117
120
  throw error;
118
121
  }
119
122
  }
123
+ /**
124
+ * Atomic batch: every write queued in the callback runs inside ONE
125
+ * transaction with a SINGLE engine round-trip (begin/commit included).
126
+ * The callback is synchronous — writes are collected, not awaited.
127
+ * Any failure rolls back everything.
128
+ *
129
+ * This is the fastest way to run a sequence of writes atomically.
130
+ * For transactions that need to READ intermediate results, use
131
+ * `transaction()` (interactive) instead. Triggers/computed fields do
132
+ * not run in batch mode.
133
+ *
134
+ * @example
135
+ * await db.batch(b => {
136
+ * b.table('accounts').where('id', '=', 1).decrement('balance', 200);
137
+ * b.table('accounts').where('id', '=', 2).increment('balance', 200);
138
+ * });
139
+ */
140
+ async batch(builder) {
141
+ const ops = [];
142
+ const collector = new _Database(this.name);
143
+ collector.engine = this.engine;
144
+ collector.computedFields = this.computedFields;
145
+ collector.triggers = this.triggers;
146
+ collector._collectInto = ops;
147
+ builder(collector);
148
+ if (ops.length === 0) return [];
149
+ const response = await this.engine.executeBatch(ops);
150
+ if (response.status !== 200) {
151
+ returnFormattedError(response.status, response.message);
152
+ throw new Error(String(response.message));
153
+ }
154
+ return response.data;
155
+ }
120
156
  async useComputes() {
121
157
  const newDatabase = new _Database(this.name);
122
158
  const arrayComputedFields = await ComputedFieldProcessor.getComputedFields(this.name);
@@ -182,7 +218,10 @@ var Database = class _Database {
182
218
  * const columns = await db.table('users').columns().get();
183
219
  */
184
220
  table(tableName) {
185
- return new Table(this, this.name, tableName, this.engine, this.computedFields, this.triggers, this.txId);
221
+ const t = new Table(this, this.name, tableName, this.engine, this.computedFields, this.triggers, this.txId);
222
+ const sink = this._collectInto;
223
+ if (sink) t._batchSink = sink;
224
+ return t;
186
225
  }
187
226
  setComputedFields(computedFields) {
188
227
  this.computedFields = computedFields;
@@ -202,13 +241,16 @@ var Table = class _Table {
202
241
  relations = [];
203
242
  /** Builders de grupo mutan en sitio (ver clone()) */
204
243
  _mutable = false;
244
+ /** En modo batch (db.batch) las escrituras se encolan aquí en vez de
245
+ * ejecutarse: toda la transacción viaja al engine en UN solo cruce. */
246
+ _batchSink = null;
205
247
  instance;
206
248
  constructor(instance, databaseName, tableName, engine, computedFields = [], triggers = [], txId = null) {
207
249
  this.engine = engine;
208
250
  this.instance = instance;
209
251
  this.computedFields = computedFields;
210
252
  this.triggers = triggers;
211
- this.trigger = new Trigger(instance, databaseName, triggers);
253
+ this.trigger = triggers.length > 0 ? new Trigger(instance, databaseName, triggers) : null;
212
254
  this.nextType = "AND";
213
255
  this.txId = txId;
214
256
  this.dml = {
@@ -545,6 +587,7 @@ var Table = class _Table {
545
587
  column,
546
588
  alias: "count"
547
589
  };
590
+ clone.dml.orderBy = [];
548
591
  clone.dml.columns = [`COUNT(${column}) AS count`];
549
592
  clone.dml.data = null;
550
593
  clone.dml.limit = null;
@@ -578,6 +621,7 @@ var Table = class _Table {
578
621
  column,
579
622
  alias: "sum"
580
623
  };
624
+ clone.dml.orderBy = [];
581
625
  clone.dml.columns = [`SUM(${column}) AS sum`];
582
626
  clone.dml.data = null;
583
627
  clone.dml.limit = 1;
@@ -610,6 +654,7 @@ var Table = class _Table {
610
654
  column,
611
655
  alias: "avg"
612
656
  };
657
+ clone.dml.orderBy = [];
613
658
  clone.dml.columns = [`AVG(${column}) AS avg`];
614
659
  clone.dml.data = null;
615
660
  clone.dml.limit = 1;
@@ -642,6 +687,7 @@ var Table = class _Table {
642
687
  column,
643
688
  alias: "max"
644
689
  };
690
+ clone.dml.orderBy = [];
645
691
  clone.dml.columns = [`MAX(${column}) AS max`];
646
692
  clone.dml.data = null;
647
693
  clone.dml.limit = 1;
@@ -674,6 +720,7 @@ var Table = class _Table {
674
720
  column,
675
721
  alias: "min"
676
722
  };
723
+ clone.dml.orderBy = [];
677
724
  clone.dml.columns = [`MIN(${column}) AS min`];
678
725
  clone.dml.data = null;
679
726
  clone.dml.limit = 1;
@@ -830,6 +877,30 @@ var Table = class _Table {
830
877
  * console.log(user); // { id: 1, name: 'John' }
831
878
  */
832
879
  async find(value, column = "id") {
880
+ if (this.computedFields.length === 0 && this.relations.length === 0 && !this._mutable) {
881
+ const dml = {
882
+ type: "select",
883
+ database: this.dml.database,
884
+ table: this.dml.table,
885
+ columns: ["*"],
886
+ distinct: false,
887
+ joins: [],
888
+ where: [{ column, operator: "=", value, type: "AND", isGroup: false }],
889
+ orderBy: [],
890
+ groupBy: [],
891
+ limit: 1,
892
+ offset: null,
893
+ data: null,
894
+ aggregation: null,
895
+ having: []
896
+ };
897
+ const response = await this.engine.executeDml(dml, this.txId ?? void 0);
898
+ if (response.status != 200) {
899
+ returnFormattedError(response.status, response.message);
900
+ throw new Error(String(response.message));
901
+ }
902
+ return response.data?.[0] || null;
903
+ }
833
904
  const clone = this.clone().where(column, "=", value);
834
905
  clone.dml.type = "select";
835
906
  clone.dml.data = null;
@@ -856,13 +927,26 @@ var Table = class _Table {
856
927
  * console.log(newUsers); // [{ id: 3, name: 'Alice', age: 28 }, { id: 4, name: 'Bob', age: 32 }]
857
928
  */
858
929
  async insert(data) {
859
- const clone = this.clone();
860
930
  if (!Array.isArray(data)) {
861
931
  throw new Error("The insert method requires an array of objects with key-value pairs.");
862
932
  }
863
933
  if (!data.every((item) => typeof item === "object" && item !== null)) {
864
934
  throw new Error("The array must contain only valid objects.");
865
935
  }
936
+ if (this._batchSink) {
937
+ this._batchSink.push({ ...this.dml, type: "insert", data });
938
+ return void 0;
939
+ }
940
+ if (this.triggers.length === 0 && this.computedFields.length === 0) {
941
+ const dml = { ...this.dml, type: "insert", data };
942
+ const response = await this.engine.executeDml(dml, this.txId ?? void 0);
943
+ if (response.status != 200) {
944
+ returnFormattedError(response.status, response.message);
945
+ throw new Error(String(response.message));
946
+ }
947
+ return response.data ?? data;
948
+ }
949
+ const clone = this.clone();
866
950
  clone.dml.type = "insert";
867
951
  clone.dml.data = data;
868
952
  const result = await clone.getResponse(clone.dml, "Add");
@@ -881,13 +965,26 @@ var Table = class _Table {
881
965
  * console.log(result); // { affectedRows: 1 }
882
966
  */
883
967
  async update(data) {
884
- const clone = this.clone();
885
968
  if (typeof data !== "object" || Array.isArray(data)) {
886
969
  throw new Error("The update method requires an object with key-value pairs.");
887
970
  }
888
- if (clone.dml.where.length === 0) {
971
+ if (this.dml.where.length === 0) {
889
972
  throw new Error("You must specify at least one WHERE condition to perform an update.");
890
973
  }
974
+ if (this._batchSink) {
975
+ this._batchSink.push({ ...this.dml, type: "update", data });
976
+ return void 0;
977
+ }
978
+ if (this.triggers.length === 0 && this.computedFields.length === 0) {
979
+ const dml = { ...this.dml, type: "update", data };
980
+ const response = await this.engine.executeDml(dml, this.txId ?? void 0);
981
+ if (response.status != 200) {
982
+ returnFormattedError(response.status, response.message);
983
+ throw new Error(String(response.message));
984
+ }
985
+ return response.data;
986
+ }
987
+ const clone = this.clone();
891
988
  clone.dml.type = "update";
892
989
  clone.dml.data = data;
893
990
  return clone.getResponse(clone.dml, "Update");
@@ -902,10 +999,23 @@ var Table = class _Table {
902
999
  * console.log(result); // { affectedRows: 1 }
903
1000
  */
904
1001
  async delete() {
905
- const clone = this.clone();
906
- if (clone.dml.where.length === 0) {
1002
+ if (this.dml.where.length === 0) {
907
1003
  throw new Error("You must specify at least one WHERE condition to perform a delete.");
908
1004
  }
1005
+ if (this._batchSink) {
1006
+ this._batchSink.push({ ...this.dml, type: "delete", data: null });
1007
+ return void 0;
1008
+ }
1009
+ if (this.triggers.length === 0 && this.computedFields.length === 0) {
1010
+ const dml = { ...this.dml, type: "delete", data: null };
1011
+ const response = await this.engine.executeDml(dml, this.txId ?? void 0);
1012
+ if (response.status != 200) {
1013
+ returnFormattedError(response.status, response.message);
1014
+ throw new Error(String(response.message));
1015
+ }
1016
+ return response.data;
1017
+ }
1018
+ const clone = this.clone();
909
1019
  clone.dml.type = "delete";
910
1020
  const deleteData = await clone.getResponse(clone.dml, "Delete");
911
1021
  return deleteData;
@@ -1058,12 +1168,26 @@ var Table = class _Table {
1058
1168
  * await db.table('posts').where('id', '=', 1).increment('views', 1, { last_viewed_at: new Date().toISOString() });
1059
1169
  */
1060
1170
  async increment(column, amount = 1, extra = {}) {
1061
- const clone = this.clone();
1062
- if (clone.dml.where.length === 0) {
1171
+ if (this.dml.where.length === 0) {
1063
1172
  throw new Error("You must specify at least one WHERE condition to perform an increment.");
1064
1173
  }
1174
+ const data = { ...extra, [column]: { $inc: Number(amount) } };
1175
+ if (this._batchSink) {
1176
+ this._batchSink.push({ ...this.dml, type: "update", data });
1177
+ return void 0;
1178
+ }
1179
+ if (this.triggers.length === 0 && this.computedFields.length === 0) {
1180
+ const dml = { ...this.dml, type: "update", data };
1181
+ const response = await this.engine.executeDml(dml, this.txId ?? void 0);
1182
+ if (response.status != 200) {
1183
+ returnFormattedError(response.status, response.message);
1184
+ throw new Error(String(response.message));
1185
+ }
1186
+ return response.data;
1187
+ }
1188
+ const clone = this.clone();
1065
1189
  clone.dml.type = "update";
1066
- clone.dml.data = { ...extra, [column]: { $inc: Number(amount) } };
1190
+ clone.dml.data = data;
1067
1191
  return clone.getResponse();
1068
1192
  }
1069
1193
  /**
@@ -1246,6 +1370,7 @@ var Table = class _Table {
1246
1370
  const localDML = dml ? dml : this.dml;
1247
1371
  const computedFieldsNeeded = [];
1248
1372
  let dependeciesArrray = [];
1373
+ const requestedColumns = new Set(localDML.columns ?? []);
1249
1374
  if (this.computedFields.length > 0) {
1250
1375
  let columns = localDML.columns;
1251
1376
  for (const field of localDML.columns) {
@@ -1262,8 +1387,8 @@ var Table = class _Table {
1262
1387
  }
1263
1388
  let arrayResult = [];
1264
1389
  if (type) {
1265
- const beffore = this.trigger.get("before" + type);
1266
- const after = this.trigger.get("after" + type);
1390
+ const beffore = this.trigger ? this.trigger.get("before" + type) : void 0;
1391
+ const after = this.trigger ? this.trigger.get("after" + type) : void 0;
1267
1392
  if (this.triggers.length > 0 && (beffore || after)) {
1268
1393
  const dataset = localDML.data;
1269
1394
  for (let index = 0; index < dataset.length; index++) {
@@ -1275,6 +1400,7 @@ var Table = class _Table {
1275
1400
  if (response.status != 200) {
1276
1401
  interceptor?.discard();
1277
1402
  returnFormattedError(response.status, response.message);
1403
+ throw new Error(String(response.message));
1278
1404
  }
1279
1405
  await interceptor?.commit();
1280
1406
  arrayResult = response.data;
@@ -1283,6 +1409,7 @@ var Table = class _Table {
1283
1409
  const response = await this.engine.executeDml(newDML, this.txId ?? void 0);
1284
1410
  if (response.status != 200) {
1285
1411
  returnFormattedError(response.status, response.message);
1412
+ throw new Error(String(response.message));
1286
1413
  }
1287
1414
  const interceptor = await this.trigger.execute("after" + type, data);
1288
1415
  await interceptor?.commit();
@@ -1292,6 +1419,7 @@ var Table = class _Table {
1292
1419
  const response = await this.engine.executeDml(localDML, this.txId ?? void 0);
1293
1420
  if (response.status != 200) {
1294
1421
  returnFormattedError(response.status, response.message);
1422
+ throw new Error(String(response.message));
1295
1423
  }
1296
1424
  arrayResult = response.data;
1297
1425
  }
@@ -1299,14 +1427,16 @@ var Table = class _Table {
1299
1427
  const response = await this.engine.executeDml(localDML, this.txId ?? void 0);
1300
1428
  if (response.status != 200) {
1301
1429
  returnFormattedError(response.status, response.message);
1430
+ throw new Error(String(response.message));
1302
1431
  }
1303
1432
  arrayResult = response.data;
1304
1433
  }
1305
1434
  if (computedFieldsNeeded.length > 0) {
1306
1435
  let newDataset = ComputedFieldProcessor.computedFields(arrayResult, computedFieldsNeeded);
1436
+ const toRemove = dependeciesArrray.filter((key) => !requestedColumns.has(key));
1307
1437
  const result = newDataset.map((obj) => {
1308
1438
  const newObj = { ...obj };
1309
- dependeciesArrray.forEach((key) => delete newObj[key]);
1439
+ toRemove.forEach((key) => delete newObj[key]);
1310
1440
  return newObj;
1311
1441
  });
1312
1442
  return result;
@@ -1318,6 +1448,7 @@ var Table = class _Table {
1318
1448
  const cloned = Object.create(Object.getPrototypeOf(this));
1319
1449
  cloned.engine = this.engine;
1320
1450
  cloned.instance = this.instance;
1451
+ cloned._batchSink = this._batchSink;
1321
1452
  cloned.nextType = this.nextType;
1322
1453
  cloned.computedFields = this.computedFields;
1323
1454
  cloned.trigger = this.trigger;