@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.cjs.js CHANGED
@@ -38,10 +38,12 @@ var C6Constants = {
38
38
  DATE_FORMAT: 'DATE_FORMAT',
39
39
  DESC: 'DESC',
40
40
  DISTINCT: 'DISTINCT',
41
+ EXISTS: 'EXISTS',
41
42
  EXTRACT: 'EXTRACT',
42
43
  EQUAL: '=',
43
44
  EQUAL_NULL_SAFE: '<=>',
44
45
  FALSE: 'FALSE',
46
+ FORCE: 'FORCE',
45
47
  FULL_OUTER: 'FULL_OUTER',
46
48
  FROM_DAYS: 'FROM_DAYS',
47
49
  FROM_UNIXTIME: 'FROM_UNIXTIME',
@@ -57,6 +59,7 @@ var C6Constants = {
57
59
  HOUR_SECOND: 'HOUR_SECOND',
58
60
  HOUR_MINUTE: 'HOUR_MINUTE',
59
61
  IN: 'IN',
62
+ INDEX: 'INDEX',
60
63
  IS: 'IS',
61
64
  IS_NOT: 'IS_NOT',
62
65
  INNER: 'INNER',
@@ -74,6 +77,7 @@ var C6Constants = {
74
77
  MAKEDATE: 'MAKEDATE',
75
78
  MAKETIME: 'MAKETIME',
76
79
  MATCH_AGAINST: 'MATCH_AGAINST',
80
+ MBRCONTAINS: 'MBRContains',
77
81
  MONTHNAME: 'MONTHNAME',
78
82
  MICROSECOND: 'MICROSECOND',
79
83
  MINUTE: 'MINUTE',
@@ -89,8 +93,14 @@ var C6Constants = {
89
93
  NULL: 'NULL',
90
94
  ORDER: 'ORDER',
91
95
  OR: 'OR',
96
+ INDEX_HINTS: 'INDEX_HINTS',
97
+ FORCE_INDEX: 'FORCE INDEX',
98
+ USE_INDEX: 'USE INDEX',
99
+ IGNORE_INDEX: 'IGNORE INDEX',
92
100
  PAGE: 'PAGE',
93
101
  PAGINATION: 'PAGINATION',
102
+ POLYGON: 'POLYGON',
103
+ POINT: 'POINT',
94
104
  RIGHT_OUTER: 'RIGHT_OUTER',
95
105
  SECOND: 'SECOND',
96
106
  SECOND_MICROSECOND: 'SECOND_MICROSECOND',
@@ -297,8 +307,30 @@ axiosInstance.interceptors.request.use(function (config) {
297
307
  };
298
308
  }
299
309
  }
310
+ config.__carbonStart = performance.now();
300
311
  return config;
301
312
  });
313
+ axiosInstance.interceptors.response.use(function (response) {
314
+ var end = performance.now();
315
+ var start = response.config.__carbonStart;
316
+ response.__carbonTiming = {
317
+ start: start,
318
+ end: end,
319
+ duration: end - start,
320
+ };
321
+ return response;
322
+ }, function (error) {
323
+ if (error.config) {
324
+ var end = performance.now();
325
+ var start = error.config.__carbonStart;
326
+ error.__carbonTiming = {
327
+ start: start,
328
+ end: end,
329
+ duration: end - start,
330
+ };
331
+ }
332
+ throw error;
333
+ });
302
334
 
303
335
  function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler) {
304
336
  if (regexErrorHandler === void 0) { regexErrorHandler = alert; }
@@ -400,10 +432,7 @@ function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler)
400
432
 
401
433
  var isNode = function () {
402
434
  var _a;
403
- console.log('Checking if running in Node.js environment...');
404
- var isNodeEnv = typeof process !== 'undefined' && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
405
- console.log("Is Node.js environment: ".concat(isNodeEnv));
406
- return isNodeEnv;
435
+ return typeof process !== 'undefined' && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
407
436
  };
408
437
 
409
438
  /**
@@ -621,26 +650,64 @@ function removeInvalidKeys(request, c6Tables) {
621
650
  return intersection;
622
651
  }
623
652
 
624
- // do not remove entries from this array. It is used to track the progress of API requests.
625
- // position in array is important. Do not sort. To not add to begging.
626
- exports.apiRequestCache = [];
627
- exports.userCustomClearCache = [];
653
+ // -----------------------------------------------------------------------------
654
+ // Cache Storage
655
+ // -----------------------------------------------------------------------------
656
+ var apiRequestCache = new Map();
657
+ var userCustomClearCache = [];
658
+ // -----------------------------------------------------------------------------
659
+ // Cache Key Generator (safe, fixed-size ~40 chars)
660
+ // -----------------------------------------------------------------------------
661
+ // -----------------------------------------------------------------------------
662
+ // Browser-safe deterministic hash (FNV-1a)
663
+ // -----------------------------------------------------------------------------
664
+ function fnv1a(str) {
665
+ var h = 0x811c9dc5;
666
+ for (var i = 0; i < str.length; i++) {
667
+ h ^= str.charCodeAt(i);
668
+ h = (h * 0x01000193) >>> 0;
669
+ }
670
+ return h.toString(16);
671
+ }
672
+ function makeCacheKey(method, tableName, requestData) {
673
+ var raw = JSON.stringify([method, tableName, requestData]);
674
+ return fnv1a(raw);
675
+ }
676
+ // -----------------------------------------------------------------------------
677
+ // Clear Cache (no shared-array bugs)
678
+ // -----------------------------------------------------------------------------
628
679
  function clearCache(props) {
629
- if (false === (props === null || props === void 0 ? void 0 : props.ignoreWarning)) {
630
- console.warn('The rest api clearCache should only be used with extreme care! Avoid using this in favor of using `cacheResults : boolean`.');
680
+ if (!(props === null || props === void 0 ? void 0 : props.ignoreWarning)) {
681
+ console.warn("The REST API clearCache should only be used with extreme care!");
682
+ }
683
+ for (var _i = 0, userCustomClearCache_1 = userCustomClearCache; _i < userCustomClearCache_1.length; _i++) {
684
+ var fn = userCustomClearCache_1[_i];
685
+ try {
686
+ fn();
687
+ }
688
+ catch (_a) { }
631
689
  }
632
- exports.userCustomClearCache.map(function (f) { return 'function' === typeof f && f(); });
633
- exports.userCustomClearCache = exports.apiRequestCache = [];
690
+ apiRequestCache.clear();
634
691
  }
635
- function checkCache(cacheResult, requestMethod, tableName, request) {
636
- if (undefined === cacheResult) {
692
+ // -----------------------------------------------------------------------------
693
+ // Check Cache (dedupe via hashed key)
694
+ // -----------------------------------------------------------------------------
695
+ function checkCache(method, tableName, requestData) {
696
+ var key = makeCacheKey(method, tableName, requestData);
697
+ var cached = apiRequestCache.get(key);
698
+ if (!cached)
637
699
  return false;
638
- }
639
- console.groupCollapsed('%c API: The request on (' + tableName + ') is in cache. Returning the request Promise!', 'color: #0c0');
640
- console.log('%c ' + requestMethod + ' ' + tableName, 'color: #0c0');
641
- console.log('%c Request Data (note you may see the success and/or error prompt):', 'color: #0c0', request);
700
+ console.groupCollapsed("%c API cache hit for ".concat(method, " ").concat(tableName), "color:#0c0");
701
+ console.log("Request Data:", requestData);
642
702
  console.groupEnd();
643
- return cacheResult.request;
703
+ return cached.request;
704
+ }
705
+ // -----------------------------------------------------------------------------
706
+ // Store Cache Entry (drop-in compatible)
707
+ // -----------------------------------------------------------------------------
708
+ function setCache(method, tableName, requestData, cacheEntry) {
709
+ var key = makeCacheKey(method, tableName, requestData);
710
+ apiRequestCache.set(key, cacheEntry);
644
711
  }
645
712
 
646
713
  function sortAndSerializeQueryObject(tables, query) {
@@ -657,7 +724,10 @@ var HttpExecutor = /** @class */ (function (_super) {
657
724
  return _super !== null && _super.apply(this, arguments) || this;
658
725
  }
659
726
  HttpExecutor.prototype.isRestResponse = function (r) {
660
- return !!r && r.data != null && typeof r.data === 'object' && 'rest' in r.data;
727
+ return !!r
728
+ && r.data != null
729
+ && typeof r.data === 'object'
730
+ && Array.isArray(r.data.rest);
661
731
  };
662
732
  HttpExecutor.prototype.stripTableNameFromKeys = function (obj) {
663
733
  var _a;
@@ -753,26 +823,16 @@ var HttpExecutor = /** @class */ (function (_super) {
753
823
  throw Error('Bad request method passed to getApi');
754
824
  }
755
825
  if (clearCache != null) {
756
- exports.userCustomClearCache.push(clearCache);
826
+ userCustomClearCache.push(clearCache);
757
827
  }
758
828
  if (isLocal() && (this.config.verbose || ((_b = this.request) === null || _b === void 0 ? void 0 : _b.debug))) {
759
829
  console.groupCollapsed('%c API:', 'color: #0c0', "(".concat(requestMethod, ") Request for (").concat(tableName, ")"));
760
830
  console.log('request', this.request);
761
831
  console.groupEnd();
762
832
  }
763
- // an undefined query would indicate queryCallback returned undefined,
764
- // thus the request shouldn't fire as is in custom cache
765
- if (undefined === this.request || null === this.request) {
766
- if (isLocal()) {
767
- console.groupCollapsed("API: (".concat(requestMethod, ") (").concat(tableName, ") query undefined/null \u2192 returning null"));
768
- console.log('request', this.request);
769
- console.groupEnd();
770
- }
771
- return [2 /*return*/, null];
772
- }
773
833
  query = this.request;
774
834
  apiRequest = function () { return tslib.__awaiter(_this, void 0, void 0, function () {
775
- 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;
835
+ 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;
776
836
  var _e;
777
837
  var _this = this;
778
838
  var _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
@@ -788,63 +848,37 @@ var HttpExecutor = /** @class */ (function (_super) {
788
848
  console.log('request', this.request);
789
849
  console.groupEnd();
790
850
  }
791
- querySerialized = sortAndSerializeQueryObject(tables, query !== null && query !== void 0 ? query : {});
792
- cacheResult = exports.apiRequestCache.find(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
793
851
  cachingConfirmed = false;
794
- if (!(requestMethod === C6.GET)) return [3 /*break*/, 9];
795
- if (undefined === (query === null || query === void 0 ? void 0 : query[C6.PAGINATION])) {
796
- if (undefined === query || null === query) {
797
- query = {};
852
+ // determine if we need to paginate.
853
+ if (requestMethod === C6.GET) {
854
+ if (undefined === (query === null || query === void 0 ? void 0 : query[C6.PAGINATION])) {
855
+ if (undefined === query || null === query) {
856
+ query = {};
857
+ }
858
+ query[C6.PAGINATION] = {};
798
859
  }
799
- query[C6.PAGINATION] = {};
860
+ query[C6.PAGINATION][C6.PAGE] = query[C6.PAGINATION][C6.PAGE] || 1;
861
+ query[C6.PAGINATION][C6.LIMIT] = query[C6.PAGINATION][C6.LIMIT] || 100;
862
+ }
863
+ cacheRequestData = JSON.parse(JSON.stringify(query !== null && query !== void 0 ? query : {}));
864
+ querySerialized = sortAndSerializeQueryObject(tables, cacheRequestData !== null && cacheRequestData !== void 0 ? cacheRequestData : {});
865
+ cachedRequest = false;
866
+ if (cacheResults) {
867
+ cachedRequest = checkCache(requestMethod, tableName, cacheRequestData);
800
868
  }
801
- query[C6.PAGINATION][C6.PAGE] = query[C6.PAGINATION][C6.PAGE] || 1;
802
- query[C6.PAGINATION][C6.LIMIT] = query[C6.PAGINATION][C6.LIMIT] || 100;
803
- if (!(true === cacheResults)) return [3 /*break*/, 7];
804
- if (!(undefined !== cacheResult)) return [3 /*break*/, 6];
805
- _r.label = 1;
806
- case 1:
807
- cacheCheck = checkCache(cacheResult, requestMethod, tableName, this.request);
808
- if (!(false !== cacheCheck)) return [3 /*break*/, 3];
809
- return [4 /*yield*/, cacheCheck];
810
- case 2: return [2 /*return*/, (_r.sent()).data];
811
- case 3:
812
- ++query[C6.PAGINATION][C6.PAGE];
813
- querySerialized = sortAndSerializeQueryObject(tables, query !== null && query !== void 0 ? query : {});
814
- cacheResult = exports.apiRequestCache.find(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
815
- _r.label = 4;
816
- case 4:
817
- if (undefined !== cacheResult) return [3 /*break*/, 1];
818
- _r.label = 5;
819
- case 5:
820
- if (debug && isLocal()) {
821
- reactToastify.toast.warning("DEVS: Request pages exhausted in cache; firing network.", toastOptionsDevs);
869
+ if (!cachedRequest) return [3 /*break*/, 2];
870
+ return [4 /*yield*/, cachedRequest];
871
+ case 1: return [2 /*return*/, (_r.sent()).data];
872
+ case 2:
873
+ if (cacheResults) {
874
+ cachingConfirmed = true;
822
875
  }
823
- _r.label = 6;
824
- case 6:
825
- cachingConfirmed = true;
826
- return [3 /*break*/, 8];
827
- case 7:
828
- if (debug && isLocal())
876
+ else if (debug && isLocal()) {
829
877
  reactToastify.toast.info("DEVS: Ignore cache was set to true.", toastOptionsDevs);
830
- _r.label = 8;
831
- case 8:
832
- if (debug && isLocal()) {
878
+ }
879
+ if (cacheResults && debug && isLocal()) {
833
880
  reactToastify.toast.success("DEVS: Request not in cache." + (requestMethod === C6.GET ? " Page (" + query[C6.PAGINATION][C6.PAGE] + ")" : ''), toastOptionsDevs);
834
881
  }
835
- return [3 /*break*/, 12];
836
- case 9:
837
- if (!cacheResults) return [3 /*break*/, 12];
838
- if (!cacheResult) return [3 /*break*/, 11];
839
- cacheCheck = checkCache(cacheResult, requestMethod, tableName, this.request);
840
- if (!(false !== cacheCheck)) return [3 /*break*/, 11];
841
- return [4 /*yield*/, cacheCheck];
842
- case 10: return [2 /*return*/, (_r.sent()).data];
843
- case 11:
844
- cachingConfirmed = true;
845
- _r.label = 12;
846
- case 12:
847
- returnGetNextPageFunction = false;
848
882
  restRequestUri = restURL + operatingTable + '/';
849
883
  needsConditionOrPrimaryCheck = (PUT === requestMethod || DELETE === requestMethod)
850
884
  && false === skipPrimaryCheck;
@@ -914,7 +948,7 @@ var HttpExecutor = /** @class */ (function (_super) {
914
948
  config: this.config,
915
949
  request: this.request
916
950
  });
917
- axiosActiveRequest = (_e = axios)[requestMethod.toLowerCase()].apply(_e, tslib.__spreadArray([restRequestUri], (function () {
951
+ axiosActiveRequest_1 = (_e = axios)[requestMethod.toLowerCase()].apply(_e, tslib.__spreadArray([restRequestUri], (function () {
918
952
  var convert = function (data) {
919
953
  return convertForRequestBody(data, fullTableList, C6, function (message) { return reactToastify.toast.error(message, toastOptions); });
920
954
  };
@@ -941,23 +975,30 @@ var HttpExecutor = /** @class */ (function (_super) {
941
975
  }
942
976
  })(), false));
943
977
  if (cachingConfirmed) {
944
- // push to cache so we do not repeat the request
945
- exports.apiRequestCache.push({
978
+ setCache(requestMethod, tableName, cacheRequestData, {
946
979
  requestArgumentsSerialized: querySerialized,
947
- request: axiosActiveRequest
980
+ request: axiosActiveRequest_1,
948
981
  });
949
982
  }
950
983
  // returning the promise with this then is important for tests. todo - we could make that optional.
951
984
  // https://rapidapi.com/guides/axios-async-await
952
- return [2 /*return*/, axiosActiveRequest.then(function (response) { return tslib.__awaiter(_this, void 0, void 0, function () {
953
- var cacheIndex, callback, responseData_1, pageLimit, got, hasNext, cacheIndex, dependencies_1, fetchReferences_1, apiRequestPromises, _loop_1, tableToFetch;
985
+ return [2 /*return*/, axiosActiveRequest_1.then(function (response) { return tslib.__awaiter(_this, void 0, void 0, function () {
986
+ var hasNext, callback, responseData_1, pageLimit, got, dependencies_1, fetchReferences_1, apiRequestPromises, _loop_1, tableToFetch;
954
987
  var _this = this;
955
- var _a, _b, _c, _d;
956
- return tslib.__generator(this, function (_e) {
957
- switch (_e.label) {
988
+ var _a, _b, _c, _d, _e;
989
+ return tslib.__generator(this, function (_f) {
990
+ switch (_f.label) {
958
991
  case 0:
959
992
  // noinspection SuspiciousTypeOfGuard
960
993
  if (typeof response.data === 'string') {
994
+ if (cachingConfirmed) {
995
+ setCache(requestMethod, tableName, cacheRequestData, {
996
+ requestArgumentsSerialized: querySerialized,
997
+ request: axiosActiveRequest_1,
998
+ response: response,
999
+ final: true,
1000
+ });
1001
+ }
961
1002
  if (isTest()) {
962
1003
  console.trace();
963
1004
  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 + ')');
@@ -965,11 +1006,11 @@ var HttpExecutor = /** @class */ (function (_super) {
965
1006
  return [2 /*return*/, Promise.reject(response)];
966
1007
  }
967
1008
  if (cachingConfirmed) {
968
- cacheIndex = exports.apiRequestCache.findIndex(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
969
- // TODO - currently nonthing is setting this correctly
970
- exports.apiRequestCache[cacheIndex].final = false === returnGetNextPageFunction;
971
- // only cache get method requests
972
- exports.apiRequestCache[cacheIndex].response = response;
1009
+ setCache(requestMethod, tableName, cacheRequestData, {
1010
+ requestArgumentsSerialized: querySerialized,
1011
+ request: axiosActiveRequest_1,
1012
+ response: response,
1013
+ });
973
1014
  }
974
1015
  this.runLifecycleHooks("afterExecution", {
975
1016
  config: this.config,
@@ -1020,17 +1061,18 @@ var HttpExecutor = /** @class */ (function (_super) {
1020
1061
  got = responseData_1.rest.length;
1021
1062
  hasNext = pageLimit !== 1 && got === pageLimit;
1022
1063
  if (hasNext) {
1023
- responseData_1.next = apiRequest; // there might be more
1064
+ responseData_1.next = apiRequest;
1024
1065
  }
1025
1066
  else {
1026
1067
  responseData_1.next = undefined; // short page => done
1027
1068
  }
1028
- // If you keep this flag, make it reflect reality:
1029
- returnGetNextPageFunction = hasNext;
1030
- // and fix cache ‘final’ flag to match:
1031
1069
  if (cachingConfirmed) {
1032
- cacheIndex = exports.apiRequestCache.findIndex(function (c) { return c.requestArgumentsSerialized === querySerialized; });
1033
- exports.apiRequestCache[cacheIndex].final = !hasNext;
1070
+ setCache(requestMethod, tableName, cacheRequestData, {
1071
+ requestArgumentsSerialized: querySerialized,
1072
+ request: axiosActiveRequest_1,
1073
+ response: response,
1074
+ final: !hasNext,
1075
+ });
1034
1076
  }
1035
1077
  if ((this.config.verbose || debug) && isLocal()) {
1036
1078
  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], ")"));
@@ -1040,7 +1082,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1040
1082
  }
1041
1083
  if (!(fetchDependencies
1042
1084
  && 'number' === typeof fetchDependencies
1043
- && responseData_1.rest.length > 0)) return [3 /*break*/, 2];
1085
+ && ((_d = responseData_1.rest) === null || _d === void 0 ? void 0 : _d.length) > 0)) return [3 /*break*/, 2];
1044
1086
  console.groupCollapsed('%c API: Fetch Dependencies segment (' + requestMethod + ' ' + tableName + ')'
1045
1087
  + (fetchDependencies & exports.eFetchDependencies.CHILDREN ? ' | (CHILDREN|REFERENCED) ' : '')
1046
1088
  + (fetchDependencies & exports.eFetchDependencies.PARENTS ? ' | (PARENTS|REFERENCED_BY)' : '')
@@ -1111,7 +1153,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1111
1153
  }); });
1112
1154
  console.log('fetchReferences', fetchReferences_1);
1113
1155
  _loop_1 = function (tableToFetch) {
1114
- var _f;
1156
+ var _g;
1115
1157
  if (fetchDependencies & exports.eFetchDependencies.C6ENTITY
1116
1158
  && 'string' === typeof tableName
1117
1159
  && tableName.endsWith("carbon_carbons")) {
@@ -1134,7 +1176,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1134
1176
  .split('_')
1135
1177
  .map(function (part) { return part.charAt(0).toUpperCase() + part.slice(1); })
1136
1178
  .join('_');
1137
- 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(', '), " ]"));
1179
+ 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(', '), " ]"));
1138
1180
  console.log('%c Fetch Dependencies will select (' + tableToFetch + ') using GET request', 'color: #33ccff');
1139
1181
  var nextFetchDependencies = exports.eFetchDependencies.NONE;
1140
1182
  if (fetchDependencies & exports.eFetchDependencies.RECURSIVE) {
@@ -1160,8 +1202,8 @@ var HttpExecutor = /** @class */ (function (_super) {
1160
1202
  console.log('RestApi object', RestApi);
1161
1203
  // this is a dynamic call to the rest api, any generated table may resolve with (RestApi)
1162
1204
  // todo - using value to avoid joins.... but. maybe this should be a parameterizable option -- think race conditions; its safer to join
1163
- apiRequestPromises.push(RestApi.Get((_f = {},
1164
- _f[C6.WHERE] = Object.keys(fetchReferences_1[tableToFetch]).reduce(function (sum, column) {
1205
+ apiRequestPromises.push(RestApi.Get((_g = {},
1206
+ _g[C6.WHERE] = Object.keys(fetchReferences_1[tableToFetch]).reduce(function (sum, column) {
1165
1207
  fetchReferences_1[tableToFetch][column] = fetchReferences_1[tableToFetch][column].flat(Infinity);
1166
1208
  if (0 === fetchReferences_1[tableToFetch][column].length) {
1167
1209
  console.warn('The column (' + column + ') was not found in the response data. We will not fetch.', responseData_1);
@@ -1174,8 +1216,8 @@ var HttpExecutor = /** @class */ (function (_super) {
1174
1216
  ];
1175
1217
  return sum;
1176
1218
  }, {}),
1177
- _f.fetchDependencies = nextFetchDependencies,
1178
- _f)));
1219
+ _g.fetchDependencies = nextFetchDependencies,
1220
+ _g)));
1179
1221
  };
1180
1222
  for (tableToFetch in fetchReferences_1) {
1181
1223
  _loop_1(tableToFetch);
@@ -1183,7 +1225,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1183
1225
  console.groupEnd();
1184
1226
  return [4 /*yield*/, Promise.all(apiRequestPromises)];
1185
1227
  case 1:
1186
- _e.sent();
1228
+ _f.sent();
1187
1229
  apiRequestPromises.map(function (promise) { return tslib.__awaiter(_this, void 0, void 0, function () {
1188
1230
  var _a, _b;
1189
1231
  return tslib.__generator(this, function (_c) {
@@ -1201,8 +1243,16 @@ var HttpExecutor = /** @class */ (function (_super) {
1201
1243
  }
1202
1244
  });
1203
1245
  }); });
1204
- _e.label = 2;
1246
+ _f.label = 2;
1205
1247
  case 2:
1248
+ if (cachingConfirmed && hasNext === undefined) {
1249
+ setCache(requestMethod, tableName, cacheRequestData, {
1250
+ requestArgumentsSerialized: querySerialized,
1251
+ request: axiosActiveRequest_1,
1252
+ response: response,
1253
+ final: true,
1254
+ });
1255
+ }
1206
1256
  if (debug && isLocal()) {
1207
1257
  reactToastify.toast.success("DEVS: (" + requestMethod + ") request complete.", toastOptionsDevs);
1208
1258
  }
@@ -1213,16 +1263,12 @@ var HttpExecutor = /** @class */ (function (_super) {
1213
1263
  }); }).then(function (response) { return response.data; })]; // this escapes from axios context
1214
1264
  }
1215
1265
  catch (throwableError) {
1216
- if (isTest()) {
1217
- throw new Error(JSON.stringify(throwableError));
1218
- }
1219
1266
  console.groupCollapsed('%c API: An error occurred in the try catch block. returning null!', 'color: #ff0000');
1220
1267
  console.log('%c ' + requestMethod + ' ' + tableName, 'color: #A020F0');
1221
- console.warn(throwableError);
1268
+ console.error(throwableError);
1222
1269
  console.trace();
1223
1270
  console.groupEnd();
1224
- TestRestfulResponse(throwableError, success, error);
1225
- return [2 /*return*/, null];
1271
+ throw new Error(JSON.stringify(throwableError));
1226
1272
  }
1227
1273
  return [2 /*return*/];
1228
1274
  }
@@ -1451,7 +1497,8 @@ var stContains = function (envelope, shape) {
1451
1497
  var ConditionBuilder = /** @class */ (function (_super) {
1452
1498
  tslib.__extends(ConditionBuilder, _super);
1453
1499
  function ConditionBuilder() {
1454
- var _this = _super !== null && _super.apply(this, arguments) || this;
1500
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1501
+ var _this = _super.apply(this, arguments) || this;
1455
1502
  _this.aliasMap = {};
1456
1503
  _this.derivedAliases = new Set();
1457
1504
  _this.BOOLEAN_OPERATORS = new Map([
@@ -1490,8 +1537,16 @@ var ConditionBuilder = /** @class */ (function (_super) {
1490
1537
  [C6C.BETWEEN, C6C.BETWEEN],
1491
1538
  ['BETWEEN', C6C.BETWEEN],
1492
1539
  ['NOT BETWEEN', 'NOT BETWEEN'],
1540
+ [C6C.EXISTS, C6C.EXISTS],
1541
+ ['EXISTS', C6C.EXISTS],
1542
+ ['NOT EXISTS', 'NOT EXISTS'],
1493
1543
  [C6C.MATCH_AGAINST, C6C.MATCH_AGAINST],
1494
1544
  ]);
1545
+ _this.BOOLEAN_FUNCTION_KEYS = new Set([
1546
+ (_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',
1547
+ (_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',
1548
+ (_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',
1549
+ ]);
1495
1550
  return _this;
1496
1551
  }
1497
1552
  ConditionBuilder.prototype.initAlias = function (baseTable, joins) {
@@ -1726,6 +1781,9 @@ var ConditionBuilder = /** @class */ (function (_super) {
1726
1781
  if (operand === null || typeof operand === 'number' || typeof operand === 'boolean') {
1727
1782
  return { sql: asParam(operand), isReference: false, isExpression: false, isSubSelect: false };
1728
1783
  }
1784
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(operand)) {
1785
+ return { sql: asParam(operand), isReference: false, isExpression: false, isSubSelect: false };
1786
+ }
1729
1787
  if (typeof operand === 'string') {
1730
1788
  if (this.isTableReference(operand) || this.isColumnRef(operand)) {
1731
1789
  return { sql: operand, isReference: true, isExpression: false, isSubSelect: false };
@@ -1769,10 +1827,185 @@ var ConditionBuilder = /** @class */ (function (_super) {
1769
1827
  }
1770
1828
  throw new Error('Unsupported operand type in SQL expression.');
1771
1829
  };
1830
+ ConditionBuilder.prototype.isPlainArrayLiteral = function (value) {
1831
+ var _this = this;
1832
+ if (!Array.isArray(value))
1833
+ return false;
1834
+ return value.every(function (item) {
1835
+ if (item === null)
1836
+ return true;
1837
+ var type = typeof item;
1838
+ if (type === 'string' || type === 'number' || type === 'boolean')
1839
+ return true;
1840
+ if (Array.isArray(item))
1841
+ return _this.isPlainArrayLiteral(item);
1842
+ if (item && typeof item === 'object')
1843
+ return _this.isPlainObjectLiteral(item);
1844
+ return false;
1845
+ });
1846
+ };
1847
+ ConditionBuilder.prototype.isPlainObjectLiteral = function (value) {
1848
+ var _this = this;
1849
+ if (!value || typeof value !== 'object' || Array.isArray(value))
1850
+ return false;
1851
+ if (value instanceof Date)
1852
+ return false;
1853
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(value))
1854
+ return false;
1855
+ var normalized = value instanceof Map ? Object.fromEntries(value) : value;
1856
+ if (C6C.SUBSELECT in normalized)
1857
+ return false;
1858
+ var entries = Object.entries(normalized);
1859
+ if (entries.length === 0)
1860
+ return true;
1861
+ if (entries.some(function (_a) {
1862
+ var key = _a[0];
1863
+ return _this.isOperator(key) || _this.BOOLEAN_OPERATORS.has(key);
1864
+ })) {
1865
+ return false;
1866
+ }
1867
+ if (entries.some(function (_a) {
1868
+ var key = _a[0];
1869
+ return typeof key === 'string' && (_this.isColumnRef(key) || key.includes('.'));
1870
+ })) {
1871
+ return false;
1872
+ }
1873
+ return true;
1874
+ };
1875
+ ConditionBuilder.prototype.serializeUpdateValue = function (value, params, contextColumn) {
1876
+ var normalized = value instanceof Map ? Object.fromEntries(value) : value;
1877
+ if (this.isPlainArrayLiteral(normalized) || this.isPlainObjectLiteral(normalized)) {
1878
+ return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
1879
+ }
1880
+ var _a = this.serializeOperand(normalized, params, contextColumn), sql = _a.sql, isReference = _a.isReference, isExpression = _a.isExpression, isSubSelect = _a.isSubSelect;
1881
+ if (!isReference && !isExpression && !isSubSelect && typeof normalized === 'object' && normalized !== null) {
1882
+ throw new Error('Unsupported operand type in SQL expression.');
1883
+ }
1884
+ return sql;
1885
+ };
1886
+ ConditionBuilder.prototype.ensurePlainObject = function (value) {
1887
+ if (value instanceof Map) {
1888
+ return Object.fromEntries(value);
1889
+ }
1890
+ return value;
1891
+ };
1892
+ ConditionBuilder.prototype.resolveExistsInnerColumn = function (subRequest, provided) {
1893
+ var _a, _b;
1894
+ if (provided) {
1895
+ if (typeof provided !== 'string' || provided.trim() === '') {
1896
+ throw new Error('EXISTS correlation column must be a non-empty string.');
1897
+ }
1898
+ return provided;
1899
+ }
1900
+ var selectClause = this.ensurePlainObject(subRequest === null || subRequest === void 0 ? void 0 : subRequest[C6C.SELECT]);
1901
+ if (Array.isArray(selectClause) && selectClause.length > 0) {
1902
+ var candidate = selectClause[0];
1903
+ if (typeof candidate === 'string' && candidate.trim() !== '') {
1904
+ return candidate;
1905
+ }
1906
+ }
1907
+ var fromTable = subRequest === null || subRequest === void 0 ? void 0 : subRequest[C6C.FROM];
1908
+ if (typeof fromTable === 'string' && fromTable.trim() !== '') {
1909
+ var table = (_b = (_a = this.config.C6) === null || _a === void 0 ? void 0 : _a.TABLES) === null || _b === void 0 ? void 0 : _b[fromTable.trim()];
1910
+ var primary = table === null || table === void 0 ? void 0 : table.PRIMARY;
1911
+ if (Array.isArray(primary) && primary.length > 0) {
1912
+ return String(primary[0]);
1913
+ }
1914
+ }
1915
+ throw new Error('EXISTS requires a correlation column to be provided or inferable from the subselect.');
1916
+ };
1917
+ ConditionBuilder.prototype.normalizeExistsSpec = function (spec) {
1918
+ var normalized = this.ensurePlainObject(spec);
1919
+ if (!Array.isArray(normalized) || normalized.length < 2) {
1920
+ throw new Error('EXISTS expects an array like [outerColumn, subselect, innerColumn?].');
1921
+ }
1922
+ var outerRaw = normalized[0], payloadRaw = normalized[1], innerRaw = normalized[2];
1923
+ if (typeof outerRaw !== 'string' || outerRaw.trim() === '') {
1924
+ throw new Error('EXISTS requires the first element to be an outer column reference string.');
1925
+ }
1926
+ var payload = this.ensurePlainObject(payloadRaw);
1927
+ var subSelect;
1928
+ if (payload && typeof payload === 'object' && C6C.SUBSELECT in payload) {
1929
+ subSelect = this.ensurePlainObject(payload[C6C.SUBSELECT]);
1930
+ }
1931
+ else if (payload && typeof payload === 'object') {
1932
+ subSelect = payload;
1933
+ }
1934
+ else {
1935
+ throw new Error('EXISTS requires a subselect payload as the second element.');
1936
+ }
1937
+ if (!subSelect || typeof subSelect !== 'object') {
1938
+ throw new Error('EXISTS subselect payload must be an object.');
1939
+ }
1940
+ var innerColumn = typeof innerRaw === 'string' ? innerRaw : undefined;
1941
+ return {
1942
+ outerColumn: outerRaw,
1943
+ subRequest: tslib.__assign({}, subSelect),
1944
+ innerColumn: innerColumn,
1945
+ };
1946
+ };
1947
+ ConditionBuilder.prototype.buildExistsExpression = function (spec, operator, params) {
1948
+ var _a, _b, _c, _d;
1949
+ var _this = this;
1950
+ var _e;
1951
+ var _f = this.normalizeExistsSpec(spec), outerColumn = _f.outerColumn, subRequest = _f.subRequest, innerColumn = _f.innerColumn;
1952
+ var fromTableRaw = subRequest[C6C.FROM];
1953
+ if (typeof fromTableRaw !== 'string' || fromTableRaw.trim() === '') {
1954
+ throw new Error('EXISTS subselect requires a table specified with C6C.FROM.');
1955
+ }
1956
+ var fromTable = fromTableRaw.trim();
1957
+ this.assertValidIdentifier(outerColumn, 'EXISTS correlation column');
1958
+ var correlationColumn = this.resolveExistsInnerColumn(subRequest, innerColumn);
1959
+ if (!this.isColumnRef(correlationColumn) && !this.isTableReference(correlationColumn)) {
1960
+ throw new Error("Unknown column reference '".concat(correlationColumn, "' used in EXISTS subquery correlation column."));
1961
+ }
1962
+ var existingWhereRaw = this.ensurePlainObject(subRequest[C6C.WHERE]);
1963
+ var correlationCondition = (_a = {}, _a[correlationColumn] = [C6C.EQUAL, outerColumn], _a);
1964
+ var normalizedExistingWhere = existingWhereRaw && typeof existingWhereRaw === 'object'
1965
+ ? Array.isArray(existingWhereRaw)
1966
+ ? existingWhereRaw.slice()
1967
+ : tslib.__assign({}, existingWhereRaw)
1968
+ : existingWhereRaw;
1969
+ var hasExistingWhere = Array.isArray(normalizedExistingWhere)
1970
+ ? normalizedExistingWhere.length > 0
1971
+ : normalizedExistingWhere && typeof normalizedExistingWhere === 'object'
1972
+ ? Object.keys(normalizedExistingWhere).length > 0
1973
+ : normalizedExistingWhere != null;
1974
+ var whereClause;
1975
+ if (!hasExistingWhere) {
1976
+ whereClause = correlationCondition;
1977
+ }
1978
+ else if (normalizedExistingWhere && typeof normalizedExistingWhere === 'object' &&
1979
+ Object.keys(normalizedExistingWhere).some(function (key) { return _this.BOOLEAN_OPERATORS.has(key); })) {
1980
+ whereClause = (_b = {}, _b[C6C.AND] = [normalizedExistingWhere, correlationCondition], _b);
1981
+ }
1982
+ else if (normalizedExistingWhere && typeof normalizedExistingWhere === 'object') {
1983
+ whereClause = tslib.__assign(tslib.__assign({}, normalizedExistingWhere), correlationCondition);
1984
+ }
1985
+ else {
1986
+ whereClause = (_c = {}, _c[C6C.AND] = [normalizedExistingWhere, correlationCondition], _c);
1987
+ }
1988
+ var subRequestWithCorrelation = tslib.__assign(tslib.__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));
1989
+ var buildScalarSubSelect = this.buildScalarSubSelect;
1990
+ if (typeof buildScalarSubSelect !== 'function') {
1991
+ throw new Error('EXISTS operator requires SelectQueryBuilder context.');
1992
+ }
1993
+ var scalar = buildScalarSubSelect.call(this, subRequestWithCorrelation, params);
1994
+ var keyword = operator === 'NOT EXISTS' ? 'NOT EXISTS' : C6C.EXISTS;
1995
+ return "".concat(keyword, " ").concat(scalar);
1996
+ };
1772
1997
  ConditionBuilder.prototype.buildOperatorExpression = function (op, rawOperands, params, contextColumn) {
1773
1998
  var _a, _b;
1774
1999
  var _this = this;
1775
2000
  var operator = this.formatOperator(op);
2001
+ if (operator === C6C.EXISTS || operator === 'NOT EXISTS') {
2002
+ var operands_1 = Array.isArray(rawOperands) ? rawOperands : [rawOperands];
2003
+ if (!operands_1.length) {
2004
+ throw new Error("".concat(operator, " requires at least one subselect specification."));
2005
+ }
2006
+ var clauses = operands_1.map(function (spec) { return _this.buildExistsExpression(spec, operator, params); });
2007
+ return this.joinBooleanParts(clauses, 'AND');
2008
+ }
1776
2009
  if (operator === C6C.MATCH_AGAINST) {
1777
2010
  if (!Array.isArray(rawOperands) || rawOperands.length !== 2) {
1778
2011
  throw new Error('MATCH_AGAINST requires an array of two operands.');
@@ -1882,6 +2115,18 @@ var ConditionBuilder = /** @class */ (function (_super) {
1882
2115
  if (value instanceof Map) {
1883
2116
  value = Object.fromEntries(value);
1884
2117
  }
2118
+ if (typeof column === 'string') {
2119
+ var normalizedColumn = column.trim().toUpperCase();
2120
+ if (this.BOOLEAN_FUNCTION_KEYS.has(normalizedColumn)) {
2121
+ if (!Array.isArray(value)) {
2122
+ throw new Error("".concat(column, " expects an array of arguments."));
2123
+ }
2124
+ return this.buildFunctionCall(column, value, params);
2125
+ }
2126
+ }
2127
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(value)) {
2128
+ return this.buildOperatorExpression(C6C.EQUAL, [column, value], params, column);
2129
+ }
1885
2130
  if (Array.isArray(value)) {
1886
2131
  if (value.length >= 2 && typeof value[0] === 'string') {
1887
2132
  var op = value[0], rest = value.slice(1);
@@ -2054,6 +2299,143 @@ var JoinBuilder = /** @class */ (function (_super) {
2054
2299
  JoinBuilder.prototype.createSelectBuilder = function (_request) {
2055
2300
  throw new Error('Subclasses must implement createSelectBuilder to support derived table serialization.');
2056
2301
  };
2302
+ JoinBuilder.prototype.resetIndexHints = function () {
2303
+ this.indexHintCache = undefined;
2304
+ };
2305
+ JoinBuilder.prototype.normalizeIndexHintKey = function (key) {
2306
+ return key
2307
+ .replace(/`/g, '')
2308
+ .replace(/_/g, ' ')
2309
+ .trim()
2310
+ .replace(/\s+/g, ' ')
2311
+ .toUpperCase();
2312
+ };
2313
+ JoinBuilder.prototype.normalizeHintTargetKey = function (target) {
2314
+ return target.replace(/`/g, '').trim();
2315
+ };
2316
+ JoinBuilder.prototype.hasIndexHintKeys = function (obj) {
2317
+ var _this = this;
2318
+ var keys = Object.keys(obj !== null && obj !== void 0 ? obj : {});
2319
+ if (!keys.length)
2320
+ return false;
2321
+ var forceKey = this.normalizeIndexHintKey(C6C.FORCE_INDEX);
2322
+ var useKey = this.normalizeIndexHintKey(C6C.USE_INDEX);
2323
+ var ignoreKey = this.normalizeIndexHintKey(C6C.IGNORE_INDEX);
2324
+ return keys.some(function (key) {
2325
+ var normalized = _this.normalizeIndexHintKey(key);
2326
+ return normalized === forceKey || normalized === useKey || normalized === ignoreKey;
2327
+ });
2328
+ };
2329
+ JoinBuilder.prototype.normalizeHintSpec = function (spec) {
2330
+ var _a;
2331
+ if (spec instanceof Map) {
2332
+ spec = Object.fromEntries(spec);
2333
+ }
2334
+ if (Array.isArray(spec) || typeof spec === 'string') {
2335
+ return _a = {}, _a[C6C.FORCE_INDEX] = spec, _a;
2336
+ }
2337
+ if (!spec || typeof spec !== 'object') {
2338
+ return undefined;
2339
+ }
2340
+ if (!this.hasIndexHintKeys(spec)) {
2341
+ return undefined;
2342
+ }
2343
+ return spec;
2344
+ };
2345
+ JoinBuilder.prototype.formatIndexHintClause = function (spec) {
2346
+ var normalizedSpec = this.normalizeHintSpec(spec);
2347
+ if (!normalizedSpec)
2348
+ return '';
2349
+ var clauses = [];
2350
+ var forceKey = this.normalizeIndexHintKey(C6C.FORCE_INDEX);
2351
+ var useKey = this.normalizeIndexHintKey(C6C.USE_INDEX);
2352
+ var ignoreKey = this.normalizeIndexHintKey(C6C.IGNORE_INDEX);
2353
+ var pushClause = function (keyword, rawValue) {
2354
+ var values = Array.isArray(rawValue) ? rawValue : [rawValue];
2355
+ var indexes = values
2356
+ .map(function (value) { return String(value !== null && value !== void 0 ? value : '').trim(); })
2357
+ .filter(Boolean)
2358
+ .map(function (value) { return "`".concat(value.replace(/`/g, '``'), "`"); });
2359
+ if (!indexes.length)
2360
+ return;
2361
+ clauses.push("".concat(keyword, " (").concat(indexes.join(', '), ")"));
2362
+ };
2363
+ for (var _i = 0, _a = Object.entries(normalizedSpec); _i < _a.length; _i++) {
2364
+ var _b = _a[_i], key = _b[0], rawValue = _b[1];
2365
+ var normalizedKey = this.normalizeIndexHintKey(key);
2366
+ if (normalizedKey === forceKey) {
2367
+ pushClause('FORCE INDEX', rawValue);
2368
+ }
2369
+ else if (normalizedKey === useKey) {
2370
+ pushClause('USE INDEX', rawValue);
2371
+ }
2372
+ else if (normalizedKey === ignoreKey) {
2373
+ pushClause('IGNORE INDEX', rawValue);
2374
+ }
2375
+ }
2376
+ return clauses.join(' ');
2377
+ };
2378
+ JoinBuilder.prototype.normalizeIndexHints = function (raw) {
2379
+ var _this = this;
2380
+ if (raw instanceof Map) {
2381
+ raw = Object.fromEntries(raw);
2382
+ }
2383
+ var cache = new Map();
2384
+ var addEntry = function (target, spec) {
2385
+ var clause = _this.formatIndexHintClause(spec);
2386
+ if (!clause)
2387
+ return;
2388
+ var normalizedTarget = target === '__base__'
2389
+ ? '__base__'
2390
+ : _this.normalizeHintTargetKey(target);
2391
+ cache.set(normalizedTarget, clause);
2392
+ };
2393
+ if (Array.isArray(raw) || typeof raw === 'string') {
2394
+ addEntry('__base__', raw);
2395
+ }
2396
+ else if (raw && typeof raw === 'object') {
2397
+ if (this.hasIndexHintKeys(raw)) {
2398
+ addEntry('__base__', raw);
2399
+ }
2400
+ else {
2401
+ for (var _i = 0, _a = Object.entries(raw); _i < _a.length; _i++) {
2402
+ var _b = _a[_i], key = _b[0], value = _b[1];
2403
+ var normalizedKey = this.normalizeHintTargetKey(key);
2404
+ if (!normalizedKey)
2405
+ continue;
2406
+ addEntry(normalizedKey, value);
2407
+ }
2408
+ }
2409
+ }
2410
+ return cache.size ? cache : undefined;
2411
+ };
2412
+ JoinBuilder.prototype.getIndexHintClause = function (table, alias) {
2413
+ var _a;
2414
+ if (!this.indexHintCache) {
2415
+ var rawHints = (_a = this.request) === null || _a === void 0 ? void 0 : _a[C6C.INDEX_HINTS];
2416
+ this.indexHintCache = this.normalizeIndexHints(rawHints);
2417
+ }
2418
+ var hints = this.indexHintCache;
2419
+ if (!hints || hints.size === 0)
2420
+ return '';
2421
+ var normalizedTable = this.normalizeHintTargetKey(table);
2422
+ var normalizedAlias = alias ? this.normalizeHintTargetKey(alias) : undefined;
2423
+ var candidates = [
2424
+ normalizedAlias,
2425
+ normalizedAlias ? "".concat(normalizedTable, " ").concat(normalizedAlias) : undefined,
2426
+ normalizedTable,
2427
+ '__base__',
2428
+ ];
2429
+ for (var _i = 0, candidates_1 = candidates; _i < candidates_1.length; _i++) {
2430
+ var candidate = candidates_1[_i];
2431
+ if (!candidate)
2432
+ continue;
2433
+ var clause = hints.get(candidate);
2434
+ if (clause)
2435
+ return clause;
2436
+ }
2437
+ return '';
2438
+ };
2057
2439
  JoinBuilder.prototype.buildJoinClauses = function (joinArgs, params) {
2058
2440
  var sql = '';
2059
2441
  var joinTypeEntries = joinArgs instanceof Map
@@ -2117,7 +2499,9 @@ var JoinBuilder = /** @class */ (function (_super) {
2117
2499
  if (alias) {
2118
2500
  this_1.registerAlias(alias, table);
2119
2501
  }
2120
- var joinSql = alias ? "`".concat(table, "` AS `").concat(alias, "`") : "`".concat(table, "`");
2502
+ var hintClause = this_1.getIndexHintClause(table, alias);
2503
+ var baseJoinSql = alias ? "`".concat(table, "` AS `").concat(alias, "`") : "`".concat(table, "`");
2504
+ var joinSql = hintClause ? "".concat(baseJoinSql, " ").concat(hintClause) : baseJoinSql;
2121
2505
  var onClause = this_1.buildBooleanJoinedConditions(conditions, true, params);
2122
2506
  sql += " ".concat(joinKind, " JOIN ").concat(joinSql);
2123
2507
  if (onClause) {
@@ -2271,6 +2655,7 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
2271
2655
  // @ts-ignore
2272
2656
  if (this.selectAliases && this.selectAliases.clear)
2273
2657
  this.selectAliases.clear();
2658
+ this.resetIndexHints();
2274
2659
  var args = this.request;
2275
2660
  this.initAlias(table, args.JOIN);
2276
2661
  var params = this.useNamedParams ? {} : [];
@@ -2279,6 +2664,10 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
2279
2664
  .map(function (f) { return _this.buildAggregateField(f, params); })
2280
2665
  .join(', ');
2281
2666
  var sql = "SELECT ".concat(selectFields, " FROM `").concat(table, "`");
2667
+ var baseIndexHint = this.getIndexHintClause(table);
2668
+ if (baseIndexHint) {
2669
+ sql += " ".concat(baseIndexHint);
2670
+ }
2282
2671
  if (args.JOIN) {
2283
2672
  sql += this.buildJoinClauses(args.JOIN, params);
2284
2673
  }
@@ -2350,12 +2739,14 @@ var PostQueryBuilder = /** @class */ (function (_super) {
2350
2739
  var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
2351
2740
  var body = verb in this.request ? this.request[verb] : this.request;
2352
2741
  var keys = Object.keys(body);
2353
- var params = [];
2742
+ var params = this.useNamedParams ? {} : [];
2354
2743
  var placeholders = [];
2355
2744
  for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
2356
2745
  var key = keys_1[_i];
2357
2746
  var value = body[key];
2358
- var placeholder = this.addParam(params, key, value);
2747
+ var trimmed = this.trimTablePrefix(table, key);
2748
+ var qualified = "".concat(table, ".").concat(trimmed);
2749
+ var placeholder = this.serializeUpdateValue(value, params, qualified);
2359
2750
  placeholders.push(placeholder);
2360
2751
  }
2361
2752
  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 )");
@@ -2407,7 +2798,9 @@ var UpdateQueryBuilder = /** @class */ (function (_super) {
2407
2798
  var col = _a[0], val = _a[1];
2408
2799
  var trimmed = _this.trimTablePrefix(table, col);
2409
2800
  var qualified = "".concat(table, ".").concat(trimmed);
2410
- return "`".concat(trimmed, "` = ").concat(_this.addParam(params, qualified, val));
2801
+ _this.assertValidIdentifier(qualified, 'UPDATE SET');
2802
+ var rightSql = _this.serializeUpdateValue(val, params, qualified);
2803
+ return "`".concat(trimmed, "` = ").concat(rightSql);
2411
2804
  });
2412
2805
  sql += " SET ".concat(setClauses.join(', '));
2413
2806
  if (args.WHERE) {
@@ -2735,12 +3128,12 @@ function ExpressHandler(_a) {
2735
3128
  var _this = this;
2736
3129
  var C6 = _a.C6, mysqlPool = _a.mysqlPool;
2737
3130
  return function (req, res, next) { return tslib.__awaiter(_this, void 0, void 0, function () {
2738
- var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, primaryKeys, primaryKeyName, response, err_1;
2739
- var _a, _b, _c, _d, _e;
2740
- return tslib.__generator(this, function (_f) {
2741
- switch (_f.label) {
3131
+ var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, restModel, primaryKeys_1, primaryShortKeys_1, columnMap_1, resolveShortKey_1, hasPrimaryKeyValues, primaryKeyName, response, err_1;
3132
+ var _a, _b, _c, _d, _e, _f, _g;
3133
+ return tslib.__generator(this, function (_h) {
3134
+ switch (_h.label) {
2742
3135
  case 0:
2743
- _f.trys.push([0, 2, , 3]);
3136
+ _h.trys.push([0, 2, , 3]);
2744
3137
  incomingMethod = req.method.toUpperCase();
2745
3138
  table = req.params.table;
2746
3139
  primary = req.params.primary;
@@ -2754,7 +3147,7 @@ function ExpressHandler(_a) {
2754
3147
  try {
2755
3148
  delete payload.METHOD;
2756
3149
  }
2757
- catch ( /* noop */_g) { /* noop */ }
3150
+ catch ( /* noop */_j) { /* noop */ }
2758
3151
  }
2759
3152
  // Warn for unsupported overrides but continue normally
2760
3153
  if (incomingMethod !== 'GET' && methodOverride && methodOverride !== 'GET') {
@@ -2764,29 +3157,56 @@ function ExpressHandler(_a) {
2764
3157
  res.status(400).json({ error: "Invalid table: ".concat(table) });
2765
3158
  return [2 /*return*/];
2766
3159
  }
2767
- primaryKeys = C6.TABLES[table].PRIMARY;
2768
- if (primary && primaryKeys.length !== 1) {
2769
- if (primaryKeys.length > 1) {
3160
+ restModel = C6.TABLES[table];
3161
+ primaryKeys_1 = restModel.PRIMARY;
3162
+ primaryShortKeys_1 = (_d = restModel.PRIMARY_SHORT) !== null && _d !== void 0 ? _d : [];
3163
+ columnMap_1 = (_e = restModel.COLUMNS) !== null && _e !== void 0 ? _e : {};
3164
+ 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; };
3165
+ hasPrimaryKeyValues = function (data) {
3166
+ if (!data || typeof data !== 'object')
3167
+ return false;
3168
+ var whereClause = data[C6C.WHERE];
3169
+ var hasKeyValue = function (obj, fullKey, shortKey) {
3170
+ if (!obj || typeof obj !== 'object')
3171
+ return false;
3172
+ var fullValue = obj[fullKey];
3173
+ if (fullValue !== undefined && fullValue !== null)
3174
+ return true;
3175
+ var shortValue = shortKey ? obj[shortKey] : undefined;
3176
+ return shortValue !== undefined && shortValue !== null;
3177
+ };
3178
+ return primaryKeys_1.every(function (fullKey, index) {
3179
+ var shortKey = resolveShortKey_1(fullKey, index);
3180
+ return hasKeyValue(whereClause, fullKey, shortKey) || hasKeyValue(data, fullKey, shortKey);
3181
+ });
3182
+ };
3183
+ if (primary && primaryKeys_1.length !== 1) {
3184
+ if (primaryKeys_1.length > 1 && hasPrimaryKeyValues(payload)) {
3185
+ primary = undefined;
3186
+ }
3187
+ else if (primaryKeys_1.length > 1) {
2770
3188
  res.status(400).json({ error: "Table ".concat(table, " has multiple primary keys. Cannot implicitly determine key.") });
2771
3189
  return [2 /*return*/];
2772
3190
  }
2773
- res.status(400).json({
2774
- error: "Table ".concat(table, " has no primary keys. Please specify one.")
2775
- });
2776
- return [2 /*return*/];
3191
+ else {
3192
+ res.status(400).json({
3193
+ error: "Table ".concat(table, " has no primary keys. Please specify one.")
3194
+ });
3195
+ return [2 /*return*/];
3196
+ }
2777
3197
  }
2778
- primaryKeyName = primaryKeys[0];
3198
+ primaryKeyName = primaryKeys_1[0];
2779
3199
  // If a primary key was provided in the URL, merge it into the payload.
2780
3200
  // Support both complex requests using WHERE and singular requests
2781
3201
  // where the primary key lives at the root of the payload.
2782
3202
  if (primary) {
2783
3203
  if (payload[C6C.WHERE]) {
2784
3204
  payload[C6C.WHERE][primaryKeyName] =
2785
- (_d = payload[C6C.WHERE][primaryKeyName]) !== null && _d !== void 0 ? _d : primary;
3205
+ (_f = payload[C6C.WHERE][primaryKeyName]) !== null && _f !== void 0 ? _f : primary;
2786
3206
  }
2787
3207
  else {
2788
3208
  payload[primaryKeyName] =
2789
- (_e = payload[primaryKeyName]) !== null && _e !== void 0 ? _e : primary;
3209
+ (_g = payload[primaryKeyName]) !== null && _g !== void 0 ? _g : primary;
2790
3210
  }
2791
3211
  }
2792
3212
  return [4 /*yield*/, restRequest({
@@ -2796,11 +3216,11 @@ function ExpressHandler(_a) {
2796
3216
  restModel: C6.TABLES[table]
2797
3217
  })(payload)];
2798
3218
  case 1:
2799
- response = _f.sent();
3219
+ response = _h.sent();
2800
3220
  res.status(200).json(tslib.__assign({ success: true }, response));
2801
3221
  return [3 /*break*/, 3];
2802
3222
  case 2:
2803
- err_1 = _f.sent();
3223
+ err_1 = _h.sent();
2804
3224
  res.status(500).json({ success: false, error: err_1 });
2805
3225
  next(err_1);
2806
3226
  return [3 /*break*/, 3];
@@ -2885,10 +3305,11 @@ function error(message) {
2885
3305
  }
2886
3306
 
2887
3307
  function checkAllRequestsComplete() {
2888
- var stillRunning = exports.apiRequestCache.filter(function (cache) { return undefined === cache.response; });
3308
+ var cacheEntries = Array.from(apiRequestCache.values());
3309
+ var stillRunning = cacheEntries.filter(function (cache) { return undefined === cache.response; });
2889
3310
  if (stillRunning.length !== 0) {
2890
3311
  if (document === null || document === undefined) {
2891
- throw new Error('document is undefined while waiting for API requests to complete (' + JSON.stringify(exports.apiRequestCache) + ')');
3312
+ throw new Error('document is undefined while waiting for API requests to complete (' + JSON.stringify(cacheEntries) + ')');
2892
3313
  }
2893
3314
  // when requests return emtpy sets in full renders, it may not be possible to track their progress.
2894
3315
  console.warn('stillRunning...', stillRunning);
@@ -2930,6 +3351,7 @@ exports.SelectQueryBuilder = SelectQueryBuilder;
2930
3351
  exports.SqlExecutor = SqlExecutor;
2931
3352
  exports.TestRestfulResponse = TestRestfulResponse;
2932
3353
  exports.UpdateQueryBuilder = UpdateQueryBuilder;
3354
+ exports.apiRequestCache = apiRequestCache;
2933
3355
  exports.axiosInstance = axiosInstance;
2934
3356
  exports.bbox = bbox;
2935
3357
  exports.carbonNodeQsStringify = carbonNodeQsStringify;
@@ -2960,10 +3382,12 @@ exports.removePrefixIfExists = removePrefixIfExists;
2960
3382
  exports.resolveDerivedTable = resolveDerivedTable;
2961
3383
  exports.restOrm = restOrm;
2962
3384
  exports.restRequest = restRequest;
3385
+ exports.setCache = setCache;
2963
3386
  exports.sortAndSerializeQueryObject = sortAndSerializeQueryObject;
2964
3387
  exports.stContains = stContains;
2965
3388
  exports.timeout = timeout;
2966
3389
  exports.toastOptions = toastOptions;
2967
3390
  exports.toastOptionsDevs = toastOptionsDevs;
3391
+ exports.userCustomClearCache = userCustomClearCache;
2968
3392
  exports.warn = warn;
2969
3393
  //# sourceMappingURL=index.cjs.js.map