@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.esm.js CHANGED
@@ -304,8 +304,30 @@ axiosInstance.interceptors.request.use(function (config) {
304
304
  };
305
305
  }
306
306
  }
307
+ config.__carbonStart = performance.now();
307
308
  return config;
308
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
+ });
309
331
 
310
332
  function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler) {
311
333
  if (regexErrorHandler === void 0) { regexErrorHandler = alert; }
@@ -407,10 +429,7 @@ function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler)
407
429
 
408
430
  var isNode = function () {
409
431
  var _a;
410
- console.log('Checking if running in Node.js environment...');
411
- var isNodeEnv = typeof process !== 'undefined' && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
412
- console.log("Is Node.js environment: ".concat(isNodeEnv));
413
- return isNodeEnv;
432
+ return typeof process !== 'undefined' && !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
414
433
  };
415
434
 
416
435
  /**
@@ -628,26 +647,64 @@ function removeInvalidKeys(request, c6Tables) {
628
647
  return intersection;
629
648
  }
630
649
 
631
- // do not remove entries from this array. It is used to track the progress of API requests.
632
- // position in array is important. Do not sort. To not add to begging.
633
- var apiRequestCache = [];
650
+ // -----------------------------------------------------------------------------
651
+ // Cache Storage
652
+ // -----------------------------------------------------------------------------
653
+ var apiRequestCache = new Map();
634
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
+ // -----------------------------------------------------------------------------
635
676
  function clearCache(props) {
636
- if (false === (props === null || props === void 0 ? void 0 : props.ignoreWarning)) {
637
- 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) { }
638
686
  }
639
- userCustomClearCache.map(function (f) { return 'function' === typeof f && f(); });
640
- userCustomClearCache = apiRequestCache = [];
687
+ apiRequestCache.clear();
641
688
  }
642
- function checkCache(cacheResult, requestMethod, tableName, request) {
643
- 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)
644
696
  return false;
645
- }
646
- console.groupCollapsed('%c API: The request on (' + tableName + ') is in cache. Returning the request Promise!', 'color: #0c0');
647
- console.log('%c ' + requestMethod + ' ' + tableName, 'color: #0c0');
648
- 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);
649
699
  console.groupEnd();
650
- 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);
651
708
  }
652
709
 
653
710
  function sortAndSerializeQueryObject(tables, query) {
@@ -664,7 +721,10 @@ var HttpExecutor = /** @class */ (function (_super) {
664
721
  return _super !== null && _super.apply(this, arguments) || this;
665
722
  }
666
723
  HttpExecutor.prototype.isRestResponse = function (r) {
667
- 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);
668
728
  };
669
729
  HttpExecutor.prototype.stripTableNameFromKeys = function (obj) {
670
730
  var _a;
@@ -767,19 +827,9 @@ var HttpExecutor = /** @class */ (function (_super) {
767
827
  console.log('request', this.request);
768
828
  console.groupEnd();
769
829
  }
770
- // an undefined query would indicate queryCallback returned undefined,
771
- // thus the request shouldn't fire as is in custom cache
772
- if (undefined === this.request || null === this.request) {
773
- if (isLocal()) {
774
- console.groupCollapsed("API: (".concat(requestMethod, ") (").concat(tableName, ") query undefined/null \u2192 returning null"));
775
- console.log('request', this.request);
776
- console.groupEnd();
777
- }
778
- return [2 /*return*/, null];
779
- }
780
830
  query = this.request;
781
831
  apiRequest = function () { return __awaiter(_this, void 0, void 0, function () {
782
- 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;
783
833
  var _e;
784
834
  var _this = this;
785
835
  var _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
@@ -795,63 +845,37 @@ var HttpExecutor = /** @class */ (function (_super) {
795
845
  console.log('request', this.request);
796
846
  console.groupEnd();
797
847
  }
798
- querySerialized = sortAndSerializeQueryObject(tables, query !== null && query !== void 0 ? query : {});
799
- cacheResult = apiRequestCache.find(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
800
848
  cachingConfirmed = false;
801
- if (!(requestMethod === C6.GET)) return [3 /*break*/, 9];
802
- if (undefined === (query === null || query === void 0 ? void 0 : query[C6.PAGINATION])) {
803
- if (undefined === query || null === query) {
804
- 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] = {};
805
856
  }
806
- 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);
807
865
  }
808
- query[C6.PAGINATION][C6.PAGE] = query[C6.PAGINATION][C6.PAGE] || 1;
809
- query[C6.PAGINATION][C6.LIMIT] = query[C6.PAGINATION][C6.LIMIT] || 100;
810
- if (!(true === cacheResults)) return [3 /*break*/, 7];
811
- if (!(undefined !== cacheResult)) return [3 /*break*/, 6];
812
- _r.label = 1;
813
- case 1:
814
- cacheCheck = checkCache(cacheResult, requestMethod, tableName, this.request);
815
- if (!(false !== cacheCheck)) return [3 /*break*/, 3];
816
- return [4 /*yield*/, cacheCheck];
817
- case 2: return [2 /*return*/, (_r.sent()).data];
818
- case 3:
819
- ++query[C6.PAGINATION][C6.PAGE];
820
- querySerialized = sortAndSerializeQueryObject(tables, query !== null && query !== void 0 ? query : {});
821
- cacheResult = apiRequestCache.find(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
822
- _r.label = 4;
823
- case 4:
824
- if (undefined !== cacheResult) return [3 /*break*/, 1];
825
- _r.label = 5;
826
- case 5:
827
- if (debug && isLocal()) {
828
- 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;
829
872
  }
830
- _r.label = 6;
831
- case 6:
832
- cachingConfirmed = true;
833
- return [3 /*break*/, 8];
834
- case 7:
835
- if (debug && isLocal())
873
+ else if (debug && isLocal()) {
836
874
  toast.info("DEVS: Ignore cache was set to true.", toastOptionsDevs);
837
- _r.label = 8;
838
- case 8:
839
- if (debug && isLocal()) {
875
+ }
876
+ if (cacheResults && debug && isLocal()) {
840
877
  toast.success("DEVS: Request not in cache." + (requestMethod === C6.GET ? " Page (" + query[C6.PAGINATION][C6.PAGE] + ")" : ''), toastOptionsDevs);
841
878
  }
842
- return [3 /*break*/, 12];
843
- case 9:
844
- if (!cacheResults) return [3 /*break*/, 12];
845
- if (!cacheResult) return [3 /*break*/, 11];
846
- cacheCheck = checkCache(cacheResult, requestMethod, tableName, this.request);
847
- if (!(false !== cacheCheck)) return [3 /*break*/, 11];
848
- return [4 /*yield*/, cacheCheck];
849
- case 10: return [2 /*return*/, (_r.sent()).data];
850
- case 11:
851
- cachingConfirmed = true;
852
- _r.label = 12;
853
- case 12:
854
- returnGetNextPageFunction = false;
855
879
  restRequestUri = restURL + operatingTable + '/';
856
880
  needsConditionOrPrimaryCheck = (PUT === requestMethod || DELETE === requestMethod)
857
881
  && false === skipPrimaryCheck;
@@ -921,7 +945,7 @@ var HttpExecutor = /** @class */ (function (_super) {
921
945
  config: this.config,
922
946
  request: this.request
923
947
  });
924
- axiosActiveRequest = (_e = axios)[requestMethod.toLowerCase()].apply(_e, __spreadArray([restRequestUri], (function () {
948
+ axiosActiveRequest_1 = (_e = axios)[requestMethod.toLowerCase()].apply(_e, __spreadArray([restRequestUri], (function () {
925
949
  var convert = function (data) {
926
950
  return convertForRequestBody(data, fullTableList, C6, function (message) { return toast.error(message, toastOptions); });
927
951
  };
@@ -948,23 +972,30 @@ var HttpExecutor = /** @class */ (function (_super) {
948
972
  }
949
973
  })(), false));
950
974
  if (cachingConfirmed) {
951
- // push to cache so we do not repeat the request
952
- apiRequestCache.push({
975
+ setCache(requestMethod, tableName, cacheRequestData, {
953
976
  requestArgumentsSerialized: querySerialized,
954
- request: axiosActiveRequest
977
+ request: axiosActiveRequest_1,
955
978
  });
956
979
  }
957
980
  // returning the promise with this then is important for tests. todo - we could make that optional.
958
981
  // https://rapidapi.com/guides/axios-async-await
959
- return [2 /*return*/, axiosActiveRequest.then(function (response) { return __awaiter(_this, void 0, void 0, function () {
960
- 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;
961
984
  var _this = this;
962
- var _a, _b, _c, _d;
963
- return __generator(this, function (_e) {
964
- switch (_e.label) {
985
+ var _a, _b, _c, _d, _e;
986
+ return __generator(this, function (_f) {
987
+ switch (_f.label) {
965
988
  case 0:
966
989
  // noinspection SuspiciousTypeOfGuard
967
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
+ }
968
999
  if (isTest()) {
969
1000
  console.trace();
970
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 + ')');
@@ -972,11 +1003,11 @@ var HttpExecutor = /** @class */ (function (_super) {
972
1003
  return [2 /*return*/, Promise.reject(response)];
973
1004
  }
974
1005
  if (cachingConfirmed) {
975
- cacheIndex = apiRequestCache.findIndex(function (cache) { return cache.requestArgumentsSerialized === querySerialized; });
976
- // TODO - currently nonthing is setting this correctly
977
- apiRequestCache[cacheIndex].final = false === returnGetNextPageFunction;
978
- // only cache get method requests
979
- apiRequestCache[cacheIndex].response = response;
1006
+ setCache(requestMethod, tableName, cacheRequestData, {
1007
+ requestArgumentsSerialized: querySerialized,
1008
+ request: axiosActiveRequest_1,
1009
+ response: response,
1010
+ });
980
1011
  }
981
1012
  this.runLifecycleHooks("afterExecution", {
982
1013
  config: this.config,
@@ -1027,17 +1058,18 @@ var HttpExecutor = /** @class */ (function (_super) {
1027
1058
  got = responseData_1.rest.length;
1028
1059
  hasNext = pageLimit !== 1 && got === pageLimit;
1029
1060
  if (hasNext) {
1030
- responseData_1.next = apiRequest; // there might be more
1061
+ responseData_1.next = apiRequest;
1031
1062
  }
1032
1063
  else {
1033
1064
  responseData_1.next = undefined; // short page => done
1034
1065
  }
1035
- // If you keep this flag, make it reflect reality:
1036
- returnGetNextPageFunction = hasNext;
1037
- // and fix cache ‘final’ flag to match:
1038
1066
  if (cachingConfirmed) {
1039
- cacheIndex = apiRequestCache.findIndex(function (c) { return c.requestArgumentsSerialized === querySerialized; });
1040
- apiRequestCache[cacheIndex].final = !hasNext;
1067
+ setCache(requestMethod, tableName, cacheRequestData, {
1068
+ requestArgumentsSerialized: querySerialized,
1069
+ request: axiosActiveRequest_1,
1070
+ response: response,
1071
+ final: !hasNext,
1072
+ });
1041
1073
  }
1042
1074
  if ((this.config.verbose || debug) && isLocal()) {
1043
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], ")"));
@@ -1047,7 +1079,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1047
1079
  }
1048
1080
  if (!(fetchDependencies
1049
1081
  && 'number' === typeof fetchDependencies
1050
- && 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];
1051
1083
  console.groupCollapsed('%c API: Fetch Dependencies segment (' + requestMethod + ' ' + tableName + ')'
1052
1084
  + (fetchDependencies & eFetchDependencies.CHILDREN ? ' | (CHILDREN|REFERENCED) ' : '')
1053
1085
  + (fetchDependencies & eFetchDependencies.PARENTS ? ' | (PARENTS|REFERENCED_BY)' : '')
@@ -1118,7 +1150,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1118
1150
  }); });
1119
1151
  console.log('fetchReferences', fetchReferences_1);
1120
1152
  _loop_1 = function (tableToFetch) {
1121
- var _f;
1153
+ var _g;
1122
1154
  if (fetchDependencies & eFetchDependencies.C6ENTITY
1123
1155
  && 'string' === typeof tableName
1124
1156
  && tableName.endsWith("carbon_carbons")) {
@@ -1141,7 +1173,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1141
1173
  .split('_')
1142
1174
  .map(function (part) { return part.charAt(0).toUpperCase() + part.slice(1); })
1143
1175
  .join('_');
1144
- 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(', '), " ]"));
1145
1177
  console.log('%c Fetch Dependencies will select (' + tableToFetch + ') using GET request', 'color: #33ccff');
1146
1178
  var nextFetchDependencies = eFetchDependencies.NONE;
1147
1179
  if (fetchDependencies & eFetchDependencies.RECURSIVE) {
@@ -1167,8 +1199,8 @@ var HttpExecutor = /** @class */ (function (_super) {
1167
1199
  console.log('RestApi object', RestApi);
1168
1200
  // this is a dynamic call to the rest api, any generated table may resolve with (RestApi)
1169
1201
  // todo - using value to avoid joins.... but. maybe this should be a parameterizable option -- think race conditions; its safer to join
1170
- apiRequestPromises.push(RestApi.Get((_f = {},
1171
- _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) {
1172
1204
  fetchReferences_1[tableToFetch][column] = fetchReferences_1[tableToFetch][column].flat(Infinity);
1173
1205
  if (0 === fetchReferences_1[tableToFetch][column].length) {
1174
1206
  console.warn('The column (' + column + ') was not found in the response data. We will not fetch.', responseData_1);
@@ -1181,8 +1213,8 @@ var HttpExecutor = /** @class */ (function (_super) {
1181
1213
  ];
1182
1214
  return sum;
1183
1215
  }, {}),
1184
- _f.fetchDependencies = nextFetchDependencies,
1185
- _f)));
1216
+ _g.fetchDependencies = nextFetchDependencies,
1217
+ _g)));
1186
1218
  };
1187
1219
  for (tableToFetch in fetchReferences_1) {
1188
1220
  _loop_1(tableToFetch);
@@ -1190,7 +1222,7 @@ var HttpExecutor = /** @class */ (function (_super) {
1190
1222
  console.groupEnd();
1191
1223
  return [4 /*yield*/, Promise.all(apiRequestPromises)];
1192
1224
  case 1:
1193
- _e.sent();
1225
+ _f.sent();
1194
1226
  apiRequestPromises.map(function (promise) { return __awaiter(_this, void 0, void 0, function () {
1195
1227
  var _a, _b;
1196
1228
  return __generator(this, function (_c) {
@@ -1208,8 +1240,16 @@ var HttpExecutor = /** @class */ (function (_super) {
1208
1240
  }
1209
1241
  });
1210
1242
  }); });
1211
- _e.label = 2;
1243
+ _f.label = 2;
1212
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
+ }
1213
1253
  if (debug && isLocal()) {
1214
1254
  toast.success("DEVS: (" + requestMethod + ") request complete.", toastOptionsDevs);
1215
1255
  }
@@ -1220,16 +1260,12 @@ var HttpExecutor = /** @class */ (function (_super) {
1220
1260
  }); }).then(function (response) { return response.data; })]; // this escapes from axios context
1221
1261
  }
1222
1262
  catch (throwableError) {
1223
- if (isTest()) {
1224
- throw new Error(JSON.stringify(throwableError));
1225
- }
1226
1263
  console.groupCollapsed('%c API: An error occurred in the try catch block. returning null!', 'color: #ff0000');
1227
1264
  console.log('%c ' + requestMethod + ' ' + tableName, 'color: #A020F0');
1228
- console.warn(throwableError);
1265
+ console.error(throwableError);
1229
1266
  console.trace();
1230
1267
  console.groupEnd();
1231
- TestRestfulResponse(throwableError, success, error);
1232
- return [2 /*return*/, null];
1268
+ throw new Error(JSON.stringify(throwableError));
1233
1269
  }
1234
1270
  return [2 /*return*/];
1235
1271
  }
@@ -1788,6 +1824,84 @@ var ConditionBuilder = /** @class */ (function (_super) {
1788
1824
  }
1789
1825
  throw new Error('Unsupported operand type in SQL expression.');
1790
1826
  };
1827
+ ConditionBuilder.prototype.isPlainArrayLiteral = function (value, allowColumnRefs) {
1828
+ var _this = this;
1829
+ if (allowColumnRefs === void 0) { allowColumnRefs = false; }
1830
+ if (!Array.isArray(value))
1831
+ return false;
1832
+ return value.every(function (item) {
1833
+ if (item === null)
1834
+ return true;
1835
+ var type = typeof item;
1836
+ if (type === 'string' || type === 'number' || type === 'boolean')
1837
+ return true;
1838
+ if (Array.isArray(item))
1839
+ return _this.isPlainArrayLiteral(item, allowColumnRefs);
1840
+ if (item && typeof item === 'object')
1841
+ return _this.isPlainObjectLiteral(item, allowColumnRefs);
1842
+ return false;
1843
+ });
1844
+ };
1845
+ ConditionBuilder.prototype.isPlainObjectLiteral = function (value, allowColumnRefs) {
1846
+ var _this = this;
1847
+ if (allowColumnRefs === void 0) { allowColumnRefs = false; }
1848
+ if (!value || typeof value !== 'object' || Array.isArray(value))
1849
+ return false;
1850
+ if (value instanceof Date)
1851
+ return false;
1852
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(value))
1853
+ return false;
1854
+ var normalized = value instanceof Map ? Object.fromEntries(value) : value;
1855
+ if (C6C.SUBSELECT in normalized)
1856
+ return false;
1857
+ var entries = Object.entries(normalized);
1858
+ if (entries.length === 0)
1859
+ return true;
1860
+ if (entries.some(function (_a) {
1861
+ var key = _a[0];
1862
+ return _this.isOperator(key) || _this.BOOLEAN_OPERATORS.has(key);
1863
+ })) {
1864
+ return false;
1865
+ }
1866
+ if (!allowColumnRefs) {
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
+ }
1874
+ return true;
1875
+ };
1876
+ ConditionBuilder.prototype.resolveColumnDefinition = function (column) {
1877
+ var _a, _b, _c, _d;
1878
+ if (!column || typeof column !== 'string' || !column.includes('.'))
1879
+ return undefined;
1880
+ var _e = column.split('.', 2), prefix = _e[0], colName = _e[1];
1881
+ var tableName = (_a = this.aliasMap[prefix]) !== null && _a !== void 0 ? _a : prefix;
1882
+ var table = (_c = (_b = this.config.C6) === null || _b === void 0 ? void 0 : _b.TABLES) === null || _c === void 0 ? void 0 : _c[tableName];
1883
+ if (!(table === null || table === void 0 ? void 0 : table.TYPE_VALIDATION))
1884
+ return undefined;
1885
+ return (_d = table.TYPE_VALIDATION[colName]) !== null && _d !== void 0 ? _d : table.TYPE_VALIDATION["".concat(tableName, ".").concat(colName)];
1886
+ };
1887
+ ConditionBuilder.prototype.isJsonColumn = function (column) {
1888
+ var columnDef = this.resolveColumnDefinition(column);
1889
+ var mysqlType = columnDef === null || columnDef === void 0 ? void 0 : columnDef.MYSQL_TYPE;
1890
+ return typeof mysqlType === 'string' && mysqlType.toLowerCase().includes('json');
1891
+ };
1892
+ ConditionBuilder.prototype.serializeUpdateValue = function (value, params, contextColumn) {
1893
+ var normalized = value instanceof Map ? Object.fromEntries(value) : value;
1894
+ var allowColumnRefs = this.isJsonColumn(contextColumn);
1895
+ if (this.isPlainArrayLiteral(normalized, allowColumnRefs)
1896
+ || this.isPlainObjectLiteral(normalized, allowColumnRefs)) {
1897
+ return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
1898
+ }
1899
+ var _a = this.serializeOperand(normalized, params, contextColumn), sql = _a.sql, isReference = _a.isReference, isExpression = _a.isExpression, isSubSelect = _a.isSubSelect;
1900
+ if (!isReference && !isExpression && !isSubSelect && typeof normalized === 'object' && normalized !== null) {
1901
+ throw new Error('Unsupported operand type in SQL expression.');
1902
+ }
1903
+ return sql;
1904
+ };
1791
1905
  ConditionBuilder.prototype.ensurePlainObject = function (value) {
1792
1906
  if (value instanceof Map) {
1793
1907
  return Object.fromEntries(value);
@@ -2644,12 +2758,14 @@ var PostQueryBuilder = /** @class */ (function (_super) {
2644
2758
  var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
2645
2759
  var body = verb in this.request ? this.request[verb] : this.request;
2646
2760
  var keys = Object.keys(body);
2647
- var params = [];
2761
+ var params = this.useNamedParams ? {} : [];
2648
2762
  var placeholders = [];
2649
2763
  for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
2650
2764
  var key = keys_1[_i];
2651
2765
  var value = body[key];
2652
- var placeholder = this.addParam(params, key, value);
2766
+ var trimmed = this.trimTablePrefix(table, key);
2767
+ var qualified = "".concat(table, ".").concat(trimmed);
2768
+ var placeholder = this.serializeUpdateValue(value, params, qualified);
2653
2769
  placeholders.push(placeholder);
2654
2770
  }
2655
2771
  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 )");
@@ -2701,7 +2817,9 @@ var UpdateQueryBuilder = /** @class */ (function (_super) {
2701
2817
  var col = _a[0], val = _a[1];
2702
2818
  var trimmed = _this.trimTablePrefix(table, col);
2703
2819
  var qualified = "".concat(table, ".").concat(trimmed);
2704
- return "`".concat(trimmed, "` = ").concat(_this.addParam(params, qualified, val));
2820
+ _this.assertValidIdentifier(qualified, 'UPDATE SET');
2821
+ var rightSql = _this.serializeUpdateValue(val, params, qualified);
2822
+ return "`".concat(trimmed, "` = ").concat(rightSql);
2705
2823
  });
2706
2824
  sql += " SET ".concat(setClauses.join(', '));
2707
2825
  if (args.WHERE) {
@@ -3029,12 +3147,12 @@ function ExpressHandler(_a) {
3029
3147
  var _this = this;
3030
3148
  var C6 = _a.C6, mysqlPool = _a.mysqlPool;
3031
3149
  return function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
3032
- var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, primaryKeys, primaryKeyName, response, err_1;
3033
- var _a, _b, _c, _d, _e;
3034
- return __generator(this, function (_f) {
3035
- switch (_f.label) {
3150
+ var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, restModel, primaryKeys_1, primaryShortKeys_1, columnMap_1, resolveShortKey_1, hasPrimaryKeyValues, primaryKeyName, response, err_1;
3151
+ var _a, _b, _c, _d, _e, _f, _g;
3152
+ return __generator(this, function (_h) {
3153
+ switch (_h.label) {
3036
3154
  case 0:
3037
- _f.trys.push([0, 2, , 3]);
3155
+ _h.trys.push([0, 2, , 3]);
3038
3156
  incomingMethod = req.method.toUpperCase();
3039
3157
  table = req.params.table;
3040
3158
  primary = req.params.primary;
@@ -3048,7 +3166,7 @@ function ExpressHandler(_a) {
3048
3166
  try {
3049
3167
  delete payload.METHOD;
3050
3168
  }
3051
- catch ( /* noop */_g) { /* noop */ }
3169
+ catch ( /* noop */_j) { /* noop */ }
3052
3170
  }
3053
3171
  // Warn for unsupported overrides but continue normally
3054
3172
  if (incomingMethod !== 'GET' && methodOverride && methodOverride !== 'GET') {
@@ -3058,29 +3176,56 @@ function ExpressHandler(_a) {
3058
3176
  res.status(400).json({ error: "Invalid table: ".concat(table) });
3059
3177
  return [2 /*return*/];
3060
3178
  }
3061
- primaryKeys = C6.TABLES[table].PRIMARY;
3062
- if (primary && primaryKeys.length !== 1) {
3063
- if (primaryKeys.length > 1) {
3179
+ restModel = C6.TABLES[table];
3180
+ primaryKeys_1 = restModel.PRIMARY;
3181
+ primaryShortKeys_1 = (_d = restModel.PRIMARY_SHORT) !== null && _d !== void 0 ? _d : [];
3182
+ columnMap_1 = (_e = restModel.COLUMNS) !== null && _e !== void 0 ? _e : {};
3183
+ 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; };
3184
+ hasPrimaryKeyValues = function (data) {
3185
+ if (!data || typeof data !== 'object')
3186
+ return false;
3187
+ var whereClause = data[C6C.WHERE];
3188
+ var hasKeyValue = function (obj, fullKey, shortKey) {
3189
+ if (!obj || typeof obj !== 'object')
3190
+ return false;
3191
+ var fullValue = obj[fullKey];
3192
+ if (fullValue !== undefined && fullValue !== null)
3193
+ return true;
3194
+ var shortValue = shortKey ? obj[shortKey] : undefined;
3195
+ return shortValue !== undefined && shortValue !== null;
3196
+ };
3197
+ return primaryKeys_1.every(function (fullKey, index) {
3198
+ var shortKey = resolveShortKey_1(fullKey, index);
3199
+ return hasKeyValue(whereClause, fullKey, shortKey) || hasKeyValue(data, fullKey, shortKey);
3200
+ });
3201
+ };
3202
+ if (primary && primaryKeys_1.length !== 1) {
3203
+ if (primaryKeys_1.length > 1 && hasPrimaryKeyValues(payload)) {
3204
+ primary = undefined;
3205
+ }
3206
+ else if (primaryKeys_1.length > 1) {
3064
3207
  res.status(400).json({ error: "Table ".concat(table, " has multiple primary keys. Cannot implicitly determine key.") });
3065
3208
  return [2 /*return*/];
3066
3209
  }
3067
- res.status(400).json({
3068
- error: "Table ".concat(table, " has no primary keys. Please specify one.")
3069
- });
3070
- return [2 /*return*/];
3210
+ else {
3211
+ res.status(400).json({
3212
+ error: "Table ".concat(table, " has no primary keys. Please specify one.")
3213
+ });
3214
+ return [2 /*return*/];
3215
+ }
3071
3216
  }
3072
- primaryKeyName = primaryKeys[0];
3217
+ primaryKeyName = primaryKeys_1[0];
3073
3218
  // If a primary key was provided in the URL, merge it into the payload.
3074
3219
  // Support both complex requests using WHERE and singular requests
3075
3220
  // where the primary key lives at the root of the payload.
3076
3221
  if (primary) {
3077
3222
  if (payload[C6C.WHERE]) {
3078
3223
  payload[C6C.WHERE][primaryKeyName] =
3079
- (_d = payload[C6C.WHERE][primaryKeyName]) !== null && _d !== void 0 ? _d : primary;
3224
+ (_f = payload[C6C.WHERE][primaryKeyName]) !== null && _f !== void 0 ? _f : primary;
3080
3225
  }
3081
3226
  else {
3082
3227
  payload[primaryKeyName] =
3083
- (_e = payload[primaryKeyName]) !== null && _e !== void 0 ? _e : primary;
3228
+ (_g = payload[primaryKeyName]) !== null && _g !== void 0 ? _g : primary;
3084
3229
  }
3085
3230
  }
3086
3231
  return [4 /*yield*/, restRequest({
@@ -3090,11 +3235,11 @@ function ExpressHandler(_a) {
3090
3235
  restModel: C6.TABLES[table]
3091
3236
  })(payload)];
3092
3237
  case 1:
3093
- response = _f.sent();
3238
+ response = _h.sent();
3094
3239
  res.status(200).json(__assign({ success: true }, response));
3095
3240
  return [3 /*break*/, 3];
3096
3241
  case 2:
3097
- err_1 = _f.sent();
3242
+ err_1 = _h.sent();
3098
3243
  res.status(500).json({ success: false, error: err_1 });
3099
3244
  next(err_1);
3100
3245
  return [3 /*break*/, 3];
@@ -3179,10 +3324,11 @@ function error(message) {
3179
3324
  }
3180
3325
 
3181
3326
  function checkAllRequestsComplete() {
3182
- var stillRunning = apiRequestCache.filter(function (cache) { return undefined === cache.response; });
3327
+ var cacheEntries = Array.from(apiRequestCache.values());
3328
+ var stillRunning = cacheEntries.filter(function (cache) { return undefined === cache.response; });
3183
3329
  if (stillRunning.length !== 0) {
3184
3330
  if (document === null || document === undefined) {
3185
- throw new Error('document is undefined while waiting for API requests to complete (' + JSON.stringify(apiRequestCache) + ')');
3331
+ throw new Error('document is undefined while waiting for API requests to complete (' + JSON.stringify(cacheEntries) + ')');
3186
3332
  }
3187
3333
  // when requests return emtpy sets in full renders, it may not be possible to track their progress.
3188
3334
  console.warn('stillRunning...', stillRunning);
@@ -3203,5 +3349,5 @@ function isVerbose () {
3203
3349
  return ['true', '1', 'yes', 'on'].includes(envVerbose.toLowerCase());
3204
3350
  }
3205
3351
 
3206
- 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 };
3352
+ 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 };
3207
3353
  //# sourceMappingURL=index.esm.js.map