@carbonorm/carbonnode 3.11.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -307,8 +307,30 @@ axiosInstance.interceptors.request.use(function (config) {
307
307
  };
308
308
  }
309
309
  }
310
+ config.__carbonStart = performance.now();
310
311
  return config;
311
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
+ });
312
334
 
313
335
  function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler) {
314
336
  if (regexErrorHandler === void 0) { regexErrorHandler = alert; }
@@ -410,10 +432,7 @@ function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler)
410
432
 
411
433
  var isNode = function () {
412
434
  var _a;
413
- console.log('Checking if running in Node.js environment...');
414
- var isNodeEnv = typeof process !== 'undefined' && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
415
- console.log("Is Node.js environment: ".concat(isNodeEnv));
416
- return isNodeEnv;
435
+ return typeof process !== 'undefined' && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
417
436
  };
418
437
 
419
438
  /**
@@ -631,26 +650,64 @@ function removeInvalidKeys(request, c6Tables) {
631
650
  return intersection;
632
651
  }
633
652
 
634
- // do not remove entries from this array. It is used to track the progress of API requests.
635
- // position in array is important. Do not sort. To not add to begging.
636
- exports.apiRequestCache = [];
637
- 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
+ // -----------------------------------------------------------------------------
638
679
  function clearCache(props) {
639
- if (false === (props === null || props === void 0 ? void 0 : props.ignoreWarning)) {
640
- 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) { }
641
689
  }
642
- exports.userCustomClearCache.map(function (f) { return 'function' === typeof f && f(); });
643
- exports.userCustomClearCache = exports.apiRequestCache = [];
690
+ apiRequestCache.clear();
644
691
  }
645
- function checkCache(cacheResult, requestMethod, tableName, request) {
646
- 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)
647
699
  return false;
648
- }
649
- console.groupCollapsed('%c API: The request on (' + tableName + ') is in cache. Returning the request Promise!', 'color: #0c0');
650
- console.log('%c ' + requestMethod + ' ' + tableName, 'color: #0c0');
651
- 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);
652
702
  console.groupEnd();
653
- 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);
654
711
  }
655
712
 
656
713
  function sortAndSerializeQueryObject(tables, query) {
@@ -667,7 +724,10 @@ var HttpExecutor = /** @class */ (function (_super) {
667
724
  return _super !== null && _super.apply(this, arguments) || this;
668
725
  }
669
726
  HttpExecutor.prototype.isRestResponse = function (r) {
670
- 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);
671
731
  };
672
732
  HttpExecutor.prototype.stripTableNameFromKeys = function (obj) {
673
733
  var _a;
@@ -763,26 +823,16 @@ var HttpExecutor = /** @class */ (function (_super) {
763
823
  throw Error('Bad request method passed to getApi');
764
824
  }
765
825
  if (clearCache != null) {
766
- exports.userCustomClearCache.push(clearCache);
826
+ userCustomClearCache.push(clearCache);
767
827
  }
768
828
  if (isLocal() && (this.config.verbose || ((_b = this.request) === null || _b === void 0 ? void 0 : _b.debug))) {
769
829
  console.groupCollapsed('%c API:', 'color: #0c0', "(".concat(requestMethod, ") Request for (").concat(tableName, ")"));
770
830
  console.log('request', this.request);
771
831
  console.groupEnd();
772
832
  }
773
- // an undefined query would indicate queryCallback returned undefined,
774
- // thus the request shouldn't fire as is in custom cache
775
- if (undefined === this.request || null === this.request) {
776
- if (isLocal()) {
777
- console.groupCollapsed("API: (".concat(requestMethod, ") (").concat(tableName, ") query undefined/null \u2192 returning null"));
778
- console.log('request', this.request);
779
- console.groupEnd();
780
- }
781
- return [2 /*return*/, null];
782
- }
783
833
  query = this.request;
784
834
  apiRequest = function () { return tslib.__awaiter(_this, void 0, void 0, function () {
785
- 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;
786
836
  var _e;
787
837
  var _this = this;
788
838
  var _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
@@ -798,63 +848,37 @@ var HttpExecutor = /** @class */ (function (_super) {
798
848
  console.log('request', this.request);
799
849
  console.groupEnd();
800
850
  }
801
- querySerialized = sortAndSerializeQueryObject(tables, query !== null && query !== void 0 ? query : {});
802
- cacheResult = exports.apiRequestCache.find(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
803
851
  cachingConfirmed = false;
804
- if (!(requestMethod === C6.GET)) return [3 /*break*/, 9];
805
- if (undefined === (query === null || query === void 0 ? void 0 : query[C6.PAGINATION])) {
806
- if (undefined === query || null === query) {
807
- 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] = {};
808
859
  }
809
- 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);
810
868
  }
811
- query[C6.PAGINATION][C6.PAGE] = query[C6.PAGINATION][C6.PAGE] || 1;
812
- query[C6.PAGINATION][C6.LIMIT] = query[C6.PAGINATION][C6.LIMIT] || 100;
813
- if (!(true === cacheResults)) return [3 /*break*/, 7];
814
- if (!(undefined !== cacheResult)) return [3 /*break*/, 6];
815
- _r.label = 1;
816
- case 1:
817
- cacheCheck = checkCache(cacheResult, requestMethod, tableName, this.request);
818
- if (!(false !== cacheCheck)) return [3 /*break*/, 3];
819
- return [4 /*yield*/, cacheCheck];
820
- case 2: return [2 /*return*/, (_r.sent()).data];
821
- case 3:
822
- ++query[C6.PAGINATION][C6.PAGE];
823
- querySerialized = sortAndSerializeQueryObject(tables, query !== null && query !== void 0 ? query : {});
824
- cacheResult = exports.apiRequestCache.find(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
825
- _r.label = 4;
826
- case 4:
827
- if (undefined !== cacheResult) return [3 /*break*/, 1];
828
- _r.label = 5;
829
- case 5:
830
- if (debug && isLocal()) {
831
- 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;
832
875
  }
833
- _r.label = 6;
834
- case 6:
835
- cachingConfirmed = true;
836
- return [3 /*break*/, 8];
837
- case 7:
838
- if (debug && isLocal())
876
+ else if (debug && isLocal()) {
839
877
  reactToastify.toast.info("DEVS: Ignore cache was set to true.", toastOptionsDevs);
840
- _r.label = 8;
841
- case 8:
842
- if (debug && isLocal()) {
878
+ }
879
+ if (cacheResults && debug && isLocal()) {
843
880
  reactToastify.toast.success("DEVS: Request not in cache." + (requestMethod === C6.GET ? " Page (" + query[C6.PAGINATION][C6.PAGE] + ")" : ''), toastOptionsDevs);
844
881
  }
845
- return [3 /*break*/, 12];
846
- case 9:
847
- if (!cacheResults) return [3 /*break*/, 12];
848
- if (!cacheResult) return [3 /*break*/, 11];
849
- cacheCheck = checkCache(cacheResult, requestMethod, tableName, this.request);
850
- if (!(false !== cacheCheck)) return [3 /*break*/, 11];
851
- return [4 /*yield*/, cacheCheck];
852
- case 10: return [2 /*return*/, (_r.sent()).data];
853
- case 11:
854
- cachingConfirmed = true;
855
- _r.label = 12;
856
- case 12:
857
- returnGetNextPageFunction = false;
858
882
  restRequestUri = restURL + operatingTable + '/';
859
883
  needsConditionOrPrimaryCheck = (PUT === requestMethod || DELETE === requestMethod)
860
884
  && false === skipPrimaryCheck;
@@ -924,7 +948,7 @@ var HttpExecutor = /** @class */ (function (_super) {
924
948
  config: this.config,
925
949
  request: this.request
926
950
  });
927
- axiosActiveRequest = (_e = axios)[requestMethod.toLowerCase()].apply(_e, tslib.__spreadArray([restRequestUri], (function () {
951
+ axiosActiveRequest_1 = (_e = axios)[requestMethod.toLowerCase()].apply(_e, tslib.__spreadArray([restRequestUri], (function () {
928
952
  var convert = function (data) {
929
953
  return convertForRequestBody(data, fullTableList, C6, function (message) { return reactToastify.toast.error(message, toastOptions); });
930
954
  };
@@ -951,23 +975,30 @@ var HttpExecutor = /** @class */ (function (_super) {
951
975
  }
952
976
  })(), false));
953
977
  if (cachingConfirmed) {
954
- // push to cache so we do not repeat the request
955
- exports.apiRequestCache.push({
978
+ setCache(requestMethod, tableName, cacheRequestData, {
956
979
  requestArgumentsSerialized: querySerialized,
957
- request: axiosActiveRequest
980
+ request: axiosActiveRequest_1,
958
981
  });
959
982
  }
960
983
  // returning the promise with this then is important for tests. todo - we could make that optional.
961
984
  // https://rapidapi.com/guides/axios-async-await
962
- return [2 /*return*/, axiosActiveRequest.then(function (response) { return tslib.__awaiter(_this, void 0, void 0, function () {
963
- 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;
964
987
  var _this = this;
965
- var _a, _b, _c, _d;
966
- return tslib.__generator(this, function (_e) {
967
- switch (_e.label) {
988
+ var _a, _b, _c, _d, _e;
989
+ return tslib.__generator(this, function (_f) {
990
+ switch (_f.label) {
968
991
  case 0:
969
992
  // noinspection SuspiciousTypeOfGuard
970
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
+ }
971
1002
  if (isTest()) {
972
1003
  console.trace();
973
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 + ')');
@@ -975,11 +1006,11 @@ var HttpExecutor = /** @class */ (function (_super) {
975
1006
  return [2 /*return*/, Promise.reject(response)];
976
1007
  }
977
1008
  if (cachingConfirmed) {
978
- cacheIndex = exports.apiRequestCache.findIndex(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
979
- // TODO - currently nonthing is setting this correctly
980
- exports.apiRequestCache[cacheIndex].final = false === returnGetNextPageFunction;
981
- // only cache get method requests
982
- exports.apiRequestCache[cacheIndex].response = response;
1009
+ setCache(requestMethod, tableName, cacheRequestData, {
1010
+ requestArgumentsSerialized: querySerialized,
1011
+ request: axiosActiveRequest_1,
1012
+ response: response,
1013
+ });
983
1014
  }
984
1015
  this.runLifecycleHooks("afterExecution", {
985
1016
  config: this.config,
@@ -1030,17 +1061,18 @@ var HttpExecutor = /** @class */ (function (_super) {
1030
1061
  got = responseData_1.rest.length;
1031
1062
  hasNext = pageLimit !== 1 && got === pageLimit;
1032
1063
  if (hasNext) {
1033
- responseData_1.next = apiRequest; // there might be more
1064
+ responseData_1.next = apiRequest;
1034
1065
  }
1035
1066
  else {
1036
1067
  responseData_1.next = undefined; // short page => done
1037
1068
  }
1038
- // If you keep this flag, make it reflect reality:
1039
- returnGetNextPageFunction = hasNext;
1040
- // and fix cache ‘final’ flag to match:
1041
1069
  if (cachingConfirmed) {
1042
- cacheIndex = exports.apiRequestCache.findIndex(function (c) { return c.requestArgumentsSerialized === querySerialized; });
1043
- exports.apiRequestCache[cacheIndex].final = !hasNext;
1070
+ setCache(requestMethod, tableName, cacheRequestData, {
1071
+ requestArgumentsSerialized: querySerialized,
1072
+ request: axiosActiveRequest_1,
1073
+ response: response,
1074
+ final: !hasNext,
1075
+ });
1044
1076
  }
1045
1077
  if ((this.config.verbose || debug) && isLocal()) {
1046
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], ")"));
@@ -1050,7 +1082,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1050
1082
  }
1051
1083
  if (!(fetchDependencies
1052
1084
  && 'number' === typeof fetchDependencies
1053
- && 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];
1054
1086
  console.groupCollapsed('%c API: Fetch Dependencies segment (' + requestMethod + ' ' + tableName + ')'
1055
1087
  + (fetchDependencies & exports.eFetchDependencies.CHILDREN ? ' | (CHILDREN|REFERENCED) ' : '')
1056
1088
  + (fetchDependencies & exports.eFetchDependencies.PARENTS ? ' | (PARENTS|REFERENCED_BY)' : '')
@@ -1121,7 +1153,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1121
1153
  }); });
1122
1154
  console.log('fetchReferences', fetchReferences_1);
1123
1155
  _loop_1 = function (tableToFetch) {
1124
- var _f;
1156
+ var _g;
1125
1157
  if (fetchDependencies & exports.eFetchDependencies.C6ENTITY
1126
1158
  && 'string' === typeof tableName
1127
1159
  && tableName.endsWith("carbon_carbons")) {
@@ -1144,7 +1176,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1144
1176
  .split('_')
1145
1177
  .map(function (part) { return part.charAt(0).toUpperCase() + part.slice(1); })
1146
1178
  .join('_');
1147
- 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(', '), " ]"));
1148
1180
  console.log('%c Fetch Dependencies will select (' + tableToFetch + ') using GET request', 'color: #33ccff');
1149
1181
  var nextFetchDependencies = exports.eFetchDependencies.NONE;
1150
1182
  if (fetchDependencies & exports.eFetchDependencies.RECURSIVE) {
@@ -1170,8 +1202,8 @@ var HttpExecutor = /** @class */ (function (_super) {
1170
1202
  console.log('RestApi object', RestApi);
1171
1203
  // this is a dynamic call to the rest api, any generated table may resolve with (RestApi)
1172
1204
  // todo - using value to avoid joins.... but. maybe this should be a parameterizable option -- think race conditions; its safer to join
1173
- apiRequestPromises.push(RestApi.Get((_f = {},
1174
- _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) {
1175
1207
  fetchReferences_1[tableToFetch][column] = fetchReferences_1[tableToFetch][column].flat(Infinity);
1176
1208
  if (0 === fetchReferences_1[tableToFetch][column].length) {
1177
1209
  console.warn('The column (' + column + ') was not found in the response data. We will not fetch.', responseData_1);
@@ -1184,8 +1216,8 @@ var HttpExecutor = /** @class */ (function (_super) {
1184
1216
  ];
1185
1217
  return sum;
1186
1218
  }, {}),
1187
- _f.fetchDependencies = nextFetchDependencies,
1188
- _f)));
1219
+ _g.fetchDependencies = nextFetchDependencies,
1220
+ _g)));
1189
1221
  };
1190
1222
  for (tableToFetch in fetchReferences_1) {
1191
1223
  _loop_1(tableToFetch);
@@ -1193,7 +1225,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1193
1225
  console.groupEnd();
1194
1226
  return [4 /*yield*/, Promise.all(apiRequestPromises)];
1195
1227
  case 1:
1196
- _e.sent();
1228
+ _f.sent();
1197
1229
  apiRequestPromises.map(function (promise) { return tslib.__awaiter(_this, void 0, void 0, function () {
1198
1230
  var _a, _b;
1199
1231
  return tslib.__generator(this, function (_c) {
@@ -1211,8 +1243,16 @@ var HttpExecutor = /** @class */ (function (_super) {
1211
1243
  }
1212
1244
  });
1213
1245
  }); });
1214
- _e.label = 2;
1246
+ _f.label = 2;
1215
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
+ }
1216
1256
  if (debug && isLocal()) {
1217
1257
  reactToastify.toast.success("DEVS: (" + requestMethod + ") request complete.", toastOptionsDevs);
1218
1258
  }
@@ -1223,16 +1263,12 @@ var HttpExecutor = /** @class */ (function (_super) {
1223
1263
  }); }).then(function (response) { return response.data; })]; // this escapes from axios context
1224
1264
  }
1225
1265
  catch (throwableError) {
1226
- if (isTest()) {
1227
- throw new Error(JSON.stringify(throwableError));
1228
- }
1229
1266
  console.groupCollapsed('%c API: An error occurred in the try catch block. returning null!', 'color: #ff0000');
1230
1267
  console.log('%c ' + requestMethod + ' ' + tableName, 'color: #A020F0');
1231
- console.warn(throwableError);
1268
+ console.error(throwableError);
1232
1269
  console.trace();
1233
1270
  console.groupEnd();
1234
- TestRestfulResponse(throwableError, success, error);
1235
- return [2 /*return*/, null];
1271
+ throw new Error(JSON.stringify(throwableError));
1236
1272
  }
1237
1273
  return [2 /*return*/];
1238
1274
  }
@@ -1791,6 +1827,84 @@ var ConditionBuilder = /** @class */ (function (_super) {
1791
1827
  }
1792
1828
  throw new Error('Unsupported operand type in SQL expression.');
1793
1829
  };
1830
+ ConditionBuilder.prototype.isPlainArrayLiteral = function (value, allowColumnRefs) {
1831
+ var _this = this;
1832
+ if (allowColumnRefs === void 0) { allowColumnRefs = false; }
1833
+ if (!Array.isArray(value))
1834
+ return false;
1835
+ return value.every(function (item) {
1836
+ if (item === null)
1837
+ return true;
1838
+ var type = typeof item;
1839
+ if (type === 'string' || type === 'number' || type === 'boolean')
1840
+ return true;
1841
+ if (Array.isArray(item))
1842
+ return _this.isPlainArrayLiteral(item, allowColumnRefs);
1843
+ if (item && typeof item === 'object')
1844
+ return _this.isPlainObjectLiteral(item, allowColumnRefs);
1845
+ return false;
1846
+ });
1847
+ };
1848
+ ConditionBuilder.prototype.isPlainObjectLiteral = function (value, allowColumnRefs) {
1849
+ var _this = this;
1850
+ if (allowColumnRefs === void 0) { allowColumnRefs = false; }
1851
+ if (!value || typeof value !== 'object' || Array.isArray(value))
1852
+ return false;
1853
+ if (value instanceof Date)
1854
+ return false;
1855
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(value))
1856
+ return false;
1857
+ var normalized = value instanceof Map ? Object.fromEntries(value) : value;
1858
+ if (C6C.SUBSELECT in normalized)
1859
+ return false;
1860
+ var entries = Object.entries(normalized);
1861
+ if (entries.length === 0)
1862
+ return true;
1863
+ if (entries.some(function (_a) {
1864
+ var key = _a[0];
1865
+ return _this.isOperator(key) || _this.BOOLEAN_OPERATORS.has(key);
1866
+ })) {
1867
+ return false;
1868
+ }
1869
+ if (!allowColumnRefs) {
1870
+ if (entries.some(function (_a) {
1871
+ var key = _a[0];
1872
+ return typeof key === 'string' && (_this.isColumnRef(key) || key.includes('.'));
1873
+ })) {
1874
+ return false;
1875
+ }
1876
+ }
1877
+ return true;
1878
+ };
1879
+ ConditionBuilder.prototype.resolveColumnDefinition = function (column) {
1880
+ var _a, _b, _c, _d;
1881
+ if (!column || typeof column !== 'string' || !column.includes('.'))
1882
+ return undefined;
1883
+ var _e = column.split('.', 2), prefix = _e[0], colName = _e[1];
1884
+ var tableName = (_a = this.aliasMap[prefix]) !== null && _a !== void 0 ? _a : prefix;
1885
+ var table = (_c = (_b = this.config.C6) === null || _b === void 0 ? void 0 : _b.TABLES) === null || _c === void 0 ? void 0 : _c[tableName];
1886
+ if (!(table === null || table === void 0 ? void 0 : table.TYPE_VALIDATION))
1887
+ return undefined;
1888
+ return (_d = table.TYPE_VALIDATION[colName]) !== null && _d !== void 0 ? _d : table.TYPE_VALIDATION["".concat(tableName, ".").concat(colName)];
1889
+ };
1890
+ ConditionBuilder.prototype.isJsonColumn = function (column) {
1891
+ var columnDef = this.resolveColumnDefinition(column);
1892
+ var mysqlType = columnDef === null || columnDef === void 0 ? void 0 : columnDef.MYSQL_TYPE;
1893
+ return typeof mysqlType === 'string' && mysqlType.toLowerCase().includes('json');
1894
+ };
1895
+ ConditionBuilder.prototype.serializeUpdateValue = function (value, params, contextColumn) {
1896
+ var normalized = value instanceof Map ? Object.fromEntries(value) : value;
1897
+ var allowColumnRefs = this.isJsonColumn(contextColumn);
1898
+ if (this.isPlainArrayLiteral(normalized, allowColumnRefs)
1899
+ || this.isPlainObjectLiteral(normalized, allowColumnRefs)) {
1900
+ return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
1901
+ }
1902
+ var _a = this.serializeOperand(normalized, params, contextColumn), sql = _a.sql, isReference = _a.isReference, isExpression = _a.isExpression, isSubSelect = _a.isSubSelect;
1903
+ if (!isReference && !isExpression && !isSubSelect && typeof normalized === 'object' && normalized !== null) {
1904
+ throw new Error('Unsupported operand type in SQL expression.');
1905
+ }
1906
+ return sql;
1907
+ };
1794
1908
  ConditionBuilder.prototype.ensurePlainObject = function (value) {
1795
1909
  if (value instanceof Map) {
1796
1910
  return Object.fromEntries(value);
@@ -2647,12 +2761,14 @@ var PostQueryBuilder = /** @class */ (function (_super) {
2647
2761
  var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
2648
2762
  var body = verb in this.request ? this.request[verb] : this.request;
2649
2763
  var keys = Object.keys(body);
2650
- var params = [];
2764
+ var params = this.useNamedParams ? {} : [];
2651
2765
  var placeholders = [];
2652
2766
  for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
2653
2767
  var key = keys_1[_i];
2654
2768
  var value = body[key];
2655
- var placeholder = this.addParam(params, key, value);
2769
+ var trimmed = this.trimTablePrefix(table, key);
2770
+ var qualified = "".concat(table, ".").concat(trimmed);
2771
+ var placeholder = this.serializeUpdateValue(value, params, qualified);
2656
2772
  placeholders.push(placeholder);
2657
2773
  }
2658
2774
  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 )");
@@ -2704,7 +2820,9 @@ var UpdateQueryBuilder = /** @class */ (function (_super) {
2704
2820
  var col = _a[0], val = _a[1];
2705
2821
  var trimmed = _this.trimTablePrefix(table, col);
2706
2822
  var qualified = "".concat(table, ".").concat(trimmed);
2707
- return "`".concat(trimmed, "` = ").concat(_this.addParam(params, qualified, val));
2823
+ _this.assertValidIdentifier(qualified, 'UPDATE SET');
2824
+ var rightSql = _this.serializeUpdateValue(val, params, qualified);
2825
+ return "`".concat(trimmed, "` = ").concat(rightSql);
2708
2826
  });
2709
2827
  sql += " SET ".concat(setClauses.join(', '));
2710
2828
  if (args.WHERE) {
@@ -3032,12 +3150,12 @@ function ExpressHandler(_a) {
3032
3150
  var _this = this;
3033
3151
  var C6 = _a.C6, mysqlPool = _a.mysqlPool;
3034
3152
  return function (req, res, next) { return tslib.__awaiter(_this, void 0, void 0, function () {
3035
- var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, primaryKeys, primaryKeyName, response, err_1;
3036
- var _a, _b, _c, _d, _e;
3037
- return tslib.__generator(this, function (_f) {
3038
- switch (_f.label) {
3153
+ var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, restModel, primaryKeys_1, primaryShortKeys_1, columnMap_1, resolveShortKey_1, hasPrimaryKeyValues, primaryKeyName, response, err_1;
3154
+ var _a, _b, _c, _d, _e, _f, _g;
3155
+ return tslib.__generator(this, function (_h) {
3156
+ switch (_h.label) {
3039
3157
  case 0:
3040
- _f.trys.push([0, 2, , 3]);
3158
+ _h.trys.push([0, 2, , 3]);
3041
3159
  incomingMethod = req.method.toUpperCase();
3042
3160
  table = req.params.table;
3043
3161
  primary = req.params.primary;
@@ -3051,7 +3169,7 @@ function ExpressHandler(_a) {
3051
3169
  try {
3052
3170
  delete payload.METHOD;
3053
3171
  }
3054
- catch ( /* noop */_g) { /* noop */ }
3172
+ catch ( /* noop */_j) { /* noop */ }
3055
3173
  }
3056
3174
  // Warn for unsupported overrides but continue normally
3057
3175
  if (incomingMethod !== 'GET' && methodOverride && methodOverride !== 'GET') {
@@ -3061,29 +3179,56 @@ function ExpressHandler(_a) {
3061
3179
  res.status(400).json({ error: "Invalid table: ".concat(table) });
3062
3180
  return [2 /*return*/];
3063
3181
  }
3064
- primaryKeys = C6.TABLES[table].PRIMARY;
3065
- if (primary && primaryKeys.length !== 1) {
3066
- if (primaryKeys.length > 1) {
3182
+ restModel = C6.TABLES[table];
3183
+ primaryKeys_1 = restModel.PRIMARY;
3184
+ primaryShortKeys_1 = (_d = restModel.PRIMARY_SHORT) !== null && _d !== void 0 ? _d : [];
3185
+ columnMap_1 = (_e = restModel.COLUMNS) !== null && _e !== void 0 ? _e : {};
3186
+ 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; };
3187
+ hasPrimaryKeyValues = function (data) {
3188
+ if (!data || typeof data !== 'object')
3189
+ return false;
3190
+ var whereClause = data[C6C.WHERE];
3191
+ var hasKeyValue = function (obj, fullKey, shortKey) {
3192
+ if (!obj || typeof obj !== 'object')
3193
+ return false;
3194
+ var fullValue = obj[fullKey];
3195
+ if (fullValue !== undefined && fullValue !== null)
3196
+ return true;
3197
+ var shortValue = shortKey ? obj[shortKey] : undefined;
3198
+ return shortValue !== undefined && shortValue !== null;
3199
+ };
3200
+ return primaryKeys_1.every(function (fullKey, index) {
3201
+ var shortKey = resolveShortKey_1(fullKey, index);
3202
+ return hasKeyValue(whereClause, fullKey, shortKey) || hasKeyValue(data, fullKey, shortKey);
3203
+ });
3204
+ };
3205
+ if (primary && primaryKeys_1.length !== 1) {
3206
+ if (primaryKeys_1.length > 1 && hasPrimaryKeyValues(payload)) {
3207
+ primary = undefined;
3208
+ }
3209
+ else if (primaryKeys_1.length > 1) {
3067
3210
  res.status(400).json({ error: "Table ".concat(table, " has multiple primary keys. Cannot implicitly determine key.") });
3068
3211
  return [2 /*return*/];
3069
3212
  }
3070
- res.status(400).json({
3071
- error: "Table ".concat(table, " has no primary keys. Please specify one.")
3072
- });
3073
- return [2 /*return*/];
3213
+ else {
3214
+ res.status(400).json({
3215
+ error: "Table ".concat(table, " has no primary keys. Please specify one.")
3216
+ });
3217
+ return [2 /*return*/];
3218
+ }
3074
3219
  }
3075
- primaryKeyName = primaryKeys[0];
3220
+ primaryKeyName = primaryKeys_1[0];
3076
3221
  // If a primary key was provided in the URL, merge it into the payload.
3077
3222
  // Support both complex requests using WHERE and singular requests
3078
3223
  // where the primary key lives at the root of the payload.
3079
3224
  if (primary) {
3080
3225
  if (payload[C6C.WHERE]) {
3081
3226
  payload[C6C.WHERE][primaryKeyName] =
3082
- (_d = payload[C6C.WHERE][primaryKeyName]) !== null && _d !== void 0 ? _d : primary;
3227
+ (_f = payload[C6C.WHERE][primaryKeyName]) !== null && _f !== void 0 ? _f : primary;
3083
3228
  }
3084
3229
  else {
3085
3230
  payload[primaryKeyName] =
3086
- (_e = payload[primaryKeyName]) !== null && _e !== void 0 ? _e : primary;
3231
+ (_g = payload[primaryKeyName]) !== null && _g !== void 0 ? _g : primary;
3087
3232
  }
3088
3233
  }
3089
3234
  return [4 /*yield*/, restRequest({
@@ -3093,11 +3238,11 @@ function ExpressHandler(_a) {
3093
3238
  restModel: C6.TABLES[table]
3094
3239
  })(payload)];
3095
3240
  case 1:
3096
- response = _f.sent();
3241
+ response = _h.sent();
3097
3242
  res.status(200).json(tslib.__assign({ success: true }, response));
3098
3243
  return [3 /*break*/, 3];
3099
3244
  case 2:
3100
- err_1 = _f.sent();
3245
+ err_1 = _h.sent();
3101
3246
  res.status(500).json({ success: false, error: err_1 });
3102
3247
  next(err_1);
3103
3248
  return [3 /*break*/, 3];
@@ -3182,10 +3327,11 @@ function error(message) {
3182
3327
  }
3183
3328
 
3184
3329
  function checkAllRequestsComplete() {
3185
- var stillRunning = exports.apiRequestCache.filter(function (cache) { return undefined === cache.response; });
3330
+ var cacheEntries = Array.from(apiRequestCache.values());
3331
+ var stillRunning = cacheEntries.filter(function (cache) { return undefined === cache.response; });
3186
3332
  if (stillRunning.length !== 0) {
3187
3333
  if (document === null || document === undefined) {
3188
- throw new Error('document is undefined while waiting for API requests to complete (' + JSON.stringify(exports.apiRequestCache) + ')');
3334
+ throw new Error('document is undefined while waiting for API requests to complete (' + JSON.stringify(cacheEntries) + ')');
3189
3335
  }
3190
3336
  // when requests return emtpy sets in full renders, it may not be possible to track their progress.
3191
3337
  console.warn('stillRunning...', stillRunning);
@@ -3227,6 +3373,7 @@ exports.SelectQueryBuilder = SelectQueryBuilder;
3227
3373
  exports.SqlExecutor = SqlExecutor;
3228
3374
  exports.TestRestfulResponse = TestRestfulResponse;
3229
3375
  exports.UpdateQueryBuilder = UpdateQueryBuilder;
3376
+ exports.apiRequestCache = apiRequestCache;
3230
3377
  exports.axiosInstance = axiosInstance;
3231
3378
  exports.bbox = bbox;
3232
3379
  exports.carbonNodeQsStringify = carbonNodeQsStringify;
@@ -3257,10 +3404,12 @@ exports.removePrefixIfExists = removePrefixIfExists;
3257
3404
  exports.resolveDerivedTable = resolveDerivedTable;
3258
3405
  exports.restOrm = restOrm;
3259
3406
  exports.restRequest = restRequest;
3407
+ exports.setCache = setCache;
3260
3408
  exports.sortAndSerializeQueryObject = sortAndSerializeQueryObject;
3261
3409
  exports.stContains = stContains;
3262
3410
  exports.timeout = timeout;
3263
3411
  exports.toastOptions = toastOptions;
3264
3412
  exports.toastOptionsDevs = toastOptionsDevs;
3413
+ exports.userCustomClearCache = userCustomClearCache;
3265
3414
  exports.warn = warn;
3266
3415
  //# sourceMappingURL=index.cjs.js.map