@carbonorm/carbonnode 3.7.6 → 3.7.7

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.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/dist/api/orm/builders/AggregateBuilder.d.ts +1 -0
  3. package/dist/api/types/ormInterfaces.d.ts +2 -2
  4. package/dist/api/utils/cacheManager.d.ts +1 -1
  5. package/dist/api/utils/normalizeSingularRequest.d.ts +10 -0
  6. package/dist/index.cjs.js +175 -32
  7. package/dist/index.cjs.js.map +1 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.esm.js +176 -34
  10. package/dist/index.esm.js.map +1 -1
  11. package/package.json +11 -6
  12. package/scripts/assets/handlebars/C6.test.ts.handlebars +55 -80
  13. package/scripts/assets/handlebars/C6.ts.handlebars +28 -2
  14. package/scripts/generateRestBindings.cjs +17 -6
  15. package/scripts/generateRestBindings.ts +22 -8
  16. package/src/__tests__/fixtures/c6.fixture.ts +74 -0
  17. package/src/__tests__/normalizeSingularRequest.test.ts +95 -0
  18. package/src/__tests__/sakila-db/C6.js +1487 -0
  19. package/src/__tests__/sakila-db/C6.test.ts +63 -0
  20. package/src/__tests__/sakila-db/C6.ts +2206 -0
  21. package/src/__tests__/sakila-db/sakila-data.sql +46444 -0
  22. package/src/__tests__/sakila-db/sakila-schema.sql +686 -0
  23. package/src/__tests__/sakila-db/sakila.mwb +0 -0
  24. package/src/__tests__/sakila.generated.test.ts +46 -0
  25. package/src/__tests__/sqlBuilders.complex.test.ts +134 -0
  26. package/src/__tests__/sqlBuilders.test.ts +121 -0
  27. package/src/api/convertForRequestBody.ts +1 -1
  28. package/src/api/executors/HttpExecutor.ts +14 -3
  29. package/src/api/executors/SqlExecutor.ts +14 -1
  30. package/src/api/orm/builders/AggregateBuilder.ts +3 -0
  31. package/src/api/orm/builders/ConditionBuilder.ts +34 -11
  32. package/src/api/orm/builders/PaginationBuilder.ts +10 -4
  33. package/src/api/orm/queries/SelectQueryBuilder.ts +3 -0
  34. package/src/api/orm/queries/UpdateQueryBuilder.ts +2 -1
  35. package/src/api/types/ormInterfaces.ts +3 -4
  36. package/src/api/utils/cacheManager.ts +1 -1
  37. package/src/api/utils/normalizeSingularRequest.ts +138 -0
  38. package/src/index.ts +1 -0
package/README.md CHANGED
@@ -566,7 +566,7 @@ export default Users;
566
566
 
567
567
  This project uses Git hooks to automate certain tasks:
568
568
 
569
- - **pre-commit**: Builds the project before pushing to ensure only working code is pushed
569
+ - **post-commit**: Builds the project before pushing to ensure only working code is pushed
570
570
  - **post-push**: Automatically publishes to npm when the version number changes
571
571
 
572
572
  To set up the Git hooks, run:
@@ -1,5 +1,6 @@
1
1
  import { Executor } from "../../executors/Executor";
2
2
  import { OrmGenerics } from "../../types/ormGenerics";
3
3
  export declare abstract class AggregateBuilder<G extends OrmGenerics> extends Executor<G> {
4
+ protected selectAliases: Set<string>;
4
5
  buildAggregateField(field: string | any[]): string;
5
6
  }
@@ -59,7 +59,7 @@ export type Pagination<T = any> = {
59
59
  };
60
60
  export type RequestGetPutDeleteBody<T extends {
61
61
  [key: string]: any;
62
- } = any> = {
62
+ } = any> = T | {
63
63
  SELECT?: SelectField<T>[];
64
64
  UPDATE?: Partial<T>;
65
65
  DELETE?: boolean;
@@ -83,7 +83,7 @@ export type RequestQueryBody<Method extends iRestMethods, T extends {
83
83
  [key: string]: any;
84
84
  } = {}, Overrides extends {
85
85
  [key: string]: any;
86
- } = {}> = Method extends 'GET' | 'PUT' | 'DELETE' ? iAPI<RequestGetPutDeleteBody<Modify<T, Overrides> & Custom>> : Method extends 'POST' ? iAPI<RequestGetPutDeleteBody<Modify<T, Overrides> & Custom> & Modify<T, Overrides> & Custom> : iAPI<Modify<T, Overrides> & Custom>;
86
+ } = {}> = Method extends 'GET' | 'PUT' | 'DELETE' ? iAPI<RequestGetPutDeleteBody<Modify<T, Overrides> & Custom>> : iAPI<Modify<T, Overrides> & Custom>;
87
87
  export interface iCacheAPI<ResponseDataType = any> {
88
88
  requestArgumentsSerialized: string;
89
89
  request: AxiosPromise<ResponseDataType>;
@@ -1,5 +1,5 @@
1
1
  import { AxiosPromise } from "axios";
2
- import { iCacheAPI } from "api/types/ormInterfaces";
2
+ import { iCacheAPI } from "../types/ormInterfaces";
3
3
  export declare let apiRequestCache: iCacheAPI[];
4
4
  export declare let userCustomClearCache: (() => void)[];
5
5
  interface iClearCache {
@@ -0,0 +1,10 @@
1
+ import { C6RestfulModel, iRestMethods, RequestQueryBody } from "../types/ormInterfaces";
2
+ /**
3
+ * Converts a singular T-shaped request into complex ORM format for GET/PUT/DELETE
4
+ * Enforces that all primary keys are present for singular syntax and that the table has PKs.
5
+ * Optionally accepts a previously removed primary key (key/value) to reconstruct WHERE.
6
+ */
7
+ export declare function normalizeSingularRequest<Method extends iRestMethods, T extends Record<string, any>, Custom extends Record<string, any> = {}, Overrides extends Record<string, any> = {}>(requestMethod: Method, request: RequestQueryBody<Method, T, Custom, Overrides>, restModel: C6RestfulModel<string, T, any>, removedPrimary?: {
8
+ key: string;
9
+ value: any;
10
+ }): RequestQueryBody<Method, T, Custom, Overrides>;
package/dist/index.cjs.js CHANGED
@@ -593,6 +593,95 @@ function removeInvalidKeys(request, c6Tables) {
593
593
  return intersection;
594
594
  }
595
595
 
596
+ /**
597
+ * Converts a singular T-shaped request into complex ORM format for GET/PUT/DELETE
598
+ * Enforces that all primary keys are present for singular syntax and that the table has PKs.
599
+ * Optionally accepts a previously removed primary key (key/value) to reconstruct WHERE.
600
+ */
601
+ function normalizeSingularRequest(requestMethod, request, restModel, removedPrimary) {
602
+ var _a, _b;
603
+ if (request == null || typeof request !== 'object')
604
+ return request;
605
+ var specialKeys = new Set([
606
+ C6Constants.SELECT,
607
+ C6Constants.UPDATE,
608
+ C6Constants.DELETE,
609
+ C6Constants.WHERE,
610
+ C6Constants.JOIN,
611
+ C6Constants.PAGINATION,
612
+ ]);
613
+ // Determine if the request is already complex (has any special key besides PAGINATION)
614
+ var keys = Object.keys(request);
615
+ var hasComplexKeys = keys.some(function (k) { return k !== C6Constants.PAGINATION && specialKeys.has(k); });
616
+ if (hasComplexKeys)
617
+ return request; // already complex
618
+ // We treat it as singular when it's not complex.
619
+ // For GET, PUT, DELETE only
620
+ if (!(requestMethod === C6Constants.GET || requestMethod === C6Constants.PUT || requestMethod === C6Constants.DELETE)) {
621
+ return request;
622
+ }
623
+ var pkShorts = Array.isArray(restModel.PRIMARY_SHORT) ? tslib.__spreadArray([], restModel.PRIMARY_SHORT, true) : [];
624
+ if (!pkShorts.length) {
625
+ throw new Error("Table (".concat(restModel.TABLE_NAME, ") has no primary key; singular request syntax is not allowed."));
626
+ }
627
+ // Build pk map from request + possibly removed primary key
628
+ var pkValues = {};
629
+ for (var _i = 0, pkShorts_1 = pkShorts; _i < pkShorts_1.length; _i++) {
630
+ var pk = pkShorts_1[_i];
631
+ var fromRequest = request[pk];
632
+ if (fromRequest !== undefined && fromRequest !== null) {
633
+ pkValues[pk] = fromRequest;
634
+ continue;
635
+ }
636
+ if (removedPrimary && removedPrimary.key === pk) {
637
+ pkValues[pk] = removedPrimary.value;
638
+ continue;
639
+ }
640
+ }
641
+ var missing = pkShorts.filter(function (pk) { return !(pk in pkValues); });
642
+ if (missing.length) {
643
+ throw new Error("Singular request requires all primary key(s) [".concat(pkShorts.join(', '), "] for table (").concat(restModel.TABLE_NAME, "). Missing: [").concat(missing.join(', '), "]"));
644
+ }
645
+ // Strip API metadata that should remain at root
646
+ var _c = request, dataInsertMultipleRows = _c.dataInsertMultipleRows, cacheResults = _c.cacheResults, fetchDependencies = _c.fetchDependencies, debug = _c.debug, success = _c.success, error = _c.error, rest = tslib.__rest(_c, ["dataInsertMultipleRows", "cacheResults", "fetchDependencies", "debug", "success", "error"]);
647
+ if (requestMethod === C6Constants.GET) {
648
+ var normalized_1 = {
649
+ WHERE: tslib.__assign({}, pkValues),
650
+ };
651
+ // Preserve pagination if any was added previously
652
+ if (request[C6Constants.PAGINATION]) {
653
+ normalized_1[C6Constants.PAGINATION] = request[C6Constants.PAGINATION];
654
+ }
655
+ return tslib.__assign(tslib.__assign({}, normalized_1), { dataInsertMultipleRows: dataInsertMultipleRows, cacheResults: cacheResults, fetchDependencies: fetchDependencies, debug: debug, success: success, error: error });
656
+ }
657
+ if (requestMethod === C6Constants.DELETE) {
658
+ var normalized_2 = (_a = {},
659
+ _a[C6Constants.DELETE] = true,
660
+ _a.WHERE = tslib.__assign({}, pkValues),
661
+ _a);
662
+ return tslib.__assign(tslib.__assign({}, normalized_2), { dataInsertMultipleRows: dataInsertMultipleRows, cacheResults: cacheResults, fetchDependencies: fetchDependencies, debug: debug, success: success, error: error });
663
+ }
664
+ // PUT
665
+ var updateBody = {};
666
+ for (var _d = 0, _e = Object.keys(rest); _d < _e.length; _d++) {
667
+ var k = _e[_d];
668
+ if (pkShorts.includes(k))
669
+ continue; // don't update PK columns
670
+ // Skip special request keys if any slipped through
671
+ if (specialKeys.has(k))
672
+ continue;
673
+ updateBody[k] = rest[k];
674
+ }
675
+ if (Object.keys(updateBody).length === 0) {
676
+ throw new Error("Singular PUT request for table (".concat(restModel.TABLE_NAME, ") must include at least one non-primary field to update."));
677
+ }
678
+ var normalized = (_b = {},
679
+ _b[C6Constants.UPDATE] = updateBody,
680
+ _b.WHERE = tslib.__assign({}, pkValues),
681
+ _b);
682
+ return tslib.__assign(tslib.__assign({}, normalized), { dataInsertMultipleRows: dataInsertMultipleRows, cacheResults: cacheResults, fetchDependencies: fetchDependencies, debug: debug, success: success, error: error });
683
+ }
684
+
596
685
  // do not remove entries from this array. It is used to track the progress of API requests.
597
686
  // position in array is important. Do not sort. To not add to begging.
598
687
  exports.apiRequestCache = [];
@@ -730,7 +819,7 @@ var HttpExecutor = /** @class */ (function (_super) {
730
819
  query[C6.PAGINATION][C6.LIMIT] = query[C6.PAGINATION][C6.LIMIT] || 100;
731
820
  }
732
821
  apiRequest = function () { return tslib.__awaiter(_this, void 0, void 0, function () {
733
- var _a, debug, _b, cacheResults, dataInsertMultipleRows, success, _c, fetchDependencies, _d, error, querySerialized, cacheResult, cachingConfirmed, cacheCheck, cacheCheck, addBackPK, apiResponse, returnGetNextPageFunction, restRequestUri, needsConditionOrPrimaryCheck, TABLES, primaryKey, removedPkValue_1, axiosActiveRequest;
822
+ var _a, debug, _b, cacheResults, dataInsertMultipleRows, success, _c, fetchDependencies, _d, error, querySerialized, cacheResult, cachingConfirmed, cacheCheck, cacheCheck, addBackPK, removedPrimaryKV, apiResponse, returnGetNextPageFunction, restRequestUri, needsConditionOrPrimaryCheck, TABLES, primaryKey, removedPkValue_1, axiosActiveRequest;
734
823
  var _e;
735
824
  var _this = this;
736
825
  var _f, _g, _h, _j, _k, _l;
@@ -850,6 +939,7 @@ var HttpExecutor = /** @class */ (function (_super) {
850
939
  && primaryKey in query) {
851
940
  restRequestUri += query[primaryKey] + '/';
852
941
  removedPkValue_1 = query[primaryKey];
942
+ removedPrimaryKV = { key: primaryKey, value: removedPkValue_1 };
853
943
  addBackPK = function () {
854
944
  query !== null && query !== void 0 ? query : (query = {});
855
945
  query[primaryKey] = removedPkValue_1;
@@ -878,9 +968,11 @@ var HttpExecutor = /** @class */ (function (_super) {
878
968
  var baseConfig = {
879
969
  withCredentials: withCredentials,
880
970
  };
971
+ // Normalize singular request (GET/PUT/DELETE) into complex ORM shape
972
+ var normalizedQuery = normalizeSingularRequest(requestMethod, query, restModel, removedPrimaryKV);
881
973
  switch (requestMethod) {
882
974
  case GET:
883
- return [tslib.__assign(tslib.__assign({}, baseConfig), { params: query })];
975
+ return [tslib.__assign(tslib.__assign({}, baseConfig), { params: normalizedQuery })];
884
976
  case POST:
885
977
  if (dataInsertMultipleRows !== undefined) {
886
978
  return [
@@ -890,9 +982,9 @@ var HttpExecutor = /** @class */ (function (_super) {
890
982
  }
891
983
  return [convert(query), baseConfig];
892
984
  case PUT:
893
- return [convert(query), baseConfig];
985
+ return [convert(normalizedQuery), baseConfig];
894
986
  case DELETE:
895
- return [tslib.__assign(tslib.__assign({}, baseConfig), { data: convert(query) })];
987
+ return [tslib.__assign(tslib.__assign({}, baseConfig), { data: convert(normalizedQuery) })];
896
988
  default:
897
989
  throw new Error("The request method (".concat(requestMethod, ") was not recognized."));
898
990
  }
@@ -1213,7 +1305,9 @@ function convertHexIfBinary(_col, val, columnDef) {
1213
1305
  var AggregateBuilder = /** @class */ (function (_super) {
1214
1306
  tslib.__extends(AggregateBuilder, _super);
1215
1307
  function AggregateBuilder() {
1216
- return _super !== null && _super.apply(this, arguments) || this;
1308
+ var _this = _super !== null && _super.apply(this, arguments) || this;
1309
+ _this.selectAliases = new Set();
1310
+ return _this;
1217
1311
  }
1218
1312
  AggregateBuilder.prototype.buildAggregateField = function (field) {
1219
1313
  var _this = this;
@@ -1241,6 +1335,7 @@ var AggregateBuilder = /** @class */ (function (_super) {
1241
1335
  expr = "".concat(F, "(").concat(argList, ")");
1242
1336
  }
1243
1337
  if (alias) {
1338
+ this.selectAliases.add(alias);
1244
1339
  expr += " AS ".concat(alias);
1245
1340
  }
1246
1341
  this.config.verbose && console.log("[SELECT] ".concat(expr));
@@ -1307,12 +1402,21 @@ var ConditionBuilder = /** @class */ (function (_super) {
1307
1402
  throw new Error("Method not implemented.");
1308
1403
  };
1309
1404
  ConditionBuilder.prototype.isTableReference = function (val) {
1310
- var _a, _b, _c;
1311
- if (typeof val !== 'string' || !val.includes('.'))
1405
+ var _a, _b, _c, _d;
1406
+ if (typeof val !== 'string')
1407
+ return false;
1408
+ // Support aggregate aliases (e.g., SELECT COUNT(x) AS cnt ... HAVING cnt > 1)
1409
+ if (!val.includes('.')) {
1410
+ var isIdentifier = /^[A-Za-z_][A-Za-z0-9_]*$/.test(val);
1411
+ // selectAliases is defined in AggregateBuilder
1412
+ if (isIdentifier && ((_a = this.selectAliases) === null || _a === void 0 ? void 0 : _a.has(val))) {
1413
+ return true;
1414
+ }
1312
1415
  return false;
1313
- var _d = val.split('.'), prefix = _d[0], column = _d[1];
1314
- var tableName = (_a = this.aliasMap[prefix]) !== null && _a !== void 0 ? _a : prefix;
1315
- var table = (_c = (_b = this.config.C6) === null || _b === void 0 ? void 0 : _b.TABLES) === null || _c === void 0 ? void 0 : _c[tableName];
1416
+ }
1417
+ var _e = val.split('.'), prefix = _e[0], column = _e[1];
1418
+ var tableName = (_b = this.aliasMap[prefix]) !== null && _b !== void 0 ? _b : prefix;
1419
+ var table = (_d = (_c = this.config.C6) === null || _c === void 0 ? void 0 : _c.TABLES) === null || _d === void 0 ? void 0 : _d[tableName];
1316
1420
  if (!table || !table.COLUMNS)
1317
1421
  return false;
1318
1422
  var fullKey = "".concat(tableName, ".").concat(column);
@@ -1325,8 +1429,14 @@ var ConditionBuilder = /** @class */ (function (_super) {
1325
1429
  }
1326
1430
  };
1327
1431
  ConditionBuilder.prototype.addParam = function (params, column, value) {
1328
- var _a, _b;
1329
- var columnDef = (_b = (_a = this.config.C6[column.split('.')[0]]) === null || _a === void 0 ? void 0 : _a.TYPE_VALIDATION) === null || _b === void 0 ? void 0 : _b[column];
1432
+ var _a, _b, _c;
1433
+ // Determine column definition from C6.TABLES to support type-aware conversions (e.g., BINARY hex -> Buffer)
1434
+ var columnDef;
1435
+ if (typeof column === 'string' && column.includes('.')) {
1436
+ var tableName = column.split('.', 2)[0];
1437
+ var table = (_b = (_a = this.config.C6) === null || _a === void 0 ? void 0 : _a.TABLES) === null || _b === void 0 ? void 0 : _b[tableName];
1438
+ columnDef = (_c = table === null || table === void 0 ? void 0 : table.TYPE_VALIDATION) === null || _c === void 0 ? void 0 : _c[column];
1439
+ }
1330
1440
  var val = convertHexIfBinary(column, value, columnDef);
1331
1441
  if (this.useNamedParams) {
1332
1442
  var key = "param".concat(Object.keys(params).length);
@@ -1370,7 +1480,7 @@ var ConditionBuilder = /** @class */ (function (_super) {
1370
1480
  var leftIsCol = _this.isColumnRef(column);
1371
1481
  var leftIsRef = _this.isTableReference(column);
1372
1482
  var rightIsCol = typeof value === 'string' && _this.isColumnRef(value);
1373
- if (!leftIsCol && !rightIsCol) {
1483
+ if (!leftIsCol && !leftIsRef && !rightIsCol) {
1374
1484
  throw new Error("Potential SQL injection detected: '".concat(column, " ").concat(op, " ").concat(value, "'"));
1375
1485
  }
1376
1486
  _this.validateOperator(op);
@@ -1437,21 +1547,22 @@ var ConditionBuilder = /** @class */ (function (_super) {
1437
1547
  var parts = [];
1438
1548
  var buildFromObject = function (obj, mode) {
1439
1549
  var subParts = [];
1440
- for (var _i = 0, _a = Object.entries(obj); _i < _a.length; _i++) {
1441
- var _b = _a[_i], k = _b[0], v = _b[1];
1442
- // numeric keys represent nested OR groups
1443
- if (!isNaN(Number(k))) {
1444
- var sub = _this.buildBooleanJoinedConditions(v, false, params);
1445
- if (sub)
1446
- subParts.push(sub);
1447
- continue;
1448
- }
1550
+ var entries = Object.entries(obj);
1551
+ var nonNumeric = entries.filter(function (_a) {
1552
+ var k = _a[0];
1553
+ return isNaN(Number(k));
1554
+ });
1555
+ var numeric = entries.filter(function (_a) {
1556
+ var k = _a[0];
1557
+ return !isNaN(Number(k));
1558
+ });
1559
+ var processEntry = function (k, v) {
1449
1560
  if (typeof v === 'object' && v !== null && Object.keys(v).length === 1) {
1450
- var _c = Object.entries(v)[0], op = _c[0], val = _c[1];
1561
+ var _a = Object.entries(v)[0], op = _a[0], val = _a[1];
1451
1562
  subParts.push(addCondition(k, op, val));
1452
1563
  }
1453
1564
  else if (Array.isArray(v) && v.length >= 2 && typeof v[0] === 'string') {
1454
- var _d = v, op = _d[0], val = _d[1];
1565
+ var _b = v, op = _b[0], val = _b[1];
1455
1566
  subParts.push(addCondition(k, op, val));
1456
1567
  }
1457
1568
  else if (typeof v === 'object' && v !== null) {
@@ -1462,6 +1573,18 @@ var ConditionBuilder = /** @class */ (function (_super) {
1462
1573
  else {
1463
1574
  subParts.push(addCondition(k, '=', v));
1464
1575
  }
1576
+ };
1577
+ // Process non-numeric keys first to preserve intuitive insertion order for params
1578
+ for (var _i = 0, nonNumeric_1 = nonNumeric; _i < nonNumeric_1.length; _i++) {
1579
+ var _a = nonNumeric_1[_i], k = _a[0], v = _a[1];
1580
+ processEntry(k, v);
1581
+ }
1582
+ // Then process numeric keys (treated as grouped OR conditions)
1583
+ for (var _b = 0, numeric_1 = numeric; _b < numeric_1.length; _b++) {
1584
+ var _c = numeric_1[_b]; _c[0]; var v = _c[1];
1585
+ var sub = _this.buildBooleanJoinedConditions(v, false, params);
1586
+ if (sub)
1587
+ subParts.push(sub);
1465
1588
  }
1466
1589
  return subParts.join(" ".concat(mode ? 'AND' : 'OR', " "));
1467
1590
  };
@@ -1598,13 +1721,12 @@ var PaginationBuilder = /** @class */ (function (_super) {
1598
1721
  */
1599
1722
  PaginationBuilder.prototype.buildPaginationClause = function (pagination) {
1600
1723
  var _this = this;
1601
- var _a;
1602
1724
  var sql = "";
1603
1725
  /* -------- ORDER BY -------- */
1604
1726
  if (pagination === null || pagination === void 0 ? void 0 : pagination[C6Constants.ORDER]) {
1605
1727
  var orderParts = [];
1606
- for (var _i = 0, _b = Object.entries(pagination[C6Constants.ORDER]); _i < _b.length; _i++) {
1607
- var _c = _b[_i], key = _c[0], val = _c[1];
1728
+ for (var _i = 0, _a = Object.entries(pagination[C6Constants.ORDER]); _i < _a.length; _i++) {
1729
+ var _b = _a[_i], key = _b[0], val = _b[1];
1608
1730
  // FUNCTION CALL: val is an array of args
1609
1731
  if (Array.isArray(val)) {
1610
1732
  var args = val
@@ -1623,9 +1745,16 @@ var PaginationBuilder = /** @class */ (function (_super) {
1623
1745
  /* -------- LIMIT / OFFSET -------- */
1624
1746
  if ((pagination === null || pagination === void 0 ? void 0 : pagination[C6Constants.LIMIT]) != null) {
1625
1747
  var lim = parseInt(pagination[C6Constants.LIMIT], 10);
1626
- var page = parseInt((_a = pagination[C6Constants.PAGE]) !== null && _a !== void 0 ? _a : 1, 10);
1627
- var offset = (page - 1) * lim;
1628
- sql += " LIMIT ".concat(offset, ", ").concat(lim);
1748
+ var pageRaw = pagination[C6Constants.PAGE];
1749
+ var pageParsed = parseInt(pageRaw !== null && pageRaw !== void 0 ? pageRaw : 1, 10);
1750
+ var page = isFinite(pageParsed) && pageParsed > 1 ? pageParsed : 1;
1751
+ if (page === 1) {
1752
+ sql += " LIMIT ".concat(lim);
1753
+ }
1754
+ else {
1755
+ var offset = (page - 1) * lim;
1756
+ sql += " LIMIT ".concat(offset, ", ").concat(lim);
1757
+ }
1629
1758
  }
1630
1759
  this.config.verbose && console.log("[PAGINATION] ".concat(sql.trim()));
1631
1760
  return sql;
@@ -1643,6 +1772,10 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
1643
1772
  var _a;
1644
1773
  if (isSubSelect === void 0) { isSubSelect = false; }
1645
1774
  this.aliasMap = {};
1775
+ // reset any previously collected SELECT aliases (from AggregateBuilder)
1776
+ // @ts-ignore
1777
+ if (this.selectAliases && this.selectAliases.clear)
1778
+ this.selectAliases.clear();
1646
1779
  var args = this.request;
1647
1780
  this.initAlias(table, args.JOIN);
1648
1781
  var params = this.useNamedParams ? {} : [];
@@ -1696,9 +1829,10 @@ var UpdateQueryBuilder = /** @class */ (function (_super) {
1696
1829
  if (!(C6C.UPDATE in this.request)) {
1697
1830
  throw new Error("No update data provided in the request.");
1698
1831
  }
1699
- var setClauses = Object.entries(this.request[C6C.UPDATE]).map(function (_a) {
1832
+ var setClauses = Object.entries(this.request[C6C.UPDATE])
1833
+ .map(function (_a) {
1700
1834
  var col = _a[0], val = _a[1];
1701
- return _this.addParam(params, col, val);
1835
+ return "`".concat(col, "` = ").concat(_this.addParam(params, col, val));
1702
1836
  });
1703
1837
  sql += " SET ".concat(setClauses.join(', '));
1704
1838
  if (args.WHERE) {
@@ -1730,6 +1864,14 @@ var SqlExecutor = /** @class */ (function (_super) {
1730
1864
  case 0:
1731
1865
  TABLE_NAME = this.config.restModel.TABLE_NAME;
1732
1866
  method = this.config.requestMethod;
1867
+ // Normalize singular T-shaped requests into complex ORM shape (GET/PUT/DELETE)
1868
+ try {
1869
+ this.request = normalizeSingularRequest(method, this.request, this.config.restModel, undefined);
1870
+ }
1871
+ catch (e) {
1872
+ // Surface normalization errors early
1873
+ throw e;
1874
+ }
1733
1875
  this.config.verbose && console.log("[SQL EXECUTOR] \u25B6\uFE0F Executing ".concat(method, " on table \"").concat(TABLE_NAME, "\""));
1734
1876
  this.config.verbose && console.log("[SQL EXECUTOR] \uD83E\uDDE9 Request:", this.request);
1735
1877
  _a = method;
@@ -2134,6 +2276,7 @@ exports.isLocal = isLocal;
2134
2276
  exports.isNode = isNode;
2135
2277
  exports.isTest = isTest;
2136
2278
  exports.isVerbose = isVerbose;
2279
+ exports.normalizeSingularRequest = normalizeSingularRequest;
2137
2280
  exports.onError = onError;
2138
2281
  exports.onSuccess = onSuccess;
2139
2282
  exports.removeInvalidKeys = removeInvalidKeys;