@carbonorm/carbonnode 3.0.15 → 3.1.2

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 (46) hide show
  1. package/dist/api/C6Constants.d.ts +183 -0
  2. package/dist/api/builders/queryHelpers.d.ts +4 -0
  3. package/dist/api/executors/Executor.d.ts +9 -15
  4. package/dist/api/executors/HttpExecutor.d.ts +7 -14
  5. package/dist/api/executors/SqlExecutor.d.ts +9 -12
  6. package/dist/api/orm/SqlBuilder.d.ts +17 -0
  7. package/dist/api/orm/builders/AggregateBuilder.d.ts +5 -0
  8. package/dist/api/orm/builders/ConditionBuilder.d.ts +11 -0
  9. package/dist/api/orm/builders/JoinBuilder.d.ts +5 -0
  10. package/dist/api/orm/builders/PaginationBuilder.d.ts +5 -0
  11. package/dist/api/orm/queries/DeleteQueryBuilder.d.ts +6 -0
  12. package/dist/api/orm/queries/SelectQueryBuilder.d.ts +6 -0
  13. package/dist/api/orm/queries/UpdateQueryBuilder.d.ts +6 -0
  14. package/dist/api/orm/queryHelpers.d.ts +4 -0
  15. package/dist/api/orm/utils/sqlUtils.d.ts +7 -0
  16. package/dist/api/restOrm.d.ts +3 -10
  17. package/dist/api/restRequest.d.ts +3 -10
  18. package/dist/api/types/ormGenerics.d.ts +13 -0
  19. package/dist/api/types/ormInterfaces.d.ts +23 -40
  20. package/dist/index.cjs.js +363 -407
  21. package/dist/index.cjs.js.map +1 -1
  22. package/dist/index.d.ts +10 -1
  23. package/dist/index.esm.js +351 -407
  24. package/dist/index.esm.js.map +1 -1
  25. package/package.json +1 -1
  26. package/scripts/assets/handlebars/C6.ts.handlebars +2 -4
  27. package/src/api/C6Constants.ts +37 -2
  28. package/src/api/executors/Executor.ts +18 -36
  29. package/src/api/executors/HttpExecutor.ts +46 -59
  30. package/src/api/executors/SqlExecutor.ts +17 -29
  31. package/src/api/handlers/ExpressHandler.ts +1 -1
  32. package/src/api/orm/builders/AggregateBuilder.ts +38 -0
  33. package/src/api/orm/builders/ConditionBuilder.ts +113 -0
  34. package/src/api/orm/builders/JoinBuilder.ts +25 -0
  35. package/src/api/orm/builders/PaginationBuilder.ts +28 -0
  36. package/src/api/orm/queries/DeleteQueryBuilder.ts +28 -0
  37. package/src/api/orm/queries/SelectQueryBuilder.ts +49 -0
  38. package/src/api/orm/queries/UpdateQueryBuilder.ts +42 -0
  39. package/src/api/orm/queryHelpers.ts +18 -0
  40. package/src/api/orm/utils/sqlUtils.ts +24 -0
  41. package/src/api/restOrm.ts +4 -14
  42. package/src/api/restRequest.ts +16 -34
  43. package/src/api/types/ormGenerics.ts +18 -0
  44. package/src/api/types/ormInterfaces.ts +31 -46
  45. package/src/index.ts +10 -1
  46. package/src/api/builders/sqlBuilder.ts +0 -454
package/dist/index.cjs.js CHANGED
@@ -92,6 +92,37 @@ var C6Constants = {
92
92
  SECOND: 'SECOND',
93
93
  SECOND_MICROSECOND: 'SECOND_MICROSECOND',
94
94
  SELECT: 'SELECT',
95
+ // MySQL Spatial Functions
96
+ ST_AREA: 'ST_Area',
97
+ ST_ASBINARY: 'ST_AsBinary',
98
+ ST_ASTEXT: 'ST_AsText',
99
+ ST_BUFFER: 'ST_Buffer',
100
+ ST_CONTAINS: 'ST_Contains',
101
+ ST_CROSSES: 'ST_Crosses',
102
+ ST_DIFFERENCE: 'ST_Difference',
103
+ ST_DIMENSION: 'ST_Dimension',
104
+ ST_DISJOINT: 'ST_Disjoint',
105
+ ST_DISTANCE: 'ST_Distance',
106
+ ST_DISTANCE_SPHERE: 'ST_Distance_Sphere',
107
+ ST_ENDPOINT: 'ST_EndPoint',
108
+ ST_ENVELOPE: 'ST_Envelope',
109
+ ST_EQUALS: 'ST_Equals',
110
+ ST_GEOMFROMGEOJSON: 'ST_GeomFromGeoJSON',
111
+ ST_GEOMFROMTEXT: 'ST_GeomFromText',
112
+ ST_GEOMFROMWKB: 'ST_GeomFromWKB',
113
+ ST_INTERSECTS: 'ST_Intersects',
114
+ ST_LENGTH: 'ST_Length',
115
+ ST_OVERLAPS: 'ST_Overlaps',
116
+ ST_POINT: 'ST_Point',
117
+ ST_SETSRID: 'ST_SetSRID',
118
+ ST_SRID: 'ST_SRID',
119
+ ST_STARTPOINT: 'ST_StartPoint',
120
+ ST_SYMDIFFERENCE: 'ST_SymDifference',
121
+ ST_TOUCHES: 'ST_Touches',
122
+ ST_UNION: 'ST_Union',
123
+ ST_WITHIN: 'ST_Within',
124
+ ST_X: 'ST_X',
125
+ ST_Y: 'ST_Y',
95
126
  STR_TO_DATE: 'STR_TO_DATE',
96
127
  SUBDATE: 'SUBDATE',
97
128
  SUBTIME: 'SUBTIME',
@@ -134,6 +165,7 @@ var C6Constants = {
134
165
  FINISH: 'FINISH',
135
166
  VALIDATE_C6_ENTITY_ID_REGEX: '#^([a-fA-F0-9]{20,35})$#',
136
167
  };
168
+ var C6C = C6Constants;
137
169
 
138
170
  // @link https://www.npmjs.com/package/axios-cache-adapter
139
171
  // updating these values
@@ -392,9 +424,11 @@ function isVerbose () {
392
424
  }
393
425
 
394
426
  var Executor = /** @class */ (function () {
395
- function Executor(config, request) {
427
+ function Executor(config, request, useNamedParams) {
428
+ if (useNamedParams === void 0) { useNamedParams = false; }
396
429
  this.config = config;
397
430
  this.request = request;
431
+ this.useNamedParams = useNamedParams;
398
432
  }
399
433
  Executor.prototype.runLifecycleHooks = function (phase, args) {
400
434
  return tslib.__awaiter(this, void 0, void 0, function () {
@@ -424,8 +458,10 @@ var Executor = /** @class */ (function () {
424
458
  _d.label = 2;
425
459
  case 2:
426
460
  _d.trys.push([2, 4, , 5]);
461
+ // todo - this
427
462
  return [4 /*yield*/, fn(args)];
428
463
  case 3:
464
+ // todo - this
429
465
  _d.sent();
430
466
  return [3 /*break*/, 5];
431
467
  case 4:
@@ -443,408 +479,6 @@ var Executor = /** @class */ (function () {
443
479
  return Executor;
444
480
  }());
445
481
 
446
- var SqlBuilder = /** @class */ (function (_super) {
447
- tslib.__extends(SqlBuilder, _super);
448
- function SqlBuilder() {
449
- var _this = _super !== null && _super.apply(this, arguments) || this;
450
- /** Flag to determine if named placeholders should be used */
451
- _this.useNamedParams = false;
452
- return _this;
453
- }
454
- SqlBuilder.prototype.convertHexIfBinary = function (col, val) {
455
- var _a;
456
- var columnDef = (_a = this.config.C6.TABLES[this.config.restModel.TABLE_NAME].TYPE_VALIDATION) === null || _a === void 0 ? void 0 : _a[col];
457
- if (typeof val === 'string' &&
458
- /^[0-9a-fA-F]{32}$/.test(val) &&
459
- typeof columnDef === 'object' &&
460
- columnDef.MYSQL_TYPE.toUpperCase().includes('BINARY')) {
461
- return Buffer.from(val, 'hex');
462
- }
463
- return val;
464
- };
465
- /** Generate nested WHERE/ON conditions with parameter binding (supports AND/OR/NOT) */
466
- SqlBuilder.prototype.buildBooleanJoinedConditions = function (set, andMode, params) {
467
- var _this = this;
468
- if (andMode === void 0) { andMode = true; }
469
- if (params === void 0) { params = []; }
470
- var booleanOperator = andMode ? 'AND' : 'OR';
471
- var OPERATORS = [
472
- '=', '!=', '<', '<=', '>', '>=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'IS', 'IS NOT',
473
- 'BETWEEN', 'NOT BETWEEN', C6Constants.MATCH_AGAINST
474
- ];
475
- var isNumericKeyed = function (obj) {
476
- return Array.isArray(obj) && Object.keys(obj).every(function (k) { return /^\d+$/.test(k); });
477
- };
478
- // Helper to add a single condition with proper parameter binding
479
- var addCondition = function (column, op, value) {
480
- var clause;
481
- // If using named parameters, generate a unique key; otherwise use positional placeholder
482
- var addParam = function (val) {
483
- if (Array.isArray(params)) {
484
- // Positional (unnamed) parameter
485
- params.push(_this.convertHexIfBinary(column, val));
486
- return '?';
487
- }
488
- else {
489
- // Named parameter mode
490
- var key = "param".concat(Object.keys(params).length);
491
- params[key] = _this.convertHexIfBinary(column, val);
492
- return ":".concat(key);
493
- }
494
- };
495
- if ((op === 'IN' || op === 'NOT IN') && Array.isArray(value)) {
496
- if (value.length === 0) {
497
- // Edge case: empty IN list - use a condition that's always false
498
- clause = "( ".concat(column, " ").concat(op, " (NULL) )");
499
- isVerbose() && console.warn("[WHERE] Empty list for ".concat(op, " on ").concat(column));
500
- }
501
- else {
502
- var placeholders = value.map(function (val) { return addParam(val); }).join(', ');
503
- clause = "( ".concat(column, " ").concat(op, " (").concat(placeholders, ") )");
504
- }
505
- }
506
- else if ((op === 'BETWEEN' || op === 'NOT BETWEEN') && Array.isArray(value) && value.length === 2) {
507
- // BETWEEN expects exactly two values [min, max]
508
- var minVal = value[0], maxVal = value[1];
509
- var ph1 = addParam(minVal);
510
- var ph2 = addParam(maxVal);
511
- clause = "( ".concat(column, " ").concat(op, " ").concat(ph1, " AND ").concat(ph2, " )");
512
- }
513
- else {
514
- // Default case for single-value operators (including IS, IS NOT, =, etc.)
515
- // Note: If value is null and op is 'IS' or 'IS NOT', the placeholder will safely bind null.
516
- var ph = addParam(value);
517
- clause = "( ".concat(column, " ").concat(op, " ").concat(ph, " )");
518
- }
519
- isVerbose() && console.log("[WHERE] \u2795 ".concat(clause));
520
- return clause;
521
- };
522
- var sql;
523
- if (isNumericKeyed(set)) {
524
- // Array of conditions, use opposite boolean mode for combining (AND/OR swap)
525
- isVerbose() && console.log("[WHERE] Grouping conditions (array) in ".concat(andMode ? 'AND' : 'OR', " mode:"), set);
526
- if (set.length === 0) {
527
- sql = ''; // no conditions
528
- }
529
- else if (set.length === 1) {
530
- // Single element array, just build it normally (no boolean operator needed)
531
- sql = this.buildBooleanJoinedConditions(set[0], true, params);
532
- }
533
- else {
534
- // Multiple conditions: join them with the opposite of current mode
535
- var subClauses = set.map(function (item) { return _this.buildBooleanJoinedConditions(item, true, params); });
536
- var op = andMode ? 'OR' : 'AND';
537
- sql = subClauses.map(function (sc) { return "(".concat(sc, ")"); }).join(" ".concat(op, " "));
538
- }
539
- }
540
- else if (typeof set === 'object') {
541
- var parts = [];
542
- for (var _i = 0, _a = Object.entries(set); _i < _a.length; _i++) {
543
- var _b = _a[_i], key = _b[0], value = _b[1];
544
- if (/^\d+$/.test(key)) {
545
- // Numeric object keys (should not happen because numeric arrays handled above)
546
- parts.push(this.buildBooleanJoinedConditions(value, !andMode, params));
547
- }
548
- else if (key.toUpperCase() === 'NOT') {
549
- // NOT operator: negate the inner condition(s)
550
- var negated = this.buildBooleanJoinedConditions(value, true, params);
551
- parts.push("NOT (".concat(negated, ")"));
552
- isVerbose() && console.log("[WHERE] NOT block: (".concat(negated, ")"));
553
- }
554
- else if (!Array.isArray(value)) {
555
- if (typeof value === 'object' && value !== null && Object.keys(value).length === 1) {
556
- var _c = Object.entries(value)[0], op = _c[0], val = _c[1];
557
- if (OPERATORS.includes(op.toUpperCase())) {
558
- if (value !== null &&
559
- C6Constants.MATCH_AGAINST in value &&
560
- Array.isArray(value[C6Constants.MATCH_AGAINST])) {
561
- var _d = value[C6Constants.MATCH_AGAINST], search = _d[0], mode = _d[1];
562
- var paramName = "param".concat(Object.keys(params).length);
563
- params[paramName] = search;
564
- var againstClause = void 0;
565
- switch (mode === null || mode === void 0 ? void 0 : mode.toUpperCase()) {
566
- case 'BOOLEAN':
567
- againstClause = "AGAINST(:".concat(paramName, " IN BOOLEAN MODE)");
568
- break;
569
- case 'WITH QUERY EXPANSION':
570
- againstClause = "AGAINST(:".concat(paramName, " WITH QUERY EXPANSION)");
571
- break;
572
- case 'NATURAL':
573
- case undefined:
574
- case null:
575
- againstClause = "AGAINST(:".concat(paramName, ")"); // default natural language mode
576
- break;
577
- default:
578
- throw new Error("Unsupported MATCH_AGAINST mode: ".concat(mode));
579
- }
580
- parts.push("(MATCH(".concat(key, ") ").concat(againstClause, ")"));
581
- continue;
582
- }
583
- parts.push(addCondition(key, op.toUpperCase(), val));
584
- }
585
- else {
586
- throw new Error("Unsupported operator '".concat(op, "' for key '").concat(key, "'"));
587
- }
588
- }
589
- else {
590
- // Fallback: treat as simple equality
591
- parts.push(addCondition(key, '=', value));
592
- }
593
- }
594
- else if (value.length === 2 && OPERATORS.includes(value[0])) {
595
- // Two-element array, first is an operator (like ['>', 5])
596
- parts.push(addCondition(key, value[0], value[1]));
597
- }
598
- else if (value.length === 3 && OPERATORS.includes(value[0])) {
599
- // Three-element array, e.g. ['BETWEEN', min, max]
600
- parts.push(addCondition(key, value[0], value.slice(1)));
601
- }
602
- else {
603
- throw new Error("Invalid condition for ".concat(key, ": ").concat(JSON.stringify(value)));
604
- }
605
- }
606
- sql = parts.length ? parts.join(" ".concat(booleanOperator, " ")) : '';
607
- }
608
- else {
609
- // Primitive type (should not happen normally), treat it as a boolean expression string
610
- sql = String(set);
611
- }
612
- // Wrap the final combined condition in parentheses to maintain correct precedence
613
- return sql ? "(".concat(sql, ")") : '';
614
- };
615
- /** Translate array or function call definitions into SQL expressions (supports nested calls) */
616
- SqlBuilder.prototype.buildAggregateField = function (field) {
617
- var _this = this;
618
- if (typeof field === 'string') {
619
- return field;
620
- }
621
- if (!Array.isArray(field) || field.length === 0) {
622
- throw new Error('Invalid SELECT field entry');
623
- }
624
- // e.g. field = [ 'ROUND', [ 'SQRT', 'age' ], 2, 'AS', 'roundedRootAge' ]
625
- var fn = field[0], args = field.slice(1);
626
- var alias;
627
- // Handle alias if present in array (e.g. ... 'AS', 'aliasName')
628
- if (args.length >= 2 && String(args[args.length - 2]).toUpperCase() === 'AS') {
629
- alias = String(args.pop()); // last element is alias name
630
- args.pop(); // remove the 'AS'
631
- }
632
- var F = String(fn).toUpperCase();
633
- // Map each argument to SQL string, handling nested function arrays recursively
634
- var argList = args.map(function (arg) {
635
- if (Array.isArray(arg)) {
636
- return _this.buildAggregateField(arg);
637
- }
638
- else {
639
- return arg;
640
- }
641
- }).join(', ');
642
- var expr;
643
- switch (F) {
644
- case 'DATE_ADD':
645
- case 'DATE_SUB':
646
- // Functions with comma-separated interval, assume args like [ date, interval_expression ]
647
- expr = "".concat(F, "(").concat(argList, ")");
648
- break;
649
- case 'YEAR':
650
- case 'MONTH':
651
- case 'DAY':
652
- case 'ROUND':
653
- case 'CEIL':
654
- case 'FLOOR':
655
- case 'ABS':
656
- case 'SQRT':
657
- case 'UPPER':
658
- case 'LOWER':
659
- case 'COALESCE':
660
- case 'CONCAT':
661
- case 'IFNULL':
662
- case 'IF':
663
- case 'COUNT':
664
- case 'SUM':
665
- case 'MIN':
666
- case 'MAX':
667
- case 'AVG':
668
- // Common SQL functions – just format as F(arg1, arg2, ...)
669
- expr = "".concat(F, "(").concat(argList, ")");
670
- break;
671
- case 'ST_DISTANCE':
672
- expr = "ST_Distance(".concat(argList, ")");
673
- break;
674
- default:
675
- if (/^[A-Z_]+$/.test(F)) {
676
- // Allow any other SQL function by name (assuming it's a valid function)
677
- expr = "".concat(F, "(").concat(argList, ")");
678
- }
679
- else {
680
- throw new Error("Unsupported function: ".concat(F));
681
- }
682
- }
683
- if (alias) {
684
- expr += " AS ".concat(alias);
685
- }
686
- isVerbose() && console.log("[SELECT] ".concat(expr));
687
- return expr;
688
- };
689
- SqlBuilder.prototype.buildJoinClauses = function (joinArgs, params) {
690
- var sql = '';
691
- for (var joinType in joinArgs) {
692
- var jk = joinType.replace('_', ' ').toUpperCase();
693
- for (var joinTable in joinArgs[joinType]) {
694
- var onConditions = joinArgs[joinType][joinTable];
695
- var onClause = this.buildBooleanJoinedConditions(onConditions, true, params);
696
- sql += " ".concat(jk, " JOIN `").concat(joinTable, "` ON ").concat(onClause);
697
- isVerbose() && console.log("[JOIN] ".concat(jk, " JOIN ").concat(joinTable, " ON ").concat(onClause));
698
- }
699
- }
700
- return sql;
701
- };
702
- SqlBuilder.prototype.buildPaginationClause = function (pagination, _params) {
703
- var _a;
704
- var sql = '';
705
- if (pagination === null || pagination === void 0 ? void 0 : pagination[C6Constants.ORDER]) {
706
- var orderParts = Object.entries(pagination[C6Constants.ORDER])
707
- .map(function (_a) {
708
- var col = _a[0], dir = _a[1];
709
- return "".concat(col, " ").concat(String(dir).toUpperCase());
710
- });
711
- sql += " ORDER BY ".concat(orderParts.join(', '));
712
- isVerbose() && console.log("[ORDER BY] ".concat(orderParts));
713
- }
714
- if ((pagination === null || pagination === void 0 ? void 0 : pagination[C6Constants.LIMIT]) != null) {
715
- var lim = parseInt(pagination[C6Constants.LIMIT], 10);
716
- var page = parseInt((_a = pagination[C6Constants.PAGE]) !== null && _a !== void 0 ? _a : 1, 10);
717
- var offset = (page - 1) * lim;
718
- sql += " LIMIT ".concat(offset, ", ").concat(lim);
719
- isVerbose() && console.log("[LIMIT] ".concat(offset, ", ").concat(lim));
720
- }
721
- return sql;
722
- };
723
- SqlBuilder.prototype.buildWhereClause = function (whereArg, params) {
724
- var whereClause = this.buildBooleanJoinedConditions(whereArg, true, params);
725
- if (whereClause) {
726
- var trimmed = whereClause.replace(/^\((.*)\)$/, '$1');
727
- isVerbose() && console.log("[WHERE] ".concat(trimmed));
728
- return " WHERE ".concat(trimmed);
729
- }
730
- return '';
731
- };
732
- SqlBuilder.prototype.buildSelectQuery = function (table, primary, args, isSubSelect) {
733
- var _this = this;
734
- var _a, _b, _c;
735
- if (isSubSelect === void 0) { isSubSelect = false; }
736
- var model = this.config.C6.TABLES[table];
737
- var params = this.useNamedParams ? {} : [];
738
- var selectList = (_a = args === null || args === void 0 ? void 0 : args[C6Constants.SELECT]) !== null && _a !== void 0 ? _a : ['*'];
739
- var selectFields = Array.isArray(selectList)
740
- ? selectList.map(function (f) { return _this.buildAggregateField(f); }).join(', ')
741
- : String(selectList);
742
- var sql = "SELECT ".concat(selectFields, " FROM ").concat(table);
743
- if (args === null || args === void 0 ? void 0 : args[C6Constants.JOIN]) {
744
- sql += this.buildJoinClauses(args[C6Constants.JOIN], params);
745
- }
746
- if (args === null || args === void 0 ? void 0 : args[C6Constants.WHERE]) {
747
- sql += this.buildWhereClause(args[C6Constants.WHERE], params);
748
- }
749
- if (args === null || args === void 0 ? void 0 : args[C6Constants.GROUP_BY]) {
750
- var gb = Array.isArray(args[C6Constants.GROUP_BY])
751
- ? args[C6Constants.GROUP_BY].join(', ')
752
- : args[C6Constants.GROUP_BY];
753
- sql += " GROUP BY ".concat(gb);
754
- isVerbose() && console.log("[GROUP BY] ".concat(gb));
755
- }
756
- if (args === null || args === void 0 ? void 0 : args[C6Constants.HAVING]) {
757
- var havingClause = this.buildBooleanJoinedConditions(args[C6Constants.HAVING], true, params);
758
- if (havingClause) {
759
- sql += " HAVING ".concat(havingClause);
760
- }
761
- }
762
- if (args === null || args === void 0 ? void 0 : args[C6Constants.PAGINATION]) {
763
- sql += this.buildPaginationClause(args[C6Constants.PAGINATION], params);
764
- }
765
- else if (!isSubSelect) {
766
- var orderKey = primary || ((_b = model === null || model === void 0 ? void 0 : model.PRIMARY_SHORT) === null || _b === void 0 ? void 0 : _b[0]);
767
- if (!orderKey) {
768
- for (var _i = 0, _d = ['created_at', 'updated_at']; _i < _d.length; _i++) {
769
- var ts = _d[_i];
770
- if ((_c = model === null || model === void 0 ? void 0 : model.COLUMNS) === null || _c === void 0 ? void 0 : _c["".concat(table, ".").concat(ts)]) {
771
- orderKey = ts;
772
- break;
773
- }
774
- }
775
- }
776
- if (orderKey) {
777
- var dir = primary ? 'ASC' : 'DESC';
778
- sql += " ORDER BY ".concat(orderKey, " ").concat(dir, " LIMIT ").concat(primary ? 1 : 100);
779
- isVerbose() && console.log("[DEFAULT ORDER] ".concat(orderKey, " ").concat(dir));
780
- }
781
- else {
782
- sql += " LIMIT 100";
783
- isVerbose() && console.warn("[DEFAULT LIMIT] 100 (no order key found)");
784
- }
785
- }
786
- isVerbose() && console.log("[SQL SELECT]", sql, params);
787
- return { sql: sql, params: params };
788
- };
789
- SqlBuilder.prototype.buildUpdateQuery = function (table, data, args) {
790
- if (args === void 0) { args = {}; }
791
- var params = this.useNamedParams ? {} : [];
792
- var sql = "UPDATE ".concat(table);
793
- if (args[C6Constants.JOIN]) {
794
- sql += this.buildJoinClauses(args[C6Constants.JOIN], params);
795
- }
796
- var setClauses = [];
797
- for (var _i = 0, _a = Object.entries(data); _i < _a.length; _i++) {
798
- var _b = _a[_i], col = _b[0], val = _b[1];
799
- if (Array.isArray(val)) {
800
- var expr = this.buildAggregateField(val);
801
- setClauses.push("`".concat(col, "` = ").concat(expr));
802
- }
803
- else {
804
- var key = "param".concat(Array.isArray(params) ? params.length : Object.keys(params).length);
805
- if (Array.isArray(params)) {
806
- params.push(val);
807
- setClauses.push("`".concat(col, "` = ?"));
808
- }
809
- else {
810
- params[key] = val;
811
- setClauses.push("`".concat(col, "` = :").concat(key));
812
- }
813
- }
814
- }
815
- sql += " SET ".concat(setClauses.join(', '));
816
- isVerbose() && console.log("[SET] ".concat(setClauses));
817
- if (args[C6Constants.WHERE]) {
818
- sql += this.buildWhereClause(args[C6Constants.WHERE], params);
819
- }
820
- if (args[C6Constants.PAGINATION]) {
821
- sql += this.buildPaginationClause(args[C6Constants.PAGINATION], params);
822
- }
823
- isVerbose() && console.log("[SQL UPDATE]", sql, params);
824
- return { sql: sql, params: params };
825
- };
826
- /** Compose a DELETE query with optional JOIN and WHERE clauses */
827
- SqlBuilder.prototype.buildDeleteQuery = function (table, args) {
828
- if (args === void 0) { args = {}; }
829
- var params = this.useNamedParams ? {} : [];
830
- var sql = args[C6Constants.JOIN]
831
- ? "DELETE ".concat(table, " FROM ").concat(table)
832
- : "DELETE FROM ".concat(table);
833
- if (args[C6Constants.JOIN]) {
834
- sql += this.buildJoinClauses(args[C6Constants.JOIN], params);
835
- }
836
- if (args[C6Constants.WHERE]) {
837
- sql += this.buildWhereClause(args[C6Constants.WHERE], params);
838
- }
839
- if (args[C6Constants.PAGINATION]) {
840
- sql += this.buildPaginationClause(args[C6Constants.PAGINATION], params);
841
- }
842
- isVerbose() && console.log("[SQL DELETE]", sql, params);
843
- return { sql: sql, params: params };
844
- };
845
- return SqlBuilder;
846
- }(Executor));
847
-
848
482
  function isLocal () {
849
483
  return getEnvVar('NODE_ENV', '') === 'development';
850
484
  }
@@ -1563,6 +1197,237 @@ var HttpExecutor$1 = /*#__PURE__*/Object.freeze({
1563
1197
  HttpExecutor: HttpExecutor
1564
1198
  });
1565
1199
 
1200
+ function convertHexIfBinary(_col, val, columnDef) {
1201
+ if (typeof val === 'string' &&
1202
+ /^[0-9a-fA-F]{32}$/.test(val) &&
1203
+ typeof columnDef === 'object' &&
1204
+ columnDef.MYSQL_TYPE.toUpperCase().includes('BINARY')) {
1205
+ return Buffer.from(val, 'hex');
1206
+ }
1207
+ return val;
1208
+ }
1209
+
1210
+ var AggregateBuilder = /** @class */ (function (_super) {
1211
+ tslib.__extends(AggregateBuilder, _super);
1212
+ function AggregateBuilder() {
1213
+ return _super !== null && _super.apply(this, arguments) || this;
1214
+ }
1215
+ AggregateBuilder.prototype.buildAggregateField = function (field) {
1216
+ var _this = this;
1217
+ if (typeof field === 'string') {
1218
+ return field;
1219
+ }
1220
+ if (!Array.isArray(field) || field.length === 0) {
1221
+ throw new Error('Invalid SELECT field entry');
1222
+ }
1223
+ var fn = field[0], args = field.slice(1);
1224
+ var alias;
1225
+ if (args.length >= 2 && String(args[args.length - 2]).toUpperCase() === 'AS') {
1226
+ alias = String(args.pop());
1227
+ args.pop();
1228
+ }
1229
+ var F = String(fn).toUpperCase();
1230
+ var argList = args.map(function (arg) {
1231
+ return Array.isArray(arg) ? _this.buildAggregateField(arg) : arg;
1232
+ }).join(', ');
1233
+ var expr = "".concat(F, "(").concat(argList, ")");
1234
+ if (alias) {
1235
+ expr += " AS ".concat(alias);
1236
+ }
1237
+ isVerbose() && console.log("[SELECT] ".concat(expr));
1238
+ return expr;
1239
+ };
1240
+ return AggregateBuilder;
1241
+ }(Executor));
1242
+
1243
+ var ConditionBuilder = /** @class */ (function (_super) {
1244
+ tslib.__extends(ConditionBuilder, _super);
1245
+ function ConditionBuilder() {
1246
+ var _this = _super !== null && _super.apply(this, arguments) || this;
1247
+ _this.OPERATORS = new Set([
1248
+ '=', '!=', '<', '<=', '>', '>=',
1249
+ 'LIKE', 'NOT LIKE', 'IN', 'NOT IN',
1250
+ 'IS', 'IS NOT', 'BETWEEN', 'NOT BETWEEN',
1251
+ C6Constants.MATCH_AGAINST
1252
+ ]);
1253
+ return _this;
1254
+ }
1255
+ ConditionBuilder.prototype.execute = function () {
1256
+ throw new Error("Method not implemented.");
1257
+ };
1258
+ ConditionBuilder.prototype.validateOperator = function (op) {
1259
+ if (!this.OPERATORS.has(op)) {
1260
+ throw new Error("Invalid or unsupported SQL operator detected: '".concat(op, "'"));
1261
+ }
1262
+ };
1263
+ ConditionBuilder.prototype.addParam = function (params, column, value) {
1264
+ var _a, _b;
1265
+ var columnDef = (_b = (_a = this.config.C6[column.split('.')[0]]) === null || _a === void 0 ? void 0 : _a.TYPE_VALIDATION) === null || _b === void 0 ? void 0 : _b[column];
1266
+ var val = convertHexIfBinary(column, value, columnDef);
1267
+ if (this.useNamedParams) {
1268
+ var key = "param".concat(Object.keys(params).length);
1269
+ params[key] = val;
1270
+ return ":".concat(key);
1271
+ }
1272
+ else {
1273
+ params.push(val);
1274
+ return '?';
1275
+ }
1276
+ };
1277
+ ConditionBuilder.prototype.buildBooleanJoinedConditions = function (set, andMode, params) {
1278
+ var _this = this;
1279
+ if (andMode === void 0) { andMode = true; }
1280
+ if (params === void 0) { params = []; }
1281
+ var booleanOperator = andMode ? 'AND' : 'OR';
1282
+ var addCondition = function (column, op, value) {
1283
+ _this.validateOperator(op);
1284
+ if (op === C6Constants.MATCH_AGAINST && Array.isArray(value)) {
1285
+ var search = value[0], mode = value[1];
1286
+ var paramName = _this.useNamedParams ? "param".concat(Object.keys(params).length) : null;
1287
+ if (_this.useNamedParams) {
1288
+ params[paramName] = search;
1289
+ }
1290
+ else {
1291
+ params.push(search);
1292
+ }
1293
+ var againstClause = void 0;
1294
+ switch ((mode || '').toUpperCase()) {
1295
+ case 'BOOLEAN':
1296
+ againstClause = _this.useNamedParams ? "AGAINST(:".concat(paramName, " IN BOOLEAN MODE)") : "AGAINST(? IN BOOLEAN MODE)";
1297
+ break;
1298
+ case 'WITH QUERY EXPANSION':
1299
+ againstClause = _this.useNamedParams ? "AGAINST(:".concat(paramName, " WITH QUERY EXPANSION)") : "AGAINST(? WITH QUERY EXPANSION)";
1300
+ break;
1301
+ default: // NATURAL or undefined
1302
+ againstClause = _this.useNamedParams ? "AGAINST(:".concat(paramName, ")") : "AGAINST(?)";
1303
+ break;
1304
+ }
1305
+ var matchClause = "(MATCH(".concat(column, ") ").concat(againstClause, ")");
1306
+ isVerbose() && console.log("[MATCH_AGAINST] ".concat(matchClause));
1307
+ return matchClause;
1308
+ }
1309
+ // handle other operators
1310
+ return "( ".concat(column, " ").concat(op, " ").concat(_this.addParam(params, column, value), " )");
1311
+ };
1312
+ var parts = [];
1313
+ if (typeof set === 'object' && !Array.isArray(set)) {
1314
+ for (var _i = 0, _a = Object.entries(set); _i < _a.length; _i++) {
1315
+ var _b = _a[_i], key = _b[0], value = _b[1];
1316
+ if (typeof value === 'object' && value !== null && Object.keys(value).length === 1) {
1317
+ var _c = Object.entries(value)[0], op = _c[0], val = _c[1];
1318
+ parts.push(addCondition(key, op, val));
1319
+ }
1320
+ else {
1321
+ parts.push(addCondition(key, '=', value));
1322
+ }
1323
+ }
1324
+ }
1325
+ var clause = parts.join(" ".concat(booleanOperator, " "));
1326
+ return clause ? "(".concat(clause, ")") : '';
1327
+ };
1328
+ ConditionBuilder.prototype.buildWhereClause = function (whereArg, params) {
1329
+ var clause = this.buildBooleanJoinedConditions(whereArg, true, params);
1330
+ if (!clause)
1331
+ return '';
1332
+ var trimmed = clause.replace(/^\((.*)\)$/, '$1');
1333
+ isVerbose() && console.log("[WHERE] ".concat(trimmed));
1334
+ return " WHERE ".concat(trimmed);
1335
+ };
1336
+ return ConditionBuilder;
1337
+ }(AggregateBuilder));
1338
+
1339
+ var JoinBuilder = /** @class */ (function (_super) {
1340
+ tslib.__extends(JoinBuilder, _super);
1341
+ function JoinBuilder() {
1342
+ return _super !== null && _super.apply(this, arguments) || this;
1343
+ }
1344
+ JoinBuilder.prototype.buildJoinClauses = function (joinArgs, params) {
1345
+ var sql = '';
1346
+ for (var joinType in joinArgs) {
1347
+ var joinKind = joinType.replace('_', ' ').toUpperCase();
1348
+ for (var raw in joinArgs[joinType]) {
1349
+ var _a = raw.split(' '), table = _a[0], alias = _a[1];
1350
+ var onClause = this.buildBooleanJoinedConditions(joinArgs[joinType][raw], true, params);
1351
+ var joinSql = alias ? "`".concat(table, "` AS `").concat(alias, "`") : "`".concat(table, "`");
1352
+ sql += " ".concat(joinKind, " JOIN ").concat(joinSql, " ON ").concat(onClause);
1353
+ }
1354
+ }
1355
+ isVerbose() && console.log("[JOIN] ".concat(sql.trim()));
1356
+ return sql;
1357
+ };
1358
+ return JoinBuilder;
1359
+ }(ConditionBuilder));
1360
+
1361
+ var PaginationBuilder = /** @class */ (function (_super) {
1362
+ tslib.__extends(PaginationBuilder, _super);
1363
+ function PaginationBuilder() {
1364
+ return _super !== null && _super.apply(this, arguments) || this;
1365
+ }
1366
+ PaginationBuilder.prototype.buildPaginationClause = function (pagination) {
1367
+ var _this = this;
1368
+ var _a;
1369
+ var sql = '';
1370
+ if (pagination === null || pagination === void 0 ? void 0 : pagination[C6Constants.ORDER]) {
1371
+ var orderParts = Object.entries(pagination[C6Constants.ORDER])
1372
+ .map(function (_a) {
1373
+ var col = _a[0], dir = _a[1];
1374
+ return "".concat(Array.isArray(col) ? _this.buildAggregateField(col) : col, " ").concat(String(dir).toUpperCase());
1375
+ });
1376
+ sql += " ORDER BY ".concat(orderParts.join(', '));
1377
+ }
1378
+ if ((pagination === null || pagination === void 0 ? void 0 : pagination[C6Constants.LIMIT]) != null) {
1379
+ var lim = parseInt(pagination[C6Constants.LIMIT], 10);
1380
+ var page = parseInt((_a = pagination[C6Constants.PAGE]) !== null && _a !== void 0 ? _a : 1, 10);
1381
+ var offset = (page - 1) * lim;
1382
+ sql += " LIMIT ".concat(offset, ", ").concat(lim);
1383
+ }
1384
+ isVerbose() && console.log("[PAGINATION] ".concat(sql.trim()));
1385
+ return sql;
1386
+ };
1387
+ return PaginationBuilder;
1388
+ }(JoinBuilder));
1389
+
1390
+ var SelectQueryBuilder = /** @class */ (function (_super) {
1391
+ tslib.__extends(SelectQueryBuilder, _super);
1392
+ function SelectQueryBuilder() {
1393
+ return _super !== null && _super.apply(this, arguments) || this;
1394
+ }
1395
+ SelectQueryBuilder.prototype.build = function (table, args, primary, isSubSelect) {
1396
+ var _this = this;
1397
+ var _a;
1398
+ if (isSubSelect === void 0) { isSubSelect = false; }
1399
+ var params = this.useNamedParams ? {} : [];
1400
+ var selectList = (_a = args.SELECT) !== null && _a !== void 0 ? _a : ['*'];
1401
+ var selectFields = selectList
1402
+ .map(function (f) { return _this.buildAggregateField(f); })
1403
+ .join(', ');
1404
+ var sql = "SELECT ".concat(selectFields, " FROM `").concat(table, "`");
1405
+ if (args.JOIN) {
1406
+ sql += this.buildJoinClauses(args.JOIN, params);
1407
+ }
1408
+ if (args.WHERE) {
1409
+ sql += this.buildWhereClause(args.WHERE, params);
1410
+ }
1411
+ if (args.GROUP_BY) {
1412
+ var groupBy = Array.isArray(args.GROUP_BY)
1413
+ ? args.GROUP_BY.join(', ')
1414
+ : args.GROUP_BY;
1415
+ sql += " GROUP BY ".concat(groupBy);
1416
+ }
1417
+ if (args.HAVING) {
1418
+ sql += " HAVING ".concat(this.buildBooleanJoinedConditions(args.HAVING, true, params));
1419
+ }
1420
+ if (args.PAGINATION) {
1421
+ sql += this.buildPaginationClause(args.PAGINATION);
1422
+ }
1423
+ else if (!isSubSelect) {
1424
+ sql += primary ? " ORDER BY ".concat(primary, " ASC LIMIT 1") : " LIMIT 100";
1425
+ }
1426
+ return { sql: sql, params: params };
1427
+ };
1428
+ return SelectQueryBuilder;
1429
+ }(PaginationBuilder));
1430
+
1566
1431
  var SqlExecutor = /** @class */ (function (_super) {
1567
1432
  tslib.__extends(SqlExecutor, _super);
1568
1433
  function SqlExecutor() {
@@ -1655,7 +1520,7 @@ var SqlExecutor = /** @class */ (function (_super) {
1655
1520
  return tslib.__generator(this, function (_b) {
1656
1521
  switch (_b.label) {
1657
1522
  case 0:
1658
- QueryResult = this.buildSelectQuery(table, primary, args);
1523
+ QueryResult = (new SelectQueryBuilder(this.config, this.request)).build(table, args, primary);
1659
1524
  console.log("[SQL EXECUTOR] \uD83E\uDDE0 Generated SELECT SQL:", QueryResult);
1660
1525
  formatted = this.formatSQLWithParams(QueryResult.sql, QueryResult.params);
1661
1526
  console.log("[SQL EXECUTOR] \uD83E\uDDE0 Formatted SELECT SQL:", formatted);
@@ -1824,7 +1689,7 @@ var SqlExecutor = /** @class */ (function (_super) {
1824
1689
  return "'".concat(JSON.stringify(val), "'");
1825
1690
  };
1826
1691
  return SqlExecutor;
1827
- }(SqlBuilder));
1692
+ }(Executor));
1828
1693
 
1829
1694
  var SqlExecutor$1 = /*#__PURE__*/Object.freeze({
1830
1695
  __proto__: null,
@@ -1879,7 +1744,7 @@ function ExpressHandler(_a) {
1879
1744
  return [3 /*break*/, 3];
1880
1745
  case 2:
1881
1746
  err_1 = _a.sent();
1882
- res.status(500).json({ success: false });
1747
+ res.status(500).json({ success: false, error: err_1 });
1883
1748
  next(err_1);
1884
1749
  return [3 /*break*/, 3];
1885
1750
  case 3: return [2 /*return*/];
@@ -1888,6 +1753,85 @@ function ExpressHandler(_a) {
1888
1753
  }); };
1889
1754
  }
1890
1755
 
1756
+ // Alias a table name with a given alias
1757
+ var A = function (tableName, alias) {
1758
+ return "".concat(tableName, " ").concat(alias);
1759
+ };
1760
+ // Qualify a column constant (e.g. 'property_units.parcel_id') to an alias
1761
+ var F = function (qualifiedCol, alias) {
1762
+ return "".concat(alias, ".").concat(qualifiedCol.split('.').pop());
1763
+ };
1764
+ // Equal join condition using full-qualified column constants
1765
+ var fieldEq = function (leftCol, rightCol, leftAlias, rightAlias) {
1766
+ var _a;
1767
+ return (_a = {},
1768
+ _a[F(leftCol, leftAlias)] = F(rightCol, rightAlias),
1769
+ _a);
1770
+ };
1771
+ // ST_Distance_Sphere for aliased fields
1772
+ var distSphere = function (fromCol, toCol, fromAlias, toAlias) {
1773
+ return [C6C.ST_DISTANCE_SPHERE, F(fromCol, fromAlias), F(toCol, toAlias)];
1774
+ };
1775
+
1776
+ var DeleteQueryBuilder = /** @class */ (function (_super) {
1777
+ tslib.__extends(DeleteQueryBuilder, _super);
1778
+ function DeleteQueryBuilder() {
1779
+ return _super !== null && _super.apply(this, arguments) || this;
1780
+ }
1781
+ DeleteQueryBuilder.prototype.build = function (table, args) {
1782
+ if (args === void 0) { args = {}; }
1783
+ var params = this.useNamedParams ? {} : [];
1784
+ var sql = args.JOIN ? "DELETE ".concat(table, "\n FROM `").concat(table, "`") : "DELETE\n FROM `".concat(table, "`");
1785
+ if (args.JOIN) {
1786
+ sql += this.buildJoinClauses(args.JOIN, params);
1787
+ }
1788
+ if (args.WHERE) {
1789
+ sql += this.buildWhereClause(args.WHERE, params);
1790
+ }
1791
+ if (args.PAGINATION) {
1792
+ sql += this.buildPaginationClause(args.PAGINATION);
1793
+ }
1794
+ return { sql: sql, params: params };
1795
+ };
1796
+ return DeleteQueryBuilder;
1797
+ }(PaginationBuilder));
1798
+
1799
+ var UpdateQueryBuilder = /** @class */ (function (_super) {
1800
+ tslib.__extends(UpdateQueryBuilder, _super);
1801
+ function UpdateQueryBuilder() {
1802
+ return _super !== null && _super.apply(this, arguments) || this;
1803
+ }
1804
+ UpdateQueryBuilder.prototype.build = function (table, data, args) {
1805
+ if (args === void 0) { args = {}; }
1806
+ var params = this.useNamedParams ? {} : [];
1807
+ var sql = "UPDATE `".concat(table, "`");
1808
+ if (args.JOIN) {
1809
+ sql += this.buildJoinClauses(args.JOIN, params);
1810
+ }
1811
+ var setClauses = Object.entries(data).map(function (_a) {
1812
+ var col = _a[0], val = _a[1];
1813
+ if (Array.isArray(params)) {
1814
+ params.push(val);
1815
+ return "`".concat(col, "` = ?");
1816
+ }
1817
+ else {
1818
+ var key = "param".concat(Object.keys(params).length);
1819
+ params[key] = val;
1820
+ return "`".concat(col, "` = :").concat(key);
1821
+ }
1822
+ });
1823
+ sql += " SET ".concat(setClauses.join(', '));
1824
+ if (args.WHERE) {
1825
+ sql += this.buildWhereClause(args.WHERE, params);
1826
+ }
1827
+ if (args.PAGINATION) {
1828
+ sql += this.buildPaginationClause(args.PAGINATION);
1829
+ }
1830
+ return { sql: sql, params: params };
1831
+ };
1832
+ return UpdateQueryBuilder;
1833
+ }(PaginationBuilder));
1834
+
1891
1835
  function determineRuntimeJsType(mysqlType) {
1892
1836
  var base = mysqlType.toLowerCase().split('(')[0];
1893
1837
  if ([
@@ -1986,24 +1930,36 @@ function onError(message) {
1986
1930
  reactToastify.toast.error(message, isLocal() ? toastOptionsDevs : toastOptions);
1987
1931
  }
1988
1932
 
1933
+ exports.A = A;
1934
+ exports.AggregateBuilder = AggregateBuilder;
1935
+ exports.C6C = C6C;
1989
1936
  exports.C6Constants = C6Constants;
1937
+ exports.ConditionBuilder = ConditionBuilder;
1990
1938
  exports.DELETE = DELETE;
1939
+ exports.DeleteQueryBuilder = DeleteQueryBuilder;
1991
1940
  exports.Executor = Executor;
1992
1941
  exports.ExpressHandler = ExpressHandler;
1942
+ exports.F = F;
1993
1943
  exports.GET = GET;
1994
1944
  exports.HttpExecutor = HttpExecutor;
1945
+ exports.JoinBuilder = JoinBuilder;
1995
1946
  exports.POST = POST;
1996
1947
  exports.PUT = PUT;
1997
- exports.SqlBuilder = SqlBuilder;
1948
+ exports.PaginationBuilder = PaginationBuilder;
1949
+ exports.SelectQueryBuilder = SelectQueryBuilder;
1998
1950
  exports.SqlExecutor = SqlExecutor;
1999
1951
  exports.TestRestfulResponse = TestRestfulResponse;
1952
+ exports.UpdateQueryBuilder = UpdateQueryBuilder;
2000
1953
  exports.axiosInstance = axiosInstance;
2001
1954
  exports.checkAllRequestsComplete = checkAllRequestsComplete;
2002
1955
  exports.checkCache = checkCache;
2003
1956
  exports.clearCache = clearCache;
2004
1957
  exports.convertForRequestBody = convertForRequestBody;
1958
+ exports.convertHexIfBinary = convertHexIfBinary;
2005
1959
  exports.determineRuntimeJsType = determineRuntimeJsType;
1960
+ exports.distSphere = distSphere;
2006
1961
  exports.error = error;
1962
+ exports.fieldEq = fieldEq;
2007
1963
  exports.getEnvVar = getEnvVar;
2008
1964
  exports.getPrimaryKeyTypes = getPrimaryKeyTypes;
2009
1965
  exports.group = group;