@carbonorm/carbonnode 6.0.10 → 6.0.13

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 (75) hide show
  1. package/dist/executors/SqlExecutor.d.ts +6 -0
  2. package/dist/handlers/ExpressHandler.d.ts +8 -9
  3. package/dist/index.cjs.js +324 -108
  4. package/dist/index.cjs.js.map +1 -1
  5. package/dist/index.esm.js +324 -109
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/types/ormInterfaces.d.ts +25 -5
  8. package/dist/utils/cacheManager.d.ts +2 -3
  9. package/package.json +1 -1
  10. package/src/__tests__/convertForRequestBody.test.ts +58 -0
  11. package/src/__tests__/expressServer.e2e.test.ts +62 -38
  12. package/src/__tests__/fixtures/createTestServer.ts +7 -3
  13. package/src/__tests__/httpExecutorSingular.e2e.test.ts +97 -60
  14. package/src/__tests__/logSql.test.ts +13 -0
  15. package/src/__tests__/sakila-db/C6.js +1 -1
  16. package/src/__tests__/sakila-db/C6.mysqldump.json +1 -1
  17. package/src/__tests__/sakila-db/C6.mysqldump.sql +1 -1
  18. package/src/__tests__/sakila-db/C6.sqlAllowList.json +11 -11
  19. package/src/__tests__/sakila-db/C6.ts +1 -1
  20. package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.json +4 -4
  21. package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.latest.json +3 -3
  22. package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.json +1 -1
  23. package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.lookup.json +3 -3
  24. package/src/__tests__/sakila-db/sqlResponses/C6.address.post.json +6 -6
  25. package/src/__tests__/sakila-db/sqlResponses/C6.address.post.latest.json +5 -5
  26. package/src/__tests__/sakila-db/sqlResponses/C6.address.put.json +1 -1
  27. package/src/__tests__/sakila-db/sqlResponses/C6.address.put.lookup.json +5 -5
  28. package/src/__tests__/sakila-db/sqlResponses/C6.category.post.json +3 -3
  29. package/src/__tests__/sakila-db/sqlResponses/C6.category.post.latest.json +2 -2
  30. package/src/__tests__/sakila-db/sqlResponses/C6.category.put.json +1 -1
  31. package/src/__tests__/sakila-db/sqlResponses/C6.category.put.lookup.json +2 -2
  32. package/src/__tests__/sakila-db/sqlResponses/C6.city.post.json +3 -3
  33. package/src/__tests__/sakila-db/sqlResponses/C6.city.post.latest.json +2 -2
  34. package/src/__tests__/sakila-db/sqlResponses/C6.city.put.json +1 -1
  35. package/src/__tests__/sakila-db/sqlResponses/C6.city.put.lookup.json +2 -2
  36. package/src/__tests__/sakila-db/sqlResponses/C6.country.post.json +3 -3
  37. package/src/__tests__/sakila-db/sqlResponses/C6.country.post.latest.json +2 -2
  38. package/src/__tests__/sakila-db/sqlResponses/C6.country.put.json +1 -1
  39. package/src/__tests__/sakila-db/sqlResponses/C6.country.put.lookup.json +2 -2
  40. package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.json +6 -6
  41. package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.latest.json +5 -5
  42. package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.json +1 -1
  43. package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.lookup.json +5 -5
  44. package/src/__tests__/sakila-db/sqlResponses/C6.film.post.json +3 -3
  45. package/src/__tests__/sakila-db/sqlResponses/C6.film.post.latest.json +2 -2
  46. package/src/__tests__/sakila-db/sqlResponses/C6.film.put.json +1 -1
  47. package/src/__tests__/sakila-db/sqlResponses/C6.film.put.lookup.json +2 -2
  48. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.json +2 -2
  49. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.latest.json +1 -1
  50. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.json +1 -1
  51. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.lookup.json +1 -1
  52. package/src/__tests__/sakila-db/sqlResponses/C6.language.post.json +3 -3
  53. package/src/__tests__/sakila-db/sqlResponses/C6.language.post.latest.json +2 -2
  54. package/src/__tests__/sakila-db/sqlResponses/C6.language.put.json +1 -1
  55. package/src/__tests__/sakila-db/sqlResponses/C6.language.put.lookup.json +2 -2
  56. package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.json +3 -3
  57. package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.latest.json +2 -2
  58. package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.lookup.json +2 -2
  59. package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.json +4 -4
  60. package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.latest.json +3 -3
  61. package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.json +1 -1
  62. package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.lookup.json +3 -3
  63. package/src/__tests__/sakila.generated.test.ts +11 -3
  64. package/src/__tests__/sqlBuilders.test.ts +46 -0
  65. package/src/__tests__/sqlExecutorLifecycleHooks.test.ts +122 -0
  66. package/src/api/convertForRequestBody.ts +9 -2
  67. package/src/api/restRequest.ts +1 -0
  68. package/src/executors/HttpExecutor.ts +1 -1
  69. package/src/executors/SqlExecutor.ts +252 -49
  70. package/src/handlers/ExpressHandler.ts +50 -24
  71. package/src/orm/builders/ConditionBuilder.ts +43 -1
  72. package/src/orm/queries/PostQueryBuilder.ts +24 -12
  73. package/src/types/ormInterfaces.ts +31 -5
  74. package/src/utils/cacheManager.ts +3 -4
  75. package/src/utils/colorSql.ts +18 -0
package/dist/index.esm.js CHANGED
@@ -533,7 +533,11 @@ var logWithLevel = function (requiredLevel, context, logger) {
533
533
  };
534
534
 
535
535
  function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler) {
536
- if (regexErrorHandler === void 0) { regexErrorHandler = alert; }
536
+ if (regexErrorHandler === void 0) { regexErrorHandler = function (message) {
537
+ if (typeof globalThis !== "undefined" && typeof globalThis.alert === "function") {
538
+ globalThis.alert(message);
539
+ }
540
+ }; }
537
541
  var payload = {};
538
542
  var tableNames = Array.isArray(tableName) ? tableName : [tableName];
539
543
  var tableDefinitions = tableNames.map(function (name) {
@@ -556,7 +560,8 @@ function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler)
556
560
  C6Constants.DELETE,
557
561
  C6Constants.WHERE,
558
562
  C6Constants.JOIN,
559
- C6Constants.PAGINATION
563
+ C6Constants.PAGINATION,
564
+ "cacheResults",
560
565
  ].includes(value)) {
561
566
  var val_1 = restfulObject[value];
562
567
  if (Array.isArray(val_1)) {
@@ -570,6 +575,9 @@ function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler)
570
575
  return (__assign(__assign({}, acc), (_a = {}, _a[key] = val_1[key], _a)));
571
576
  }, {});
572
577
  }
578
+ else {
579
+ payload[value] = val_1;
580
+ }
573
581
  return "continue";
574
582
  }
575
583
  if (shortReference in tableDefinition) {
@@ -901,7 +909,7 @@ function checkCache(method, tableName, requestData) {
901
909
  var cached = apiRequestCache.get(key);
902
910
  if (!cached)
903
911
  return false;
904
- if (shouldLog(LogLevel.DEBUG, undefined)) {
912
+ if (shouldLog(LogLevel.INFO, undefined)) {
905
913
  console.groupCollapsed("%c API cache hit for ".concat(method, " ").concat(tableName), "color:#0c0");
906
914
  console.log("Request Data:", requestData);
907
915
  console.groupEnd();
@@ -2128,14 +2136,51 @@ var ConditionBuilder = /** @class */ (function (_super) {
2128
2136
  return typeof mysqlType === 'string' && mysqlType.toLowerCase().includes('json');
2129
2137
  };
2130
2138
  ConditionBuilder.prototype.serializeUpdateValue = function (value, params, contextColumn) {
2139
+ var _a;
2140
+ var _this = this;
2131
2141
  var normalized = value instanceof Map ? Object.fromEntries(value) : value;
2132
2142
  var allowColumnRefs = this.isJsonColumn(contextColumn);
2133
2143
  if (this.isPlainArrayLiteral(normalized, allowColumnRefs)
2134
2144
  || this.isPlainObjectLiteral(normalized, allowColumnRefs)) {
2135
2145
  return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
2136
2146
  }
2137
- var _a = this.serializeOperand(normalized, params, contextColumn), sql = _a.sql, isReference = _a.isReference, isExpression = _a.isExpression, isSubSelect = _a.isSubSelect;
2147
+ var sql;
2148
+ var isReference;
2149
+ var isExpression;
2150
+ var isSubSelect;
2151
+ var shouldStringifyObjectFallback = function (candidate) {
2152
+ if (typeof candidate !== 'object'
2153
+ || candidate === null
2154
+ || candidate instanceof Date
2155
+ || (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(candidate))) {
2156
+ return false;
2157
+ }
2158
+ var normalizedCandidate = candidate instanceof Map
2159
+ ? Object.fromEntries(candidate)
2160
+ : candidate;
2161
+ var entries = Object.entries(normalizedCandidate);
2162
+ if (entries.length !== 1) {
2163
+ return true;
2164
+ }
2165
+ var key = entries[0][0];
2166
+ if (_this.isOperator(key) || _this.BOOLEAN_OPERATORS.has(key)) {
2167
+ return false;
2168
+ }
2169
+ return true;
2170
+ };
2171
+ try {
2172
+ (_a = this.serializeOperand(normalized, params, contextColumn), sql = _a.sql, isReference = _a.isReference, isExpression = _a.isExpression, isSubSelect = _a.isSubSelect);
2173
+ }
2174
+ catch (err) {
2175
+ if (shouldStringifyObjectFallback(normalized)) {
2176
+ return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
2177
+ }
2178
+ throw err;
2179
+ }
2138
2180
  if (!isReference && !isExpression && !isSubSelect && typeof normalized === 'object' && normalized !== null) {
2181
+ if (shouldStringifyObjectFallback(normalized)) {
2182
+ return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
2183
+ }
2139
2184
  throw new Error('Unsupported operand type in SQL expression.');
2140
2185
  }
2141
2186
  return sql;
@@ -2949,11 +2994,27 @@ function collapseBinds(sql) {
2949
2994
  return "".concat(C$1.DIM, "? \u00D7").concat(count).concat(RESET);
2950
2995
  });
2951
2996
  }
2997
+ /**
2998
+ * ( ? ×9 ), ( ? ×9 ), ( ? ×9 ) -> ( ? ×9 ) ×3
2999
+ */
3000
+ function collapseRepeatedValueRows(sql) {
3001
+ var repeatedRowPattern = /(\((?:\x1b\[[0-9;]*m)?\?\s*×\d+(?:\x1b\[[0-9;]*m)?\)|\(\s*(?:\?\s*,\s*)+\?\s*\))(?:\s*,\s*\1){2,}/g;
3002
+ return sql.replace(repeatedRowPattern, function (match, row) {
3003
+ var _a, _b;
3004
+ var rowMatches = match.match(new RegExp(row.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"));
3005
+ var count = (_a = rowMatches === null || rowMatches === void 0 ? void 0 : rowMatches.length) !== null && _a !== void 0 ? _a : 1;
3006
+ var normalizedRow = row.includes("×")
3007
+ ? row
3008
+ : "(".concat(C$1.DIM, "? \u00D7").concat(((_b = row.match(/\?/g)) !== null && _b !== void 0 ? _b : []).length).concat(RESET, ")");
3009
+ return "".concat(normalizedRow, " ").concat(C$1.DIM, "\u00D7").concat(count).concat(RESET);
3010
+ });
3011
+ }
2952
3012
  /* ---------- main formatter ---------- */
2953
3013
  function colorSql(sql) {
2954
3014
  var s = sql.trim();
2955
3015
  /* 1️⃣ collapse bind noise */
2956
3016
  s = collapseBinds(s);
3017
+ s = collapseRepeatedValueRows(s);
2957
3018
  /* 2️⃣ table.column coloring (core visual grouping) */
2958
3019
  s = s.replace(/\b(`?\w+`?)\.(\w+)\b/g, function (_, table, column) {
2959
3020
  return "".concat(tableColor(table)).concat(table).concat(RESET, ".") +
@@ -2970,7 +3031,7 @@ function colorSql(sql) {
2970
3031
  return s;
2971
3032
  }
2972
3033
 
2973
- var version = "6.0.10";
3034
+ var version = "6.0.13";
2974
3035
 
2975
3036
  var DEFAULT_STEP = 8;
2976
3037
  function parseSemver(version) {
@@ -3157,21 +3218,35 @@ var PostQueryBuilder = /** @class */ (function (_super) {
3157
3218
  };
3158
3219
  PostQueryBuilder.prototype.build = function (table) {
3159
3220
  var _this = this;
3221
+ var _a, _b;
3160
3222
  this.aliasMap = {};
3161
3223
  var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
3162
- var body = verb in this.request ? this.request[verb] : this.request;
3163
- var keys = Object.keys(body);
3224
+ var directRows = Array.isArray(this.request)
3225
+ ? this.request
3226
+ : [];
3227
+ var rows = directRows.length > 0
3228
+ ? directRows
3229
+ : Array.isArray(this.request.dataInsertMultipleRows) &&
3230
+ this.request.dataInsertMultipleRows.length > 0
3231
+ ? this.request.dataInsertMultipleRows
3232
+ : [verb in this.request ? this.request[verb] : this.request];
3233
+ var keys = Object.keys((_a = rows[0]) !== null && _a !== void 0 ? _a : {});
3164
3234
  var params = this.useNamedParams ? {} : [];
3165
- var placeholders = [];
3166
- for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
3167
- var key = keys_1[_i];
3168
- var value = body[key];
3169
- var trimmed = this.trimTablePrefix(table, key);
3170
- var qualified = "".concat(table, ".").concat(trimmed);
3171
- var placeholder = this.serializeUpdateValue(value, params, qualified);
3172
- placeholders.push(placeholder);
3235
+ var rowPlaceholders = [];
3236
+ for (var _i = 0, rows_1 = rows; _i < rows_1.length; _i++) {
3237
+ var row = rows_1[_i];
3238
+ var placeholders = [];
3239
+ for (var _c = 0, keys_1 = keys; _c < keys_1.length; _c++) {
3240
+ var key = keys_1[_c];
3241
+ var value = (_b = row[key]) !== null && _b !== void 0 ? _b : null;
3242
+ var trimmed = this.trimTablePrefix(table, key);
3243
+ var qualified = "".concat(table, ".").concat(trimmed);
3244
+ var placeholder = this.serializeUpdateValue(value, params, qualified);
3245
+ placeholders.push(placeholder);
3246
+ }
3247
+ rowPlaceholders.push("(".concat(placeholders.join(', '), ")"));
3173
3248
  }
3174
- 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 )");
3249
+ var sql = "".concat(verb, " INTO `").concat(table, "` (\n ").concat(keys.map(function (k) { return "`".concat(_this.trimTablePrefix(table, k), "`"); }).join(', '), "\n ) VALUES\n ").concat(rowPlaceholders.join(',\n '));
3175
3250
  if (C6C.UPDATE in this.request) {
3176
3251
  var updateData = this.request[C6C.UPDATE];
3177
3252
  if (!Array.isArray(updateData)) {
@@ -3491,12 +3566,18 @@ var SqlExecutor = /** @class */ (function (_super) {
3491
3566
  }
3492
3567
  SqlExecutor.prototype.execute = function () {
3493
3568
  return __awaiter(this, void 0, void 0, function () {
3494
- var TABLE_NAME, method, logContext, _a, rest, result, result, result;
3569
+ var TABLE_NAME, method, logContext, response, _a, rest, getResponse, restRows, result, result, result;
3495
3570
  return __generator(this, function (_b) {
3496
3571
  switch (_b.label) {
3497
3572
  case 0:
3498
3573
  TABLE_NAME = this.config.restModel.TABLE_NAME;
3499
3574
  method = this.config.requestMethod;
3575
+ return [4 /*yield*/, this.runLifecycleHooks("beforeProcessing", {
3576
+ config: this.config,
3577
+ request: this.request,
3578
+ })];
3579
+ case 1:
3580
+ _b.sent();
3500
3581
  // Normalize singular T-shaped requests into complex ORM shape (GET/PUT/DELETE)
3501
3582
  try {
3502
3583
  this.request = normalizeSingularRequest(method, this.request, this.config.restModel, undefined);
@@ -3510,45 +3591,54 @@ var SqlExecutor = /** @class */ (function (_super) {
3510
3591
  logWithLevel(LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE9 Request:", this.request);
3511
3592
  _a = method;
3512
3593
  switch (_a) {
3513
- case 'GET': return [3 /*break*/, 1];
3514
- case 'POST': return [3 /*break*/, 3];
3515
- case 'PUT': return [3 /*break*/, 6];
3516
- case 'DELETE': return [3 /*break*/, 9];
3594
+ case 'GET': return [3 /*break*/, 2];
3595
+ case 'POST': return [3 /*break*/, 4];
3596
+ case 'PUT': return [3 /*break*/, 7];
3597
+ case 'DELETE': return [3 /*break*/, 10];
3517
3598
  }
3518
- return [3 /*break*/, 12];
3519
- case 1: return [4 /*yield*/, this.runQuery()];
3520
- case 2:
3599
+ return [3 /*break*/, 13];
3600
+ case 2: return [4 /*yield*/, this.runQuery()];
3601
+ case 3:
3521
3602
  rest = _b.sent();
3522
3603
  if (this.config.reactBootstrap) {
3604
+ getResponse = rest;
3605
+ restRows = Array.isArray(getResponse.rest)
3606
+ ? getResponse.rest
3607
+ : [getResponse.rest];
3523
3608
  this.config.reactBootstrap.updateRestfulObjectArrays({
3524
- dataOrCallback: rest.rest,
3609
+ dataOrCallback: restRows,
3525
3610
  stateKey: this.config.restModel.TABLE_NAME,
3526
3611
  uniqueObjectId: this.config.restModel.PRIMARY_SHORT,
3527
3612
  });
3528
3613
  }
3529
- return [2 /*return*/, rest];
3530
- case 3: return [4 /*yield*/, this.runQuery()];
3531
- case 4:
3614
+ response = rest;
3615
+ return [3 /*break*/, 14];
3616
+ case 4: return [4 /*yield*/, this.runQuery()];
3617
+ case 5:
3532
3618
  result = _b.sent();
3533
3619
  return [4 /*yield*/, this.broadcastWebsocketIfConfigured(result)];
3534
- case 5:
3620
+ case 6:
3535
3621
  _b.sent();
3536
- return [2 /*return*/, result];
3537
- case 6: return [4 /*yield*/, this.runQuery()];
3538
- case 7:
3622
+ response = result;
3623
+ return [3 /*break*/, 14];
3624
+ case 7: return [4 /*yield*/, this.runQuery()];
3625
+ case 8:
3539
3626
  result = _b.sent();
3540
3627
  return [4 /*yield*/, this.broadcastWebsocketIfConfigured(result)];
3541
- case 8:
3628
+ case 9:
3542
3629
  _b.sent();
3543
- return [2 /*return*/, result];
3544
- case 9: return [4 /*yield*/, this.runQuery()];
3545
- case 10:
3630
+ response = result;
3631
+ return [3 /*break*/, 14];
3632
+ case 10: return [4 /*yield*/, this.runQuery()];
3633
+ case 11:
3546
3634
  result = _b.sent();
3547
3635
  return [4 /*yield*/, this.broadcastWebsocketIfConfigured(result)];
3548
- case 11:
3636
+ case 12:
3549
3637
  _b.sent();
3550
- return [2 /*return*/, result];
3551
- case 12: throw new Error("Unsupported request method: ".concat(method));
3638
+ response = result;
3639
+ return [3 /*break*/, 14];
3640
+ case 13: throw new Error("Unsupported request method: ".concat(method));
3641
+ case 14: return [2 /*return*/, response];
3552
3642
  }
3553
3643
  });
3554
3644
  });
@@ -3845,65 +3935,182 @@ var SqlExecutor = /** @class */ (function (_super) {
3845
3935
  };
3846
3936
  SqlExecutor.prototype.runQuery = function () {
3847
3937
  return __awaiter(this, void 0, void 0, function () {
3848
- var TABLE_NAME, method, builder, QueryResult, logContext, formatted, toUnnamed, _a, sql, values;
3938
+ var method, tableName, logContext, cacheResults, cacheRequestData, requestArgumentsSerialized, cachedRequest, sqlExecution, queryPromise, cacheRequest, cacheResponse;
3849
3939
  var _this = this;
3850
- return __generator(this, function (_b) {
3851
- switch (_b.label) {
3940
+ var _a, _b;
3941
+ return __generator(this, function (_c) {
3942
+ switch (_c.label) {
3852
3943
  case 0:
3853
- TABLE_NAME = this.config.restModel.TABLE_NAME;
3854
3944
  method = this.config.requestMethod;
3855
- switch (method) {
3856
- case 'GET':
3857
- builder = new SelectQueryBuilder(this.config, this.request);
3858
- break;
3859
- case 'PUT':
3860
- builder = new UpdateQueryBuilder(this.config, this.request);
3861
- break;
3862
- case 'DELETE':
3863
- builder = new DeleteQueryBuilder(this.config, this.request);
3864
- break;
3865
- case 'POST':
3866
- builder = new PostQueryBuilder(this.config, this.request);
3867
- break;
3868
- default:
3869
- throw new Error("Unsupported query method: ".concat(method));
3870
- }
3871
- QueryResult = builder.build(TABLE_NAME);
3945
+ tableName = this.config.restModel.TABLE_NAME;
3872
3946
  logContext = getLogContext(this.config, this.request);
3873
- logWithLevel(LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Generated ".concat(method.toUpperCase(), " SQL:"), QueryResult);
3874
- formatted = this.formatSQLWithParams(QueryResult.sql, QueryResult.params);
3875
- logWithLevel(LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Formatted ".concat(method.toUpperCase(), " SQL:"), formatted);
3876
- toUnnamed = namedPlaceholders();
3877
- _a = toUnnamed(QueryResult.sql, QueryResult.params), sql = _a[0], values = _a[1];
3878
- return [4 /*yield*/, this.validateSqlAllowList(sql)];
3947
+ cacheResults = method === C6Constants.GET
3948
+ && !this.config.sqlAllowListPath
3949
+ && ((_a = this.request) === null || _a === void 0 ? void 0 : _a.cacheResults) !== false;
3950
+ cacheRequestData = cacheResults
3951
+ ? JSON.parse(JSON.stringify((_b = this.request) !== null && _b !== void 0 ? _b : {}))
3952
+ : undefined;
3953
+ requestArgumentsSerialized = cacheResults
3954
+ ? sortAndSerializeQueryObject(tableName, cacheRequestData !== null && cacheRequestData !== void 0 ? cacheRequestData : {})
3955
+ : undefined;
3956
+ if (!cacheResults) return [3 /*break*/, 2];
3957
+ cachedRequest = checkCache(method, tableName, cacheRequestData);
3958
+ if (!cachedRequest) return [3 /*break*/, 2];
3959
+ return [4 /*yield*/, cachedRequest];
3960
+ case 1: return [2 /*return*/, (_c.sent()).data];
3961
+ case 2:
3962
+ sqlExecution = this.buildSqlExecutionContext(method, tableName, logContext);
3963
+ queryPromise = this.withConnection(function (conn) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
3964
+ return [2 /*return*/, this.executeQueryWithLifecycle(conn, method, sqlExecution, logContext)];
3965
+ }); }); });
3966
+ if (!(!cacheResults || !cacheRequestData || !requestArgumentsSerialized)) return [3 /*break*/, 4];
3967
+ return [4 /*yield*/, queryPromise];
3968
+ case 3: return [2 /*return*/, _c.sent()];
3969
+ case 4:
3970
+ cacheRequest = queryPromise.then(function (data) {
3971
+ return _this.createCacheResponseEnvelope(method, tableName, data);
3972
+ });
3973
+ setCache(method, tableName, cacheRequestData, {
3974
+ requestArgumentsSerialized: requestArgumentsSerialized,
3975
+ request: cacheRequest,
3976
+ });
3977
+ return [4 /*yield*/, cacheRequest];
3978
+ case 5:
3979
+ cacheResponse = _c.sent();
3980
+ setCache(method, tableName, cacheRequestData, {
3981
+ requestArgumentsSerialized: requestArgumentsSerialized,
3982
+ request: cacheRequest,
3983
+ response: cacheResponse,
3984
+ final: true,
3985
+ });
3986
+ return [2 /*return*/, cacheResponse.data];
3987
+ }
3988
+ });
3989
+ });
3990
+ };
3991
+ SqlExecutor.prototype.getQueryBuilder = function (method) {
3992
+ switch (method) {
3993
+ case C6Constants.GET:
3994
+ return new SelectQueryBuilder(this.config, this.request);
3995
+ case C6Constants.PUT:
3996
+ return new UpdateQueryBuilder(this.config, this.request);
3997
+ case C6Constants.DELETE:
3998
+ return new DeleteQueryBuilder(this.config, this.request);
3999
+ case C6Constants.POST:
4000
+ return new PostQueryBuilder(this.config, this.request);
4001
+ default:
4002
+ throw new Error("Unsupported query method: ".concat(method));
4003
+ }
4004
+ };
4005
+ SqlExecutor.prototype.buildSqlExecutionContext = function (method, tableName, logContext) {
4006
+ var builder = this.getQueryBuilder(method);
4007
+ var queryResult = builder.build(tableName);
4008
+ logWithLevel(LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Generated ".concat(method.toUpperCase(), " SQL:"), queryResult);
4009
+ var formatted = this.formatSQLWithParams(queryResult.sql, queryResult.params);
4010
+ logWithLevel(LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Formatted ".concat(method.toUpperCase(), " SQL:"), formatted);
4011
+ var toUnnamed = namedPlaceholders();
4012
+ var _a = toUnnamed(queryResult.sql, queryResult.params), sql = _a[0], values = _a[1];
4013
+ return { sql: sql, values: values };
4014
+ };
4015
+ SqlExecutor.prototype.createResponseFromQueryResult = function (method, result, sqlExecution, logContext) {
4016
+ if (method === C6Constants.GET) {
4017
+ return {
4018
+ rest: result.map(this.serialize),
4019
+ sql: { sql: sqlExecution.sql, values: sqlExecution.values },
4020
+ };
4021
+ }
4022
+ logWithLevel(LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \u270F\uFE0F Rows affected:", result.affectedRows);
4023
+ return {
4024
+ affected: result.affectedRows,
4025
+ insertId: result.insertId,
4026
+ rest: [],
4027
+ sql: { sql: sqlExecution.sql, values: sqlExecution.values },
4028
+ };
4029
+ };
4030
+ SqlExecutor.prototype.createLifecycleHookResponse = function (response) {
4031
+ var data = Object.assign({ success: true }, response);
4032
+ return { data: data };
4033
+ };
4034
+ SqlExecutor.prototype.createCacheResponseEnvelope = function (method, tableName, data) {
4035
+ return {
4036
+ data: data,
4037
+ config: {
4038
+ method: method.toLowerCase(),
4039
+ url: "/rest/".concat(tableName),
4040
+ },
4041
+ };
4042
+ };
4043
+ SqlExecutor.prototype.executeQueryWithLifecycle = function (conn, method, sqlExecution, logContext) {
4044
+ return __awaiter(this, void 0, void 0, function () {
4045
+ var useTransaction, committed, result, response, hookResponse, err_1, rollbackErr_1;
4046
+ return __generator(this, function (_a) {
4047
+ switch (_a.label) {
4048
+ case 0:
4049
+ useTransaction = method !== C6Constants.GET;
4050
+ committed = false;
4051
+ _a.label = 1;
3879
4052
  case 1:
3880
- _b.sent();
3881
- return [4 /*yield*/, this.withConnection(function (conn) { return __awaiter(_this, void 0, void 0, function () {
3882
- var result;
3883
- return __generator(this, function (_a) {
3884
- switch (_a.label) {
3885
- case 0: return [4 /*yield*/, conn.query(sql, values)];
3886
- case 1:
3887
- result = (_a.sent())[0];
3888
- if (method === 'GET') {
3889
- return [2 /*return*/, {
3890
- rest: result.map(this.serialize),
3891
- sql: { sql: sql, values: values }
3892
- }];
3893
- }
3894
- else {
3895
- logWithLevel(LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \u270F\uFE0F Rows affected:", result.affectedRows);
3896
- return [2 /*return*/, {
3897
- affected: result.affectedRows,
3898
- insertId: result.insertId,
3899
- rest: [], // TODO - remove rest empty array from non-GET responses?
3900
- sql: { sql: sql, values: values }
3901
- }];
3902
- }
3903
- }
3904
- });
3905
- }); })];
3906
- case 2: return [2 /*return*/, _b.sent()];
4053
+ _a.trys.push([1, 11, , 16]);
4054
+ if (!useTransaction) return [3 /*break*/, 3];
4055
+ logWithLevel(LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDFE Beginning transaction");
4056
+ return [4 /*yield*/, conn.beginTransaction()];
4057
+ case 2:
4058
+ _a.sent();
4059
+ _a.label = 3;
4060
+ case 3: return [4 /*yield*/, this.validateSqlAllowList(sqlExecution.sql)];
4061
+ case 4:
4062
+ _a.sent();
4063
+ return [4 /*yield*/, this.runLifecycleHooks("beforeExecution", {
4064
+ config: this.config,
4065
+ request: this.request,
4066
+ sqlExecution: sqlExecution,
4067
+ })];
4068
+ case 5:
4069
+ _a.sent();
4070
+ return [4 /*yield*/, conn.query(sqlExecution.sql, sqlExecution.values)];
4071
+ case 6:
4072
+ result = (_a.sent())[0];
4073
+ response = this.createResponseFromQueryResult(method, result, sqlExecution, logContext);
4074
+ hookResponse = this.createLifecycleHookResponse(response);
4075
+ return [4 /*yield*/, this.runLifecycleHooks("afterExecution", {
4076
+ config: this.config,
4077
+ request: this.request,
4078
+ response: hookResponse,
4079
+ })];
4080
+ case 7:
4081
+ _a.sent();
4082
+ if (!useTransaction) return [3 /*break*/, 9];
4083
+ return [4 /*yield*/, conn.commit()];
4084
+ case 8:
4085
+ _a.sent();
4086
+ committed = true;
4087
+ logWithLevel(LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDFE Transaction committed");
4088
+ _a.label = 9;
4089
+ case 9: return [4 /*yield*/, this.runLifecycleHooks("afterCommit", {
4090
+ config: this.config,
4091
+ request: this.request,
4092
+ response: hookResponse,
4093
+ })];
4094
+ case 10:
4095
+ _a.sent();
4096
+ return [2 /*return*/, response];
4097
+ case 11:
4098
+ err_1 = _a.sent();
4099
+ if (!(useTransaction && !committed)) return [3 /*break*/, 15];
4100
+ _a.label = 12;
4101
+ case 12:
4102
+ _a.trys.push([12, 14, , 15]);
4103
+ return [4 /*yield*/, conn.rollback()];
4104
+ case 13:
4105
+ _a.sent();
4106
+ logWithLevel(LogLevel.WARN, logContext, console.warn, "[SQL EXECUTOR] \uD83E\uDDFE Transaction rolled back");
4107
+ return [3 /*break*/, 15];
4108
+ case 14:
4109
+ rollbackErr_1 = _a.sent();
4110
+ logWithLevel(LogLevel.ERROR, logContext, console.error, "[SQL EXECUTOR] Rollback failed", rollbackErr_1);
4111
+ return [3 /*break*/, 15];
4112
+ case 15: throw err_1;
4113
+ case 16: return [2 /*return*/];
3907
4114
  }
3908
4115
  });
3909
4116
  });
@@ -3938,18 +4145,23 @@ var SqlExecutor$1 = /*#__PURE__*/Object.freeze({
3938
4145
  SqlExecutor: SqlExecutor
3939
4146
  });
3940
4147
 
4148
+ function restExpressRequest(routerConfig) {
4149
+ var router = routerConfig.router, _a = routerConfig.routePath, routePath = _a === void 0 ? "/rest/:table{/:primary}" : _a, handlerConfig = __rest(routerConfig, ["router", "routePath"]);
4150
+ router.all(routePath, ExpressHandler(handlerConfig));
4151
+ }
3941
4152
  // TODO - WE MUST make this a generic - optional, but helpful
3942
4153
  // note sure how it would help anyone actually...
3943
- function ExpressHandler(_a) {
4154
+ function ExpressHandler(configX) {
3944
4155
  var _this = this;
3945
- var C6 = _a.C6, mysqlPool = _a.mysqlPool, sqlAllowListPath = _a.sqlAllowListPath, websocketBroadcast = _a.websocketBroadcast;
3946
- return function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
3947
- var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, restModel, primaryKeys_1, primaryShortKeys_1, columnMap_1, resolveShortKey_1, hasPrimaryKeyValues, primaryKeyName, response, err_1;
4156
+ return function (req, res) { return __awaiter(_this, void 0, void 0, function () {
4157
+ var config, C6, incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, normalized, restModel, primaryKeys_1, primaryShortKeys_1, columnMap_1, resolveShortKey_1, hasPrimaryKeyValues, primaryKeyName, response, err_1, message;
3948
4158
  var _a, _b, _c, _d, _e, _f, _g;
3949
4159
  return __generator(this, function (_h) {
3950
4160
  switch (_h.label) {
3951
4161
  case 0:
3952
4162
  _h.trys.push([0, 2, , 3]);
4163
+ config = typeof configX === "function" ? configX() : configX;
4164
+ C6 = config.C6;
3953
4165
  incomingMethod = req.method.toUpperCase();
3954
4166
  table = req.params.table;
3955
4167
  primary = req.params.primary;
@@ -3958,12 +4170,21 @@ function ExpressHandler(_a) {
3958
4170
  treatAsGet = incomingMethod === 'POST' && methodOverride === 'GET';
3959
4171
  method = treatAsGet ? 'GET' : incomingMethod;
3960
4172
  payload = treatAsGet ? __assign({}, req.body) : (method === 'GET' ? req.query : req.body);
4173
+ // Query strings are text; coerce known boolean controls.
4174
+ if (typeof (payload === null || payload === void 0 ? void 0 : payload.cacheResults) === "string") {
4175
+ normalized = payload.cacheResults.toLowerCase();
4176
+ if (normalized === "false")
4177
+ payload.cacheResults = false;
4178
+ if (normalized === "true")
4179
+ payload.cacheResults = true;
4180
+ }
3961
4181
  // Remove transport-only METHOD flag so it never leaks into ORM parsing
3962
4182
  if (treatAsGet && 'METHOD' in payload) {
3963
4183
  try {
3964
4184
  delete payload.METHOD;
3965
4185
  }
3966
- catch ( /* noop */_j) { /* noop */ }
4186
+ catch ( /* noop */_j) { /* noop */
4187
+ }
3967
4188
  }
3968
4189
  // Warn for unsupported overrides but continue normally
3969
4190
  if (incomingMethod !== 'GET' && methodOverride && methodOverride !== 'GET') {
@@ -4025,22 +4246,16 @@ function ExpressHandler(_a) {
4025
4246
  (_g = payload[primaryKeyName]) !== null && _g !== void 0 ? _g : primary;
4026
4247
  }
4027
4248
  }
4028
- return [4 /*yield*/, restRequest({
4029
- C6: C6,
4030
- mysqlPool: mysqlPool,
4031
- sqlAllowListPath: sqlAllowListPath,
4032
- websocketBroadcast: websocketBroadcast,
4033
- requestMethod: method,
4034
- restModel: C6.TABLES[table]
4035
- })(payload)];
4249
+ return [4 /*yield*/, restRequest(__assign(__assign({}, config), { requestMethod: method, restModel: C6.TABLES[table] }))(payload)];
4036
4250
  case 1:
4037
4251
  response = _h.sent();
4038
4252
  res.status(200).json(__assign({ success: true }, response));
4039
4253
  return [3 /*break*/, 3];
4040
4254
  case 2:
4041
4255
  err_1 = _h.sent();
4042
- res.status(500).json({ success: false, error: err_1 });
4043
- next(err_1);
4256
+ message = err_1 instanceof Error ? err_1.message : String(err_1);
4257
+ logWithLevel(LogLevel.ERROR, undefined, console.error, message);
4258
+ res.status(500).json({ success: false, error: message });
4044
4259
  return [3 /*break*/, 3];
4045
4260
  case 3: return [2 /*return*/];
4046
4261
  }
@@ -4152,5 +4367,5 @@ function isVerbose() {
4152
4367
  return resolveLogLevel() >= LogLevel.DEBUG;
4153
4368
  }
4154
4369
 
4155
- export { A, AggregateBuilder, C6C, C6Constants, ConditionBuilder, DELETE, DeleteQueryBuilder, Executor, ExpressHandler, F, GET, HttpExecutor, JoinBuilder, LogLevel, POST, PUT, PaginationBuilder, PostQueryBuilder, SelectQueryBuilder, SqlExecutor, TestRestfulResponse, UpdateQueryBuilder, apiRequestCache, applyLogLevelDefaults, axiosInstance, bbox, carbonNodeQsStringify, checkAllRequestsComplete, checkCache, clearCache, collectSqlAllowListEntries, colorSql, compileSqlAllowList, convertForRequestBody, convertHexIfBinary, derivedTable, determineRuntimeJsType, distSphere, eFetchDependencies, error, extractSqlEntries, fieldEq, getEnv, getEnvBool, getEnvDebug, getEnvLogLevel, getLogContext, getPrimaryKeyTypes, group, info, isDerivedTableKey, isLocal, isNode, isTest, isVerbose, loadSqlAllowList, logSql, logWithLevel, normalizeSingularRequest, normalizeSql, notifyToast, onError, onSuccess, parseLogLevel, removeInvalidKeys, removePrefixIfExists, resolveDerivedTable, resolveLogLevel, restOrm, restRequest, setCache, setToastHandler, shouldLog, sortAndSerializeQueryObject, stContains, timeout, toastOptions, toastOptionsDevs, userCustomClearCache, versionToRgb as versionColor, warn };
4370
+ export { A, AggregateBuilder, C6C, C6Constants, ConditionBuilder, DELETE, DeleteQueryBuilder, Executor, ExpressHandler, F, GET, HttpExecutor, JoinBuilder, LogLevel, POST, PUT, PaginationBuilder, PostQueryBuilder, SelectQueryBuilder, SqlExecutor, TestRestfulResponse, UpdateQueryBuilder, apiRequestCache, applyLogLevelDefaults, axiosInstance, bbox, carbonNodeQsStringify, checkAllRequestsComplete, checkCache, clearCache, collectSqlAllowListEntries, colorSql, compileSqlAllowList, convertForRequestBody, convertHexIfBinary, derivedTable, determineRuntimeJsType, distSphere, eFetchDependencies, error, extractSqlEntries, fieldEq, getEnv, getEnvBool, getEnvDebug, getEnvLogLevel, getLogContext, getPrimaryKeyTypes, group, info, isDerivedTableKey, isLocal, isNode, isTest, isVerbose, loadSqlAllowList, logSql, logWithLevel, normalizeSingularRequest, normalizeSql, notifyToast, onError, onSuccess, parseLogLevel, removeInvalidKeys, removePrefixIfExists, resolveDerivedTable, resolveLogLevel, restExpressRequest, restOrm, restRequest, setCache, setToastHandler, shouldLog, sortAndSerializeQueryObject, stContains, timeout, toastOptions, toastOptionsDevs, userCustomClearCache, versionToRgb as versionColor, warn };
4156
4371
  //# sourceMappingURL=index.esm.js.map