@carbonorm/carbonnode 3.0.15 → 3.1.3

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