@carbonorm/carbonnode 3.10.0 → 4.0.0

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 (33) hide show
  1. package/dist/api/C6Constants.d.ts +20 -0
  2. package/dist/api/orm/builders/ConditionBuilder.d.ts +8 -0
  3. package/dist/api/orm/builders/JoinBuilder.d.ts +9 -0
  4. package/dist/api/orm/queries/PostQueryBuilder.d.ts +1 -1
  5. package/dist/api/restOrm.d.ts +4 -4
  6. package/dist/api/types/ormInterfaces.d.ts +32 -12
  7. package/dist/api/utils/cacheManager.d.ts +7 -8
  8. package/dist/index.cjs.js +566 -142
  9. package/dist/index.cjs.js.map +1 -1
  10. package/dist/index.esm.js +562 -141
  11. package/dist/index.esm.js.map +1 -1
  12. package/package.json +1 -1
  13. package/src/__tests__/cacheManager.test.ts +67 -0
  14. package/src/__tests__/expressServer.e2e.test.ts +104 -2
  15. package/src/__tests__/fixtures/c6.fixture.ts +5 -0
  16. package/src/__tests__/httpExecutorSingular.e2e.test.ts +35 -4
  17. package/src/__tests__/sakila-db/C6.js +1 -1
  18. package/src/__tests__/sakila-db/C6.ts +1 -1
  19. package/src/__tests__/sqlBuilders.complex.test.ts +85 -0
  20. package/src/__tests__/sqlBuilders.test.ts +28 -0
  21. package/src/api/C6Constants.ts +12 -2
  22. package/src/api/axiosInstance.ts +29 -0
  23. package/src/api/executors/HttpExecutor.ts +73 -97
  24. package/src/api/handlers/ExpressHandler.ts +30 -7
  25. package/src/api/orm/builders/ConditionBuilder.ts +227 -0
  26. package/src/api/orm/builders/JoinBuilder.ts +150 -1
  27. package/src/api/orm/queries/PostQueryBuilder.ts +4 -2
  28. package/src/api/orm/queries/SelectQueryBuilder.ts +5 -0
  29. package/src/api/orm/queries/UpdateQueryBuilder.ts +3 -1
  30. package/src/api/types/ormInterfaces.ts +32 -18
  31. package/src/api/utils/cacheManager.ts +75 -34
  32. package/src/api/utils/testHelpers.ts +5 -3
  33. package/src/variables/isNode.ts +1 -8
package/dist/index.esm.js CHANGED
@@ -35,10 +35,12 @@ var C6Constants = {
35
35
  DATE_FORMAT: 'DATE_FORMAT',
36
36
  DESC: 'DESC',
37
37
  DISTINCT: 'DISTINCT',
38
+ EXISTS: 'EXISTS',
38
39
  EXTRACT: 'EXTRACT',
39
40
  EQUAL: '=',
40
41
  EQUAL_NULL_SAFE: '<=>',
41
42
  FALSE: 'FALSE',
43
+ FORCE: 'FORCE',
42
44
  FULL_OUTER: 'FULL_OUTER',
43
45
  FROM_DAYS: 'FROM_DAYS',
44
46
  FROM_UNIXTIME: 'FROM_UNIXTIME',
@@ -54,6 +56,7 @@ var C6Constants = {
54
56
  HOUR_SECOND: 'HOUR_SECOND',
55
57
  HOUR_MINUTE: 'HOUR_MINUTE',
56
58
  IN: 'IN',
59
+ INDEX: 'INDEX',
57
60
  IS: 'IS',
58
61
  IS_NOT: 'IS_NOT',
59
62
  INNER: 'INNER',
@@ -71,6 +74,7 @@ var C6Constants = {
71
74
  MAKEDATE: 'MAKEDATE',
72
75
  MAKETIME: 'MAKETIME',
73
76
  MATCH_AGAINST: 'MATCH_AGAINST',
77
+ MBRCONTAINS: 'MBRContains',
74
78
  MONTHNAME: 'MONTHNAME',
75
79
  MICROSECOND: 'MICROSECOND',
76
80
  MINUTE: 'MINUTE',
@@ -86,8 +90,14 @@ var C6Constants = {
86
90
  NULL: 'NULL',
87
91
  ORDER: 'ORDER',
88
92
  OR: 'OR',
93
+ INDEX_HINTS: 'INDEX_HINTS',
94
+ FORCE_INDEX: 'FORCE INDEX',
95
+ USE_INDEX: 'USE INDEX',
96
+ IGNORE_INDEX: 'IGNORE INDEX',
89
97
  PAGE: 'PAGE',
90
98
  PAGINATION: 'PAGINATION',
99
+ POLYGON: 'POLYGON',
100
+ POINT: 'POINT',
91
101
  RIGHT_OUTER: 'RIGHT_OUTER',
92
102
  SECOND: 'SECOND',
93
103
  SECOND_MICROSECOND: 'SECOND_MICROSECOND',
@@ -294,8 +304,30 @@ axiosInstance.interceptors.request.use(function (config) {
294
304
  };
295
305
  }
296
306
  }
307
+ config.__carbonStart = performance.now();
297
308
  return config;
298
309
  });
310
+ axiosInstance.interceptors.response.use(function (response) {
311
+ var end = performance.now();
312
+ var start = response.config.__carbonStart;
313
+ response.__carbonTiming = {
314
+ start: start,
315
+ end: end,
316
+ duration: end - start,
317
+ };
318
+ return response;
319
+ }, function (error) {
320
+ if (error.config) {
321
+ var end = performance.now();
322
+ var start = error.config.__carbonStart;
323
+ error.__carbonTiming = {
324
+ start: start,
325
+ end: end,
326
+ duration: end - start,
327
+ };
328
+ }
329
+ throw error;
330
+ });
299
331
 
300
332
  function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler) {
301
333
  if (regexErrorHandler === void 0) { regexErrorHandler = alert; }
@@ -397,10 +429,7 @@ function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler)
397
429
 
398
430
  var isNode = function () {
399
431
  var _a;
400
- console.log('Checking if running in Node.js environment...');
401
- var isNodeEnv = typeof process !== 'undefined' && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
402
- console.log("Is Node.js environment: ".concat(isNodeEnv));
403
- return isNodeEnv;
432
+ return typeof process !== 'undefined' && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
404
433
  };
405
434
 
406
435
  /**
@@ -618,26 +647,64 @@ function removeInvalidKeys(request, c6Tables) {
618
647
  return intersection;
619
648
  }
620
649
 
621
- // do not remove entries from this array. It is used to track the progress of API requests.
622
- // position in array is important. Do not sort. To not add to begging.
623
- var apiRequestCache = [];
650
+ // -----------------------------------------------------------------------------
651
+ // Cache Storage
652
+ // -----------------------------------------------------------------------------
653
+ var apiRequestCache = new Map();
624
654
  var userCustomClearCache = [];
655
+ // -----------------------------------------------------------------------------
656
+ // Cache Key Generator (safe, fixed-size ~40 chars)
657
+ // -----------------------------------------------------------------------------
658
+ // -----------------------------------------------------------------------------
659
+ // Browser-safe deterministic hash (FNV-1a)
660
+ // -----------------------------------------------------------------------------
661
+ function fnv1a(str) {
662
+ var h = 0x811c9dc5;
663
+ for (var i = 0; i < str.length; i++) {
664
+ h ^= str.charCodeAt(i);
665
+ h = (h * 0x01000193) >>> 0;
666
+ }
667
+ return h.toString(16);
668
+ }
669
+ function makeCacheKey(method, tableName, requestData) {
670
+ var raw = JSON.stringify([method, tableName, requestData]);
671
+ return fnv1a(raw);
672
+ }
673
+ // -----------------------------------------------------------------------------
674
+ // Clear Cache (no shared-array bugs)
675
+ // -----------------------------------------------------------------------------
625
676
  function clearCache(props) {
626
- if (false === (props === null || props === void 0 ? void 0 : props.ignoreWarning)) {
627
- console.warn('The rest api clearCache should only be used with extreme care! Avoid using this in favor of using `cacheResults : boolean`.');
677
+ if (!(props === null || props === void 0 ? void 0 : props.ignoreWarning)) {
678
+ console.warn("The REST API clearCache should only be used with extreme care!");
679
+ }
680
+ for (var _i = 0, userCustomClearCache_1 = userCustomClearCache; _i < userCustomClearCache_1.length; _i++) {
681
+ var fn = userCustomClearCache_1[_i];
682
+ try {
683
+ fn();
684
+ }
685
+ catch (_a) { }
628
686
  }
629
- userCustomClearCache.map(function (f) { return 'function' === typeof f && f(); });
630
- userCustomClearCache = apiRequestCache = [];
687
+ apiRequestCache.clear();
631
688
  }
632
- function checkCache(cacheResult, requestMethod, tableName, request) {
633
- if (undefined === cacheResult) {
689
+ // -----------------------------------------------------------------------------
690
+ // Check Cache (dedupe via hashed key)
691
+ // -----------------------------------------------------------------------------
692
+ function checkCache(method, tableName, requestData) {
693
+ var key = makeCacheKey(method, tableName, requestData);
694
+ var cached = apiRequestCache.get(key);
695
+ if (!cached)
634
696
  return false;
635
- }
636
- console.groupCollapsed('%c API: The request on (' + tableName + ') is in cache. Returning the request Promise!', 'color: #0c0');
637
- console.log('%c ' + requestMethod + ' ' + tableName, 'color: #0c0');
638
- console.log('%c Request Data (note you may see the success and/or error prompt):', 'color: #0c0', request);
697
+ console.groupCollapsed("%c API cache hit for ".concat(method, " ").concat(tableName), "color:#0c0");
698
+ console.log("Request Data:", requestData);
639
699
  console.groupEnd();
640
- return cacheResult.request;
700
+ return cached.request;
701
+ }
702
+ // -----------------------------------------------------------------------------
703
+ // Store Cache Entry (drop-in compatible)
704
+ // -----------------------------------------------------------------------------
705
+ function setCache(method, tableName, requestData, cacheEntry) {
706
+ var key = makeCacheKey(method, tableName, requestData);
707
+ apiRequestCache.set(key, cacheEntry);
641
708
  }
642
709
 
643
710
  function sortAndSerializeQueryObject(tables, query) {
@@ -654,7 +721,10 @@ var HttpExecutor = /** @class */ (function (_super) {
654
721
  return _super !== null && _super.apply(this, arguments) || this;
655
722
  }
656
723
  HttpExecutor.prototype.isRestResponse = function (r) {
657
- return !!r && r.data != null && typeof r.data === 'object' && 'rest' in r.data;
724
+ return !!r
725
+ && r.data != null
726
+ && typeof r.data === 'object'
727
+ && Array.isArray(r.data.rest);
658
728
  };
659
729
  HttpExecutor.prototype.stripTableNameFromKeys = function (obj) {
660
730
  var _a;
@@ -757,19 +827,9 @@ var HttpExecutor = /** @class */ (function (_super) {
757
827
  console.log('request', this.request);
758
828
  console.groupEnd();
759
829
  }
760
- // an undefined query would indicate queryCallback returned undefined,
761
- // thus the request shouldn't fire as is in custom cache
762
- if (undefined === this.request || null === this.request) {
763
- if (isLocal()) {
764
- console.groupCollapsed("API: (".concat(requestMethod, ") (").concat(tableName, ") query undefined/null \u2192 returning null"));
765
- console.log('request', this.request);
766
- console.groupEnd();
767
- }
768
- return [2 /*return*/, null];
769
- }
770
830
  query = this.request;
771
831
  apiRequest = function () { return __awaiter(_this, void 0, void 0, function () {
772
- var _a, debug, _b, cacheResults, dataInsertMultipleRows, success, _c, fetchDependencies, _d, error, querySerialized, cacheResult, cachingConfirmed, cacheCheck, cacheCheck, apiResponse, returnGetNextPageFunction, restRequestUri, needsConditionOrPrimaryCheck, TABLES, primaryKeyList, primaryKeyFullyQualified, primaryKey, whereVal, whereIsEmpty, providedPrimary, primaryVal, axiosActiveRequest;
832
+ var _a, debug, _b, cacheResults, dataInsertMultipleRows, success, _c, fetchDependencies, _d, error, cachingConfirmed, cacheRequestData, querySerialized, cachedRequest, apiResponse, restRequestUri, needsConditionOrPrimaryCheck, TABLES, primaryKeyList, primaryKeyFullyQualified, primaryKey, whereVal, whereIsEmpty, providedPrimary, primaryVal, axiosActiveRequest_1;
773
833
  var _e;
774
834
  var _this = this;
775
835
  var _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
@@ -785,63 +845,37 @@ var HttpExecutor = /** @class */ (function (_super) {
785
845
  console.log('request', this.request);
786
846
  console.groupEnd();
787
847
  }
788
- querySerialized = sortAndSerializeQueryObject(tables, query !== null && query !== void 0 ? query : {});
789
- cacheResult = apiRequestCache.find(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
790
848
  cachingConfirmed = false;
791
- if (!(requestMethod === C6.GET)) return [3 /*break*/, 9];
792
- if (undefined === (query === null || query === void 0 ? void 0 : query[C6.PAGINATION])) {
793
- if (undefined === query || null === query) {
794
- query = {};
849
+ // determine if we need to paginate.
850
+ if (requestMethod === C6.GET) {
851
+ if (undefined === (query === null || query === void 0 ? void 0 : query[C6.PAGINATION])) {
852
+ if (undefined === query || null === query) {
853
+ query = {};
854
+ }
855
+ query[C6.PAGINATION] = {};
795
856
  }
796
- query[C6.PAGINATION] = {};
857
+ query[C6.PAGINATION][C6.PAGE] = query[C6.PAGINATION][C6.PAGE] || 1;
858
+ query[C6.PAGINATION][C6.LIMIT] = query[C6.PAGINATION][C6.LIMIT] || 100;
859
+ }
860
+ cacheRequestData = JSON.parse(JSON.stringify(query !== null && query !== void 0 ? query : {}));
861
+ querySerialized = sortAndSerializeQueryObject(tables, cacheRequestData !== null && cacheRequestData !== void 0 ? cacheRequestData : {});
862
+ cachedRequest = false;
863
+ if (cacheResults) {
864
+ cachedRequest = checkCache(requestMethod, tableName, cacheRequestData);
797
865
  }
798
- query[C6.PAGINATION][C6.PAGE] = query[C6.PAGINATION][C6.PAGE] || 1;
799
- query[C6.PAGINATION][C6.LIMIT] = query[C6.PAGINATION][C6.LIMIT] || 100;
800
- if (!(true === cacheResults)) return [3 /*break*/, 7];
801
- if (!(undefined !== cacheResult)) return [3 /*break*/, 6];
802
- _r.label = 1;
803
- case 1:
804
- cacheCheck = checkCache(cacheResult, requestMethod, tableName, this.request);
805
- if (!(false !== cacheCheck)) return [3 /*break*/, 3];
806
- return [4 /*yield*/, cacheCheck];
807
- case 2: return [2 /*return*/, (_r.sent()).data];
808
- case 3:
809
- ++query[C6.PAGINATION][C6.PAGE];
810
- querySerialized = sortAndSerializeQueryObject(tables, query !== null && query !== void 0 ? query : {});
811
- cacheResult = apiRequestCache.find(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
812
- _r.label = 4;
813
- case 4:
814
- if (undefined !== cacheResult) return [3 /*break*/, 1];
815
- _r.label = 5;
816
- case 5:
817
- if (debug && isLocal()) {
818
- toast.warning("DEVS: Request pages exhausted in cache; firing network.", toastOptionsDevs);
866
+ if (!cachedRequest) return [3 /*break*/, 2];
867
+ return [4 /*yield*/, cachedRequest];
868
+ case 1: return [2 /*return*/, (_r.sent()).data];
869
+ case 2:
870
+ if (cacheResults) {
871
+ cachingConfirmed = true;
819
872
  }
820
- _r.label = 6;
821
- case 6:
822
- cachingConfirmed = true;
823
- return [3 /*break*/, 8];
824
- case 7:
825
- if (debug && isLocal())
873
+ else if (debug && isLocal()) {
826
874
  toast.info("DEVS: Ignore cache was set to true.", toastOptionsDevs);
827
- _r.label = 8;
828
- case 8:
829
- if (debug && isLocal()) {
875
+ }
876
+ if (cacheResults && debug && isLocal()) {
830
877
  toast.success("DEVS: Request not in cache." + (requestMethod === C6.GET ? " Page (" + query[C6.PAGINATION][C6.PAGE] + ")" : ''), toastOptionsDevs);
831
878
  }
832
- return [3 /*break*/, 12];
833
- case 9:
834
- if (!cacheResults) return [3 /*break*/, 12];
835
- if (!cacheResult) return [3 /*break*/, 11];
836
- cacheCheck = checkCache(cacheResult, requestMethod, tableName, this.request);
837
- if (!(false !== cacheCheck)) return [3 /*break*/, 11];
838
- return [4 /*yield*/, cacheCheck];
839
- case 10: return [2 /*return*/, (_r.sent()).data];
840
- case 11:
841
- cachingConfirmed = true;
842
- _r.label = 12;
843
- case 12:
844
- returnGetNextPageFunction = false;
845
879
  restRequestUri = restURL + operatingTable + '/';
846
880
  needsConditionOrPrimaryCheck = (PUT === requestMethod || DELETE === requestMethod)
847
881
  && false === skipPrimaryCheck;
@@ -911,7 +945,7 @@ var HttpExecutor = /** @class */ (function (_super) {
911
945
  config: this.config,
912
946
  request: this.request
913
947
  });
914
- axiosActiveRequest = (_e = axios)[requestMethod.toLowerCase()].apply(_e, __spreadArray([restRequestUri], (function () {
948
+ axiosActiveRequest_1 = (_e = axios)[requestMethod.toLowerCase()].apply(_e, __spreadArray([restRequestUri], (function () {
915
949
  var convert = function (data) {
916
950
  return convertForRequestBody(data, fullTableList, C6, function (message) { return toast.error(message, toastOptions); });
917
951
  };
@@ -938,23 +972,30 @@ var HttpExecutor = /** @class */ (function (_super) {
938
972
  }
939
973
  })(), false));
940
974
  if (cachingConfirmed) {
941
- // push to cache so we do not repeat the request
942
- apiRequestCache.push({
975
+ setCache(requestMethod, tableName, cacheRequestData, {
943
976
  requestArgumentsSerialized: querySerialized,
944
- request: axiosActiveRequest
977
+ request: axiosActiveRequest_1,
945
978
  });
946
979
  }
947
980
  // returning the promise with this then is important for tests. todo - we could make that optional.
948
981
  // https://rapidapi.com/guides/axios-async-await
949
- return [2 /*return*/, axiosActiveRequest.then(function (response) { return __awaiter(_this, void 0, void 0, function () {
950
- var cacheIndex, callback, responseData_1, pageLimit, got, hasNext, cacheIndex, dependencies_1, fetchReferences_1, apiRequestPromises, _loop_1, tableToFetch;
982
+ return [2 /*return*/, axiosActiveRequest_1.then(function (response) { return __awaiter(_this, void 0, void 0, function () {
983
+ var hasNext, callback, responseData_1, pageLimit, got, dependencies_1, fetchReferences_1, apiRequestPromises, _loop_1, tableToFetch;
951
984
  var _this = this;
952
- var _a, _b, _c, _d;
953
- return __generator(this, function (_e) {
954
- switch (_e.label) {
985
+ var _a, _b, _c, _d, _e;
986
+ return __generator(this, function (_f) {
987
+ switch (_f.label) {
955
988
  case 0:
956
989
  // noinspection SuspiciousTypeOfGuard
957
990
  if (typeof response.data === 'string') {
991
+ if (cachingConfirmed) {
992
+ setCache(requestMethod, tableName, cacheRequestData, {
993
+ requestArgumentsSerialized: querySerialized,
994
+ request: axiosActiveRequest_1,
995
+ response: response,
996
+ final: true,
997
+ });
998
+ }
958
999
  if (isTest()) {
959
1000
  console.trace();
960
1001
  throw new Error('The response data was a string this typically indicated html was sent. Make sure all cookies (' + JSON.stringify(response.config.headers) + ') needed are present! (' + response.data + ')');
@@ -962,11 +1003,11 @@ var HttpExecutor = /** @class */ (function (_super) {
962
1003
  return [2 /*return*/, Promise.reject(response)];
963
1004
  }
964
1005
  if (cachingConfirmed) {
965
- cacheIndex = apiRequestCache.findIndex(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
966
- // TODO - currently nonthing is setting this correctly
967
- apiRequestCache[cacheIndex].final = false === returnGetNextPageFunction;
968
- // only cache get method requests
969
- apiRequestCache[cacheIndex].response = response;
1006
+ setCache(requestMethod, tableName, cacheRequestData, {
1007
+ requestArgumentsSerialized: querySerialized,
1008
+ request: axiosActiveRequest_1,
1009
+ response: response,
1010
+ });
970
1011
  }
971
1012
  this.runLifecycleHooks("afterExecution", {
972
1013
  config: this.config,
@@ -1017,17 +1058,18 @@ var HttpExecutor = /** @class */ (function (_super) {
1017
1058
  got = responseData_1.rest.length;
1018
1059
  hasNext = pageLimit !== 1 && got === pageLimit;
1019
1060
  if (hasNext) {
1020
- responseData_1.next = apiRequest; // there might be more
1061
+ responseData_1.next = apiRequest;
1021
1062
  }
1022
1063
  else {
1023
1064
  responseData_1.next = undefined; // short page => done
1024
1065
  }
1025
- // If you keep this flag, make it reflect reality:
1026
- returnGetNextPageFunction = hasNext;
1027
- // and fix cache ‘final’ flag to match:
1028
1066
  if (cachingConfirmed) {
1029
- cacheIndex = apiRequestCache.findIndex(function (c) { return c.requestArgumentsSerialized === querySerialized; });
1030
- apiRequestCache[cacheIndex].final = !hasNext;
1067
+ setCache(requestMethod, tableName, cacheRequestData, {
1068
+ requestArgumentsSerialized: querySerialized,
1069
+ request: axiosActiveRequest_1,
1070
+ response: response,
1071
+ final: !hasNext,
1072
+ });
1031
1073
  }
1032
1074
  if ((this.config.verbose || debug) && isLocal()) {
1033
1075
  console.groupCollapsed("API: Response (".concat(requestMethod, " ").concat(tableName, ") len (").concat((_b = responseData_1.rest) === null || _b === void 0 ? void 0 : _b.length, ") of (").concat((_c = query === null || query === void 0 ? void 0 : query[C6.PAGINATION]) === null || _c === void 0 ? void 0 : _c[C6.LIMIT], ")"));
@@ -1037,7 +1079,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1037
1079
  }
1038
1080
  if (!(fetchDependencies
1039
1081
  && 'number' === typeof fetchDependencies
1040
- && responseData_1.rest.length > 0)) return [3 /*break*/, 2];
1082
+ && ((_d = responseData_1.rest) === null || _d === void 0 ? void 0 : _d.length) > 0)) return [3 /*break*/, 2];
1041
1083
  console.groupCollapsed('%c API: Fetch Dependencies segment (' + requestMethod + ' ' + tableName + ')'
1042
1084
  + (fetchDependencies & eFetchDependencies.CHILDREN ? ' | (CHILDREN|REFERENCED) ' : '')
1043
1085
  + (fetchDependencies & eFetchDependencies.PARENTS ? ' | (PARENTS|REFERENCED_BY)' : '')
@@ -1108,7 +1150,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1108
1150
  }); });
1109
1151
  console.log('fetchReferences', fetchReferences_1);
1110
1152
  _loop_1 = function (tableToFetch) {
1111
- var _f;
1153
+ var _g;
1112
1154
  if (fetchDependencies & eFetchDependencies.C6ENTITY
1113
1155
  && 'string' === typeof tableName
1114
1156
  && tableName.endsWith("carbon_carbons")) {
@@ -1131,7 +1173,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1131
1173
  .split('_')
1132
1174
  .map(function (part) { return part.charAt(0).toUpperCase() + part.slice(1); })
1133
1175
  .join('_');
1134
- var RestApi = (_d = C6.ORM[ormKey]) !== null && _d !== void 0 ? _d : new Error("Fetch Dependencies could not find table (".concat(ormKey, ") in the set \u2209 [ ").concat(Object.keys(C6.ORM).join(', '), " ]"));
1176
+ var RestApi = (_e = C6.ORM[ormKey]) !== null && _e !== void 0 ? _e : new Error("Fetch Dependencies could not find table (".concat(ormKey, ") in the set \u2209 [ ").concat(Object.keys(C6.ORM).join(', '), " ]"));
1135
1177
  console.log('%c Fetch Dependencies will select (' + tableToFetch + ') using GET request', 'color: #33ccff');
1136
1178
  var nextFetchDependencies = eFetchDependencies.NONE;
1137
1179
  if (fetchDependencies & eFetchDependencies.RECURSIVE) {
@@ -1157,8 +1199,8 @@ var HttpExecutor = /** @class */ (function (_super) {
1157
1199
  console.log('RestApi object', RestApi);
1158
1200
  // this is a dynamic call to the rest api, any generated table may resolve with (RestApi)
1159
1201
  // todo - using value to avoid joins.... but. maybe this should be a parameterizable option -- think race conditions; its safer to join
1160
- apiRequestPromises.push(RestApi.Get((_f = {},
1161
- _f[C6.WHERE] = Object.keys(fetchReferences_1[tableToFetch]).reduce(function (sum, column) {
1202
+ apiRequestPromises.push(RestApi.Get((_g = {},
1203
+ _g[C6.WHERE] = Object.keys(fetchReferences_1[tableToFetch]).reduce(function (sum, column) {
1162
1204
  fetchReferences_1[tableToFetch][column] = fetchReferences_1[tableToFetch][column].flat(Infinity);
1163
1205
  if (0 === fetchReferences_1[tableToFetch][column].length) {
1164
1206
  console.warn('The column (' + column + ') was not found in the response data. We will not fetch.', responseData_1);
@@ -1171,8 +1213,8 @@ var HttpExecutor = /** @class */ (function (_super) {
1171
1213
  ];
1172
1214
  return sum;
1173
1215
  }, {}),
1174
- _f.fetchDependencies = nextFetchDependencies,
1175
- _f)));
1216
+ _g.fetchDependencies = nextFetchDependencies,
1217
+ _g)));
1176
1218
  };
1177
1219
  for (tableToFetch in fetchReferences_1) {
1178
1220
  _loop_1(tableToFetch);
@@ -1180,7 +1222,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1180
1222
  console.groupEnd();
1181
1223
  return [4 /*yield*/, Promise.all(apiRequestPromises)];
1182
1224
  case 1:
1183
- _e.sent();
1225
+ _f.sent();
1184
1226
  apiRequestPromises.map(function (promise) { return __awaiter(_this, void 0, void 0, function () {
1185
1227
  var _a, _b;
1186
1228
  return __generator(this, function (_c) {
@@ -1198,8 +1240,16 @@ var HttpExecutor = /** @class */ (function (_super) {
1198
1240
  }
1199
1241
  });
1200
1242
  }); });
1201
- _e.label = 2;
1243
+ _f.label = 2;
1202
1244
  case 2:
1245
+ if (cachingConfirmed && hasNext === undefined) {
1246
+ setCache(requestMethod, tableName, cacheRequestData, {
1247
+ requestArgumentsSerialized: querySerialized,
1248
+ request: axiosActiveRequest_1,
1249
+ response: response,
1250
+ final: true,
1251
+ });
1252
+ }
1203
1253
  if (debug && isLocal()) {
1204
1254
  toast.success("DEVS: (" + requestMethod + ") request complete.", toastOptionsDevs);
1205
1255
  }
@@ -1210,16 +1260,12 @@ var HttpExecutor = /** @class */ (function (_super) {
1210
1260
  }); }).then(function (response) { return response.data; })]; // this escapes from axios context
1211
1261
  }
1212
1262
  catch (throwableError) {
1213
- if (isTest()) {
1214
- throw new Error(JSON.stringify(throwableError));
1215
- }
1216
1263
  console.groupCollapsed('%c API: An error occurred in the try catch block. returning null!', 'color: #ff0000');
1217
1264
  console.log('%c ' + requestMethod + ' ' + tableName, 'color: #A020F0');
1218
- console.warn(throwableError);
1265
+ console.error(throwableError);
1219
1266
  console.trace();
1220
1267
  console.groupEnd();
1221
- TestRestfulResponse(throwableError, success, error);
1222
- return [2 /*return*/, null];
1268
+ throw new Error(JSON.stringify(throwableError));
1223
1269
  }
1224
1270
  return [2 /*return*/];
1225
1271
  }
@@ -1448,7 +1494,8 @@ var stContains = function (envelope, shape) {
1448
1494
  var ConditionBuilder = /** @class */ (function (_super) {
1449
1495
  __extends(ConditionBuilder, _super);
1450
1496
  function ConditionBuilder() {
1451
- var _this = _super !== null && _super.apply(this, arguments) || this;
1497
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1498
+ var _this = _super.apply(this, arguments) || this;
1452
1499
  _this.aliasMap = {};
1453
1500
  _this.derivedAliases = new Set();
1454
1501
  _this.BOOLEAN_OPERATORS = new Map([
@@ -1487,8 +1534,16 @@ var ConditionBuilder = /** @class */ (function (_super) {
1487
1534
  [C6C.BETWEEN, C6C.BETWEEN],
1488
1535
  ['BETWEEN', C6C.BETWEEN],
1489
1536
  ['NOT BETWEEN', 'NOT BETWEEN'],
1537
+ [C6C.EXISTS, C6C.EXISTS],
1538
+ ['EXISTS', C6C.EXISTS],
1539
+ ['NOT EXISTS', 'NOT EXISTS'],
1490
1540
  [C6C.MATCH_AGAINST, C6C.MATCH_AGAINST],
1491
1541
  ]);
1542
+ _this.BOOLEAN_FUNCTION_KEYS = new Set([
1543
+ (_c = (_b = (_a = C6C.ST_CONTAINS) === null || _a === void 0 ? void 0 : _a.toUpperCase) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : 'ST_CONTAINS',
1544
+ (_f = (_e = (_d = C6C.ST_WITHIN) === null || _d === void 0 ? void 0 : _d.toUpperCase) === null || _e === void 0 ? void 0 : _e.call(_d)) !== null && _f !== void 0 ? _f : 'ST_WITHIN',
1545
+ (_j = (_h = (_g = C6C.MBRCONTAINS) === null || _g === void 0 ? void 0 : _g.toUpperCase) === null || _h === void 0 ? void 0 : _h.call(_g)) !== null && _j !== void 0 ? _j : 'MBRCONTAINS',
1546
+ ]);
1492
1547
  return _this;
1493
1548
  }
1494
1549
  ConditionBuilder.prototype.initAlias = function (baseTable, joins) {
@@ -1723,6 +1778,9 @@ var ConditionBuilder = /** @class */ (function (_super) {
1723
1778
  if (operand === null || typeof operand === 'number' || typeof operand === 'boolean') {
1724
1779
  return { sql: asParam(operand), isReference: false, isExpression: false, isSubSelect: false };
1725
1780
  }
1781
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(operand)) {
1782
+ return { sql: asParam(operand), isReference: false, isExpression: false, isSubSelect: false };
1783
+ }
1726
1784
  if (typeof operand === 'string') {
1727
1785
  if (this.isTableReference(operand) || this.isColumnRef(operand)) {
1728
1786
  return { sql: operand, isReference: true, isExpression: false, isSubSelect: false };
@@ -1766,10 +1824,185 @@ var ConditionBuilder = /** @class */ (function (_super) {
1766
1824
  }
1767
1825
  throw new Error('Unsupported operand type in SQL expression.');
1768
1826
  };
1827
+ ConditionBuilder.prototype.isPlainArrayLiteral = function (value) {
1828
+ var _this = this;
1829
+ if (!Array.isArray(value))
1830
+ return false;
1831
+ return value.every(function (item) {
1832
+ if (item === null)
1833
+ return true;
1834
+ var type = typeof item;
1835
+ if (type === 'string' || type === 'number' || type === 'boolean')
1836
+ return true;
1837
+ if (Array.isArray(item))
1838
+ return _this.isPlainArrayLiteral(item);
1839
+ if (item && typeof item === 'object')
1840
+ return _this.isPlainObjectLiteral(item);
1841
+ return false;
1842
+ });
1843
+ };
1844
+ ConditionBuilder.prototype.isPlainObjectLiteral = function (value) {
1845
+ var _this = this;
1846
+ if (!value || typeof value !== 'object' || Array.isArray(value))
1847
+ return false;
1848
+ if (value instanceof Date)
1849
+ return false;
1850
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(value))
1851
+ return false;
1852
+ var normalized = value instanceof Map ? Object.fromEntries(value) : value;
1853
+ if (C6C.SUBSELECT in normalized)
1854
+ return false;
1855
+ var entries = Object.entries(normalized);
1856
+ if (entries.length === 0)
1857
+ return true;
1858
+ if (entries.some(function (_a) {
1859
+ var key = _a[0];
1860
+ return _this.isOperator(key) || _this.BOOLEAN_OPERATORS.has(key);
1861
+ })) {
1862
+ return false;
1863
+ }
1864
+ if (entries.some(function (_a) {
1865
+ var key = _a[0];
1866
+ return typeof key === 'string' && (_this.isColumnRef(key) || key.includes('.'));
1867
+ })) {
1868
+ return false;
1869
+ }
1870
+ return true;
1871
+ };
1872
+ ConditionBuilder.prototype.serializeUpdateValue = function (value, params, contextColumn) {
1873
+ var normalized = value instanceof Map ? Object.fromEntries(value) : value;
1874
+ if (this.isPlainArrayLiteral(normalized) || this.isPlainObjectLiteral(normalized)) {
1875
+ return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
1876
+ }
1877
+ var _a = this.serializeOperand(normalized, params, contextColumn), sql = _a.sql, isReference = _a.isReference, isExpression = _a.isExpression, isSubSelect = _a.isSubSelect;
1878
+ if (!isReference && !isExpression && !isSubSelect && typeof normalized === 'object' && normalized !== null) {
1879
+ throw new Error('Unsupported operand type in SQL expression.');
1880
+ }
1881
+ return sql;
1882
+ };
1883
+ ConditionBuilder.prototype.ensurePlainObject = function (value) {
1884
+ if (value instanceof Map) {
1885
+ return Object.fromEntries(value);
1886
+ }
1887
+ return value;
1888
+ };
1889
+ ConditionBuilder.prototype.resolveExistsInnerColumn = function (subRequest, provided) {
1890
+ var _a, _b;
1891
+ if (provided) {
1892
+ if (typeof provided !== 'string' || provided.trim() === '') {
1893
+ throw new Error('EXISTS correlation column must be a non-empty string.');
1894
+ }
1895
+ return provided;
1896
+ }
1897
+ var selectClause = this.ensurePlainObject(subRequest === null || subRequest === void 0 ? void 0 : subRequest[C6C.SELECT]);
1898
+ if (Array.isArray(selectClause) && selectClause.length > 0) {
1899
+ var candidate = selectClause[0];
1900
+ if (typeof candidate === 'string' && candidate.trim() !== '') {
1901
+ return candidate;
1902
+ }
1903
+ }
1904
+ var fromTable = subRequest === null || subRequest === void 0 ? void 0 : subRequest[C6C.FROM];
1905
+ if (typeof fromTable === 'string' && fromTable.trim() !== '') {
1906
+ var table = (_b = (_a = this.config.C6) === null || _a === void 0 ? void 0 : _a.TABLES) === null || _b === void 0 ? void 0 : _b[fromTable.trim()];
1907
+ var primary = table === null || table === void 0 ? void 0 : table.PRIMARY;
1908
+ if (Array.isArray(primary) && primary.length > 0) {
1909
+ return String(primary[0]);
1910
+ }
1911
+ }
1912
+ throw new Error('EXISTS requires a correlation column to be provided or inferable from the subselect.');
1913
+ };
1914
+ ConditionBuilder.prototype.normalizeExistsSpec = function (spec) {
1915
+ var normalized = this.ensurePlainObject(spec);
1916
+ if (!Array.isArray(normalized) || normalized.length < 2) {
1917
+ throw new Error('EXISTS expects an array like [outerColumn, subselect, innerColumn?].');
1918
+ }
1919
+ var outerRaw = normalized[0], payloadRaw = normalized[1], innerRaw = normalized[2];
1920
+ if (typeof outerRaw !== 'string' || outerRaw.trim() === '') {
1921
+ throw new Error('EXISTS requires the first element to be an outer column reference string.');
1922
+ }
1923
+ var payload = this.ensurePlainObject(payloadRaw);
1924
+ var subSelect;
1925
+ if (payload && typeof payload === 'object' && C6C.SUBSELECT in payload) {
1926
+ subSelect = this.ensurePlainObject(payload[C6C.SUBSELECT]);
1927
+ }
1928
+ else if (payload && typeof payload === 'object') {
1929
+ subSelect = payload;
1930
+ }
1931
+ else {
1932
+ throw new Error('EXISTS requires a subselect payload as the second element.');
1933
+ }
1934
+ if (!subSelect || typeof subSelect !== 'object') {
1935
+ throw new Error('EXISTS subselect payload must be an object.');
1936
+ }
1937
+ var innerColumn = typeof innerRaw === 'string' ? innerRaw : undefined;
1938
+ return {
1939
+ outerColumn: outerRaw,
1940
+ subRequest: __assign({}, subSelect),
1941
+ innerColumn: innerColumn,
1942
+ };
1943
+ };
1944
+ ConditionBuilder.prototype.buildExistsExpression = function (spec, operator, params) {
1945
+ var _a, _b, _c, _d;
1946
+ var _this = this;
1947
+ var _e;
1948
+ var _f = this.normalizeExistsSpec(spec), outerColumn = _f.outerColumn, subRequest = _f.subRequest, innerColumn = _f.innerColumn;
1949
+ var fromTableRaw = subRequest[C6C.FROM];
1950
+ if (typeof fromTableRaw !== 'string' || fromTableRaw.trim() === '') {
1951
+ throw new Error('EXISTS subselect requires a table specified with C6C.FROM.');
1952
+ }
1953
+ var fromTable = fromTableRaw.trim();
1954
+ this.assertValidIdentifier(outerColumn, 'EXISTS correlation column');
1955
+ var correlationColumn = this.resolveExistsInnerColumn(subRequest, innerColumn);
1956
+ if (!this.isColumnRef(correlationColumn) && !this.isTableReference(correlationColumn)) {
1957
+ throw new Error("Unknown column reference '".concat(correlationColumn, "' used in EXISTS subquery correlation column."));
1958
+ }
1959
+ var existingWhereRaw = this.ensurePlainObject(subRequest[C6C.WHERE]);
1960
+ var correlationCondition = (_a = {}, _a[correlationColumn] = [C6C.EQUAL, outerColumn], _a);
1961
+ var normalizedExistingWhere = existingWhereRaw && typeof existingWhereRaw === 'object'
1962
+ ? Array.isArray(existingWhereRaw)
1963
+ ? existingWhereRaw.slice()
1964
+ : __assign({}, existingWhereRaw)
1965
+ : existingWhereRaw;
1966
+ var hasExistingWhere = Array.isArray(normalizedExistingWhere)
1967
+ ? normalizedExistingWhere.length > 0
1968
+ : normalizedExistingWhere && typeof normalizedExistingWhere === 'object'
1969
+ ? Object.keys(normalizedExistingWhere).length > 0
1970
+ : normalizedExistingWhere != null;
1971
+ var whereClause;
1972
+ if (!hasExistingWhere) {
1973
+ whereClause = correlationCondition;
1974
+ }
1975
+ else if (normalizedExistingWhere && typeof normalizedExistingWhere === 'object' &&
1976
+ Object.keys(normalizedExistingWhere).some(function (key) { return _this.BOOLEAN_OPERATORS.has(key); })) {
1977
+ whereClause = (_b = {}, _b[C6C.AND] = [normalizedExistingWhere, correlationCondition], _b);
1978
+ }
1979
+ else if (normalizedExistingWhere && typeof normalizedExistingWhere === 'object') {
1980
+ whereClause = __assign(__assign({}, normalizedExistingWhere), correlationCondition);
1981
+ }
1982
+ else {
1983
+ whereClause = (_c = {}, _c[C6C.AND] = [normalizedExistingWhere, correlationCondition], _c);
1984
+ }
1985
+ var subRequestWithCorrelation = __assign(__assign({}, subRequest), (_d = {}, _d[C6C.FROM] = fromTable, _d[C6C.WHERE] = whereClause, _d[C6C.SELECT] = (_e = subRequest[C6C.SELECT]) !== null && _e !== void 0 ? _e : ['1'], _d));
1986
+ var buildScalarSubSelect = this.buildScalarSubSelect;
1987
+ if (typeof buildScalarSubSelect !== 'function') {
1988
+ throw new Error('EXISTS operator requires SelectQueryBuilder context.');
1989
+ }
1990
+ var scalar = buildScalarSubSelect.call(this, subRequestWithCorrelation, params);
1991
+ var keyword = operator === 'NOT EXISTS' ? 'NOT EXISTS' : C6C.EXISTS;
1992
+ return "".concat(keyword, " ").concat(scalar);
1993
+ };
1769
1994
  ConditionBuilder.prototype.buildOperatorExpression = function (op, rawOperands, params, contextColumn) {
1770
1995
  var _a, _b;
1771
1996
  var _this = this;
1772
1997
  var operator = this.formatOperator(op);
1998
+ if (operator === C6C.EXISTS || operator === 'NOT EXISTS') {
1999
+ var operands_1 = Array.isArray(rawOperands) ? rawOperands : [rawOperands];
2000
+ if (!operands_1.length) {
2001
+ throw new Error("".concat(operator, " requires at least one subselect specification."));
2002
+ }
2003
+ var clauses = operands_1.map(function (spec) { return _this.buildExistsExpression(spec, operator, params); });
2004
+ return this.joinBooleanParts(clauses, 'AND');
2005
+ }
1773
2006
  if (operator === C6C.MATCH_AGAINST) {
1774
2007
  if (!Array.isArray(rawOperands) || rawOperands.length !== 2) {
1775
2008
  throw new Error('MATCH_AGAINST requires an array of two operands.');
@@ -1879,6 +2112,18 @@ var ConditionBuilder = /** @class */ (function (_super) {
1879
2112
  if (value instanceof Map) {
1880
2113
  value = Object.fromEntries(value);
1881
2114
  }
2115
+ if (typeof column === 'string') {
2116
+ var normalizedColumn = column.trim().toUpperCase();
2117
+ if (this.BOOLEAN_FUNCTION_KEYS.has(normalizedColumn)) {
2118
+ if (!Array.isArray(value)) {
2119
+ throw new Error("".concat(column, " expects an array of arguments."));
2120
+ }
2121
+ return this.buildFunctionCall(column, value, params);
2122
+ }
2123
+ }
2124
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(value)) {
2125
+ return this.buildOperatorExpression(C6C.EQUAL, [column, value], params, column);
2126
+ }
1882
2127
  if (Array.isArray(value)) {
1883
2128
  if (value.length >= 2 && typeof value[0] === 'string') {
1884
2129
  var op = value[0], rest = value.slice(1);
@@ -2051,6 +2296,143 @@ var JoinBuilder = /** @class */ (function (_super) {
2051
2296
  JoinBuilder.prototype.createSelectBuilder = function (_request) {
2052
2297
  throw new Error('Subclasses must implement createSelectBuilder to support derived table serialization.');
2053
2298
  };
2299
+ JoinBuilder.prototype.resetIndexHints = function () {
2300
+ this.indexHintCache = undefined;
2301
+ };
2302
+ JoinBuilder.prototype.normalizeIndexHintKey = function (key) {
2303
+ return key
2304
+ .replace(/`/g, '')
2305
+ .replace(/_/g, ' ')
2306
+ .trim()
2307
+ .replace(/\s+/g, ' ')
2308
+ .toUpperCase();
2309
+ };
2310
+ JoinBuilder.prototype.normalizeHintTargetKey = function (target) {
2311
+ return target.replace(/`/g, '').trim();
2312
+ };
2313
+ JoinBuilder.prototype.hasIndexHintKeys = function (obj) {
2314
+ var _this = this;
2315
+ var keys = Object.keys(obj !== null && obj !== void 0 ? obj : {});
2316
+ if (!keys.length)
2317
+ return false;
2318
+ var forceKey = this.normalizeIndexHintKey(C6C.FORCE_INDEX);
2319
+ var useKey = this.normalizeIndexHintKey(C6C.USE_INDEX);
2320
+ var ignoreKey = this.normalizeIndexHintKey(C6C.IGNORE_INDEX);
2321
+ return keys.some(function (key) {
2322
+ var normalized = _this.normalizeIndexHintKey(key);
2323
+ return normalized === forceKey || normalized === useKey || normalized === ignoreKey;
2324
+ });
2325
+ };
2326
+ JoinBuilder.prototype.normalizeHintSpec = function (spec) {
2327
+ var _a;
2328
+ if (spec instanceof Map) {
2329
+ spec = Object.fromEntries(spec);
2330
+ }
2331
+ if (Array.isArray(spec) || typeof spec === 'string') {
2332
+ return _a = {}, _a[C6C.FORCE_INDEX] = spec, _a;
2333
+ }
2334
+ if (!spec || typeof spec !== 'object') {
2335
+ return undefined;
2336
+ }
2337
+ if (!this.hasIndexHintKeys(spec)) {
2338
+ return undefined;
2339
+ }
2340
+ return spec;
2341
+ };
2342
+ JoinBuilder.prototype.formatIndexHintClause = function (spec) {
2343
+ var normalizedSpec = this.normalizeHintSpec(spec);
2344
+ if (!normalizedSpec)
2345
+ return '';
2346
+ var clauses = [];
2347
+ var forceKey = this.normalizeIndexHintKey(C6C.FORCE_INDEX);
2348
+ var useKey = this.normalizeIndexHintKey(C6C.USE_INDEX);
2349
+ var ignoreKey = this.normalizeIndexHintKey(C6C.IGNORE_INDEX);
2350
+ var pushClause = function (keyword, rawValue) {
2351
+ var values = Array.isArray(rawValue) ? rawValue : [rawValue];
2352
+ var indexes = values
2353
+ .map(function (value) { return String(value !== null && value !== void 0 ? value : '').trim(); })
2354
+ .filter(Boolean)
2355
+ .map(function (value) { return "`".concat(value.replace(/`/g, '``'), "`"); });
2356
+ if (!indexes.length)
2357
+ return;
2358
+ clauses.push("".concat(keyword, " (").concat(indexes.join(', '), ")"));
2359
+ };
2360
+ for (var _i = 0, _a = Object.entries(normalizedSpec); _i < _a.length; _i++) {
2361
+ var _b = _a[_i], key = _b[0], rawValue = _b[1];
2362
+ var normalizedKey = this.normalizeIndexHintKey(key);
2363
+ if (normalizedKey === forceKey) {
2364
+ pushClause('FORCE INDEX', rawValue);
2365
+ }
2366
+ else if (normalizedKey === useKey) {
2367
+ pushClause('USE INDEX', rawValue);
2368
+ }
2369
+ else if (normalizedKey === ignoreKey) {
2370
+ pushClause('IGNORE INDEX', rawValue);
2371
+ }
2372
+ }
2373
+ return clauses.join(' ');
2374
+ };
2375
+ JoinBuilder.prototype.normalizeIndexHints = function (raw) {
2376
+ var _this = this;
2377
+ if (raw instanceof Map) {
2378
+ raw = Object.fromEntries(raw);
2379
+ }
2380
+ var cache = new Map();
2381
+ var addEntry = function (target, spec) {
2382
+ var clause = _this.formatIndexHintClause(spec);
2383
+ if (!clause)
2384
+ return;
2385
+ var normalizedTarget = target === '__base__'
2386
+ ? '__base__'
2387
+ : _this.normalizeHintTargetKey(target);
2388
+ cache.set(normalizedTarget, clause);
2389
+ };
2390
+ if (Array.isArray(raw) || typeof raw === 'string') {
2391
+ addEntry('__base__', raw);
2392
+ }
2393
+ else if (raw && typeof raw === 'object') {
2394
+ if (this.hasIndexHintKeys(raw)) {
2395
+ addEntry('__base__', raw);
2396
+ }
2397
+ else {
2398
+ for (var _i = 0, _a = Object.entries(raw); _i < _a.length; _i++) {
2399
+ var _b = _a[_i], key = _b[0], value = _b[1];
2400
+ var normalizedKey = this.normalizeHintTargetKey(key);
2401
+ if (!normalizedKey)
2402
+ continue;
2403
+ addEntry(normalizedKey, value);
2404
+ }
2405
+ }
2406
+ }
2407
+ return cache.size ? cache : undefined;
2408
+ };
2409
+ JoinBuilder.prototype.getIndexHintClause = function (table, alias) {
2410
+ var _a;
2411
+ if (!this.indexHintCache) {
2412
+ var rawHints = (_a = this.request) === null || _a === void 0 ? void 0 : _a[C6C.INDEX_HINTS];
2413
+ this.indexHintCache = this.normalizeIndexHints(rawHints);
2414
+ }
2415
+ var hints = this.indexHintCache;
2416
+ if (!hints || hints.size === 0)
2417
+ return '';
2418
+ var normalizedTable = this.normalizeHintTargetKey(table);
2419
+ var normalizedAlias = alias ? this.normalizeHintTargetKey(alias) : undefined;
2420
+ var candidates = [
2421
+ normalizedAlias,
2422
+ normalizedAlias ? "".concat(normalizedTable, " ").concat(normalizedAlias) : undefined,
2423
+ normalizedTable,
2424
+ '__base__',
2425
+ ];
2426
+ for (var _i = 0, candidates_1 = candidates; _i < candidates_1.length; _i++) {
2427
+ var candidate = candidates_1[_i];
2428
+ if (!candidate)
2429
+ continue;
2430
+ var clause = hints.get(candidate);
2431
+ if (clause)
2432
+ return clause;
2433
+ }
2434
+ return '';
2435
+ };
2054
2436
  JoinBuilder.prototype.buildJoinClauses = function (joinArgs, params) {
2055
2437
  var sql = '';
2056
2438
  var joinTypeEntries = joinArgs instanceof Map
@@ -2114,7 +2496,9 @@ var JoinBuilder = /** @class */ (function (_super) {
2114
2496
  if (alias) {
2115
2497
  this_1.registerAlias(alias, table);
2116
2498
  }
2117
- var joinSql = alias ? "`".concat(table, "` AS `").concat(alias, "`") : "`".concat(table, "`");
2499
+ var hintClause = this_1.getIndexHintClause(table, alias);
2500
+ var baseJoinSql = alias ? "`".concat(table, "` AS `").concat(alias, "`") : "`".concat(table, "`");
2501
+ var joinSql = hintClause ? "".concat(baseJoinSql, " ").concat(hintClause) : baseJoinSql;
2118
2502
  var onClause = this_1.buildBooleanJoinedConditions(conditions, true, params);
2119
2503
  sql += " ".concat(joinKind, " JOIN ").concat(joinSql);
2120
2504
  if (onClause) {
@@ -2268,6 +2652,7 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
2268
2652
  // @ts-ignore
2269
2653
  if (this.selectAliases && this.selectAliases.clear)
2270
2654
  this.selectAliases.clear();
2655
+ this.resetIndexHints();
2271
2656
  var args = this.request;
2272
2657
  this.initAlias(table, args.JOIN);
2273
2658
  var params = this.useNamedParams ? {} : [];
@@ -2276,6 +2661,10 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
2276
2661
  .map(function (f) { return _this.buildAggregateField(f, params); })
2277
2662
  .join(', ');
2278
2663
  var sql = "SELECT ".concat(selectFields, " FROM `").concat(table, "`");
2664
+ var baseIndexHint = this.getIndexHintClause(table);
2665
+ if (baseIndexHint) {
2666
+ sql += " ".concat(baseIndexHint);
2667
+ }
2279
2668
  if (args.JOIN) {
2280
2669
  sql += this.buildJoinClauses(args.JOIN, params);
2281
2670
  }
@@ -2347,12 +2736,14 @@ var PostQueryBuilder = /** @class */ (function (_super) {
2347
2736
  var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
2348
2737
  var body = verb in this.request ? this.request[verb] : this.request;
2349
2738
  var keys = Object.keys(body);
2350
- var params = [];
2739
+ var params = this.useNamedParams ? {} : [];
2351
2740
  var placeholders = [];
2352
2741
  for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
2353
2742
  var key = keys_1[_i];
2354
2743
  var value = body[key];
2355
- var placeholder = this.addParam(params, key, value);
2744
+ var trimmed = this.trimTablePrefix(table, key);
2745
+ var qualified = "".concat(table, ".").concat(trimmed);
2746
+ var placeholder = this.serializeUpdateValue(value, params, qualified);
2356
2747
  placeholders.push(placeholder);
2357
2748
  }
2358
2749
  var sql = "".concat(verb, " INTO `").concat(table, "` (\n ").concat(keys.map(function (k) { return "`".concat(_this.trimTablePrefix(table, k), "`"); }).join(', '), "\n ) VALUES (\n ").concat(placeholders.join(', '), "\n )");
@@ -2404,7 +2795,9 @@ var UpdateQueryBuilder = /** @class */ (function (_super) {
2404
2795
  var col = _a[0], val = _a[1];
2405
2796
  var trimmed = _this.trimTablePrefix(table, col);
2406
2797
  var qualified = "".concat(table, ".").concat(trimmed);
2407
- return "`".concat(trimmed, "` = ").concat(_this.addParam(params, qualified, val));
2798
+ _this.assertValidIdentifier(qualified, 'UPDATE SET');
2799
+ var rightSql = _this.serializeUpdateValue(val, params, qualified);
2800
+ return "`".concat(trimmed, "` = ").concat(rightSql);
2408
2801
  });
2409
2802
  sql += " SET ".concat(setClauses.join(', '));
2410
2803
  if (args.WHERE) {
@@ -2732,12 +3125,12 @@ function ExpressHandler(_a) {
2732
3125
  var _this = this;
2733
3126
  var C6 = _a.C6, mysqlPool = _a.mysqlPool;
2734
3127
  return function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
2735
- var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, primaryKeys, primaryKeyName, response, err_1;
2736
- var _a, _b, _c, _d, _e;
2737
- return __generator(this, function (_f) {
2738
- switch (_f.label) {
3128
+ var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, restModel, primaryKeys_1, primaryShortKeys_1, columnMap_1, resolveShortKey_1, hasPrimaryKeyValues, primaryKeyName, response, err_1;
3129
+ var _a, _b, _c, _d, _e, _f, _g;
3130
+ return __generator(this, function (_h) {
3131
+ switch (_h.label) {
2739
3132
  case 0:
2740
- _f.trys.push([0, 2, , 3]);
3133
+ _h.trys.push([0, 2, , 3]);
2741
3134
  incomingMethod = req.method.toUpperCase();
2742
3135
  table = req.params.table;
2743
3136
  primary = req.params.primary;
@@ -2751,7 +3144,7 @@ function ExpressHandler(_a) {
2751
3144
  try {
2752
3145
  delete payload.METHOD;
2753
3146
  }
2754
- catch ( /* noop */_g) { /* noop */ }
3147
+ catch ( /* noop */_j) { /* noop */ }
2755
3148
  }
2756
3149
  // Warn for unsupported overrides but continue normally
2757
3150
  if (incomingMethod !== 'GET' && methodOverride && methodOverride !== 'GET') {
@@ -2761,29 +3154,56 @@ function ExpressHandler(_a) {
2761
3154
  res.status(400).json({ error: "Invalid table: ".concat(table) });
2762
3155
  return [2 /*return*/];
2763
3156
  }
2764
- primaryKeys = C6.TABLES[table].PRIMARY;
2765
- if (primary && primaryKeys.length !== 1) {
2766
- if (primaryKeys.length > 1) {
3157
+ restModel = C6.TABLES[table];
3158
+ primaryKeys_1 = restModel.PRIMARY;
3159
+ primaryShortKeys_1 = (_d = restModel.PRIMARY_SHORT) !== null && _d !== void 0 ? _d : [];
3160
+ columnMap_1 = (_e = restModel.COLUMNS) !== null && _e !== void 0 ? _e : {};
3161
+ resolveShortKey_1 = function (fullKey, index) { var _a, _b, _c; return (_c = (_b = (_a = columnMap_1[fullKey]) !== null && _a !== void 0 ? _a : primaryShortKeys_1[index]) !== null && _b !== void 0 ? _b : fullKey.split('.').pop()) !== null && _c !== void 0 ? _c : fullKey; };
3162
+ hasPrimaryKeyValues = function (data) {
3163
+ if (!data || typeof data !== 'object')
3164
+ return false;
3165
+ var whereClause = data[C6C.WHERE];
3166
+ var hasKeyValue = function (obj, fullKey, shortKey) {
3167
+ if (!obj || typeof obj !== 'object')
3168
+ return false;
3169
+ var fullValue = obj[fullKey];
3170
+ if (fullValue !== undefined && fullValue !== null)
3171
+ return true;
3172
+ var shortValue = shortKey ? obj[shortKey] : undefined;
3173
+ return shortValue !== undefined && shortValue !== null;
3174
+ };
3175
+ return primaryKeys_1.every(function (fullKey, index) {
3176
+ var shortKey = resolveShortKey_1(fullKey, index);
3177
+ return hasKeyValue(whereClause, fullKey, shortKey) || hasKeyValue(data, fullKey, shortKey);
3178
+ });
3179
+ };
3180
+ if (primary && primaryKeys_1.length !== 1) {
3181
+ if (primaryKeys_1.length > 1 && hasPrimaryKeyValues(payload)) {
3182
+ primary = undefined;
3183
+ }
3184
+ else if (primaryKeys_1.length > 1) {
2767
3185
  res.status(400).json({ error: "Table ".concat(table, " has multiple primary keys. Cannot implicitly determine key.") });
2768
3186
  return [2 /*return*/];
2769
3187
  }
2770
- res.status(400).json({
2771
- error: "Table ".concat(table, " has no primary keys. Please specify one.")
2772
- });
2773
- return [2 /*return*/];
3188
+ else {
3189
+ res.status(400).json({
3190
+ error: "Table ".concat(table, " has no primary keys. Please specify one.")
3191
+ });
3192
+ return [2 /*return*/];
3193
+ }
2774
3194
  }
2775
- primaryKeyName = primaryKeys[0];
3195
+ primaryKeyName = primaryKeys_1[0];
2776
3196
  // If a primary key was provided in the URL, merge it into the payload.
2777
3197
  // Support both complex requests using WHERE and singular requests
2778
3198
  // where the primary key lives at the root of the payload.
2779
3199
  if (primary) {
2780
3200
  if (payload[C6C.WHERE]) {
2781
3201
  payload[C6C.WHERE][primaryKeyName] =
2782
- (_d = payload[C6C.WHERE][primaryKeyName]) !== null && _d !== void 0 ? _d : primary;
3202
+ (_f = payload[C6C.WHERE][primaryKeyName]) !== null && _f !== void 0 ? _f : primary;
2783
3203
  }
2784
3204
  else {
2785
3205
  payload[primaryKeyName] =
2786
- (_e = payload[primaryKeyName]) !== null && _e !== void 0 ? _e : primary;
3206
+ (_g = payload[primaryKeyName]) !== null && _g !== void 0 ? _g : primary;
2787
3207
  }
2788
3208
  }
2789
3209
  return [4 /*yield*/, restRequest({
@@ -2793,11 +3213,11 @@ function ExpressHandler(_a) {
2793
3213
  restModel: C6.TABLES[table]
2794
3214
  })(payload)];
2795
3215
  case 1:
2796
- response = _f.sent();
3216
+ response = _h.sent();
2797
3217
  res.status(200).json(__assign({ success: true }, response));
2798
3218
  return [3 /*break*/, 3];
2799
3219
  case 2:
2800
- err_1 = _f.sent();
3220
+ err_1 = _h.sent();
2801
3221
  res.status(500).json({ success: false, error: err_1 });
2802
3222
  next(err_1);
2803
3223
  return [3 /*break*/, 3];
@@ -2882,10 +3302,11 @@ function error(message) {
2882
3302
  }
2883
3303
 
2884
3304
  function checkAllRequestsComplete() {
2885
- var stillRunning = apiRequestCache.filter(function (cache) { return undefined === cache.response; });
3305
+ var cacheEntries = Array.from(apiRequestCache.values());
3306
+ var stillRunning = cacheEntries.filter(function (cache) { return undefined === cache.response; });
2886
3307
  if (stillRunning.length !== 0) {
2887
3308
  if (document === null || document === undefined) {
2888
- throw new Error('document is undefined while waiting for API requests to complete (' + JSON.stringify(apiRequestCache) + ')');
3309
+ throw new Error('document is undefined while waiting for API requests to complete (' + JSON.stringify(cacheEntries) + ')');
2889
3310
  }
2890
3311
  // when requests return emtpy sets in full renders, it may not be possible to track their progress.
2891
3312
  console.warn('stillRunning...', stillRunning);
@@ -2906,5 +3327,5 @@ function isVerbose () {
2906
3327
  return ['true', '1', 'yes', 'on'].includes(envVerbose.toLowerCase());
2907
3328
  }
2908
3329
 
2909
- export { A, AggregateBuilder, C6C, C6Constants, ConditionBuilder, DELETE, DeleteQueryBuilder, Executor, ExpressHandler, F, GET, HttpExecutor, JoinBuilder, POST, PUT, PaginationBuilder, PostQueryBuilder, SelectQueryBuilder, SqlExecutor, TestRestfulResponse, UpdateQueryBuilder, apiRequestCache, axiosInstance, bbox, carbonNodeQsStringify, checkAllRequestsComplete, checkCache, clearCache, convertForRequestBody, convertHexIfBinary, derivedTable, determineRuntimeJsType, distSphere, eFetchDependencies, error, fieldEq, getEnvVar, getPrimaryKeyTypes, group, info, isDerivedTableKey, isLocal, isNode, isTest, isVerbose, normalizeSingularRequest, onError, onSuccess, removeInvalidKeys, removePrefixIfExists, resolveDerivedTable, restOrm, restRequest, sortAndSerializeQueryObject, stContains, timeout, toastOptions, toastOptionsDevs, userCustomClearCache, warn };
3330
+ export { A, AggregateBuilder, C6C, C6Constants, ConditionBuilder, DELETE, DeleteQueryBuilder, Executor, ExpressHandler, F, GET, HttpExecutor, JoinBuilder, POST, PUT, PaginationBuilder, PostQueryBuilder, SelectQueryBuilder, SqlExecutor, TestRestfulResponse, UpdateQueryBuilder, apiRequestCache, axiosInstance, bbox, carbonNodeQsStringify, checkAllRequestsComplete, checkCache, clearCache, convertForRequestBody, convertHexIfBinary, derivedTable, determineRuntimeJsType, distSphere, eFetchDependencies, error, fieldEq, getEnvVar, getPrimaryKeyTypes, group, info, isDerivedTableKey, isLocal, isNode, isTest, isVerbose, normalizeSingularRequest, onError, onSuccess, removeInvalidKeys, removePrefixIfExists, resolveDerivedTable, restOrm, restRequest, setCache, sortAndSerializeQueryObject, stContains, timeout, toastOptions, toastOptionsDevs, userCustomClearCache, warn };
2910
3331
  //# sourceMappingURL=index.esm.js.map