@carbonorm/carbonnode 3.8.4 → 3.9.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.
package/dist/index.esm.js CHANGED
@@ -58,6 +58,7 @@ var C6Constants = {
58
58
  INNER: 'INNER',
59
59
  INTERVAL: 'INTERVAL',
60
60
  JOIN: 'JOIN',
61
+ FROM: 'FROM',
61
62
  LEFT: 'LEFT',
62
63
  LEFT_OUTER: 'LEFT_OUTER',
63
64
  LESS_THAN: '<',
@@ -89,6 +90,8 @@ var C6Constants = {
89
90
  SECOND: 'SECOND',
90
91
  SECOND_MICROSECOND: 'SECOND_MICROSECOND',
91
92
  SELECT: 'SELECT',
93
+ SUBSELECT: 'SUBSELECT',
94
+ PARAM: 'PARAM',
92
95
  // MySQL Spatial Functions
93
96
  ST_AREA: 'ST_Area',
94
97
  ST_ASBINARY: 'ST_AsBinary',
@@ -1251,14 +1254,34 @@ var AggregateBuilder = /** @class */ (function (_super) {
1251
1254
  _this.selectAliases = new Set();
1252
1255
  return _this;
1253
1256
  }
1254
- AggregateBuilder.prototype.buildAggregateField = function (field) {
1257
+ // Overridden in ConditionBuilder where alias tracking is available.
1258
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1259
+ AggregateBuilder.prototype.assertValidIdentifier = function (_identifier, _context) {
1260
+ // no-op placeholder for subclasses that do not implement alias validation
1261
+ };
1262
+ AggregateBuilder.prototype.buildAggregateField = function (field, params) {
1255
1263
  var _this = this;
1264
+ var _a, _b;
1256
1265
  if (typeof field === 'string') {
1266
+ this.assertValidIdentifier(field, 'SELECT field');
1257
1267
  return field;
1258
1268
  }
1259
1269
  if (!Array.isArray(field) || field.length === 0) {
1260
1270
  throw new Error('Invalid SELECT field entry');
1261
1271
  }
1272
+ // If the array represents a tuple/literal list (e.g., [lng, lat]) rather than a
1273
+ // function call like [FN, ...args], serialize the list as a comma-separated
1274
+ // literal sequence so parent calls (like ORDER BY FN(<here>)) can embed it.
1275
+ var isNumericString = function (s) { return /^-?\d+(?:\.\d+)?$/.test(String(s).trim()); };
1276
+ if (typeof field[0] !== 'string' || isNumericString(field[0])) {
1277
+ return field
1278
+ .map(function (arg) {
1279
+ if (Array.isArray(arg))
1280
+ return _this.buildAggregateField(arg, params);
1281
+ return String(arg);
1282
+ })
1283
+ .join(', ');
1284
+ }
1262
1285
  var fn = field[0], args = field.slice(1);
1263
1286
  var alias;
1264
1287
  if (args.length >= 2 && String(args[args.length - 2]).toUpperCase() === 'AS') {
@@ -1266,8 +1289,50 @@ var AggregateBuilder = /** @class */ (function (_super) {
1266
1289
  args.pop();
1267
1290
  }
1268
1291
  var F = String(fn).toUpperCase();
1292
+ // Parameter placeholder helper: [C6C.PARAM, value]
1293
+ if (F === C6C.PARAM) {
1294
+ if (!params) {
1295
+ throw new Error('PARAM requires parameter tracking.');
1296
+ }
1297
+ var value = args[0];
1298
+ // Use empty column context; ORDER/SELECT literals have no column typing.
1299
+ // @ts-ignore addParam is provided by ConditionBuilder in our hierarchy.
1300
+ return this.addParam(params, '', value);
1301
+ }
1302
+ if (F === C6C.SUBSELECT) {
1303
+ if (!params) {
1304
+ throw new Error('Scalar subselects in SELECT require parameter tracking.');
1305
+ }
1306
+ var subRequest = args[0];
1307
+ var subSql = (_b = (_a = this).buildScalarSubSelect) === null || _b === void 0 ? void 0 : _b.call(_a, subRequest, params);
1308
+ if (!subSql) {
1309
+ throw new Error('Failed to build scalar subselect.');
1310
+ }
1311
+ var expr_1 = subSql;
1312
+ if (alias) {
1313
+ this.selectAliases.add(alias);
1314
+ expr_1 += " AS ".concat(alias);
1315
+ }
1316
+ this.config.verbose && console.log("[SELECT] ".concat(expr_1));
1317
+ return expr_1;
1318
+ }
1319
+ var identifierPathRegex = /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/;
1269
1320
  var argList = args
1270
- .map(function (arg) { return Array.isArray(arg) ? _this.buildAggregateField(arg) : arg; })
1321
+ .map(function (arg) {
1322
+ if (Array.isArray(arg))
1323
+ return _this.buildAggregateField(arg, params);
1324
+ if (typeof arg === 'string') {
1325
+ if (identifierPathRegex.test(arg)) {
1326
+ _this.assertValidIdentifier(arg, 'SELECT expression');
1327
+ return arg;
1328
+ }
1329
+ // Treat numeric-looking strings as literals, not identifier paths
1330
+ if (isNumericString(arg))
1331
+ return arg;
1332
+ return arg;
1333
+ }
1334
+ return String(arg);
1335
+ })
1271
1336
  .join(', ');
1272
1337
  var expr;
1273
1338
  if (F === 'DISTINCT') {
@@ -1286,11 +1351,91 @@ var AggregateBuilder = /** @class */ (function (_super) {
1286
1351
  return AggregateBuilder;
1287
1352
  }(Executor));
1288
1353
 
1354
+ // Alias a table name with a given alias
1355
+ var DERIVED_TABLE_PREFIX = '__c6DerivedTable__';
1356
+ var DERIVED_ID_SYMBOL = Symbol('c6DerivedTableId');
1357
+ var derivedTableLookup = new Map();
1358
+ var derivedTableReverseLookup = new WeakMap();
1359
+ var derivedTableCounter = 0;
1360
+ var isDerivedTableKey = function (key) {
1361
+ return typeof key === 'string' && key.startsWith(DERIVED_TABLE_PREFIX);
1362
+ };
1363
+ var resolveDerivedTable = function (key) {
1364
+ return derivedTableLookup.get(key);
1365
+ };
1366
+ var derivedTable = function (spec) {
1367
+ if (!spec || typeof spec !== 'object') {
1368
+ throw new Error('Derived table definition must be an object.');
1369
+ }
1370
+ var aliasRaw = spec[C6C.AS];
1371
+ if (typeof aliasRaw !== 'string' || aliasRaw.trim() === '') {
1372
+ throw new Error('Derived tables require a non-empty alias via C6C.AS.');
1373
+ }
1374
+ if (!spec[C6C.SUBSELECT] || typeof spec[C6C.SUBSELECT] !== 'object') {
1375
+ throw new Error('Derived tables require a nested SELECT payload under C6C.SUBSELECT.');
1376
+ }
1377
+ var id = derivedTableReverseLookup.get(spec);
1378
+ if (!id) {
1379
+ id = "".concat(DERIVED_TABLE_PREFIX).concat(++derivedTableCounter);
1380
+ derivedTableReverseLookup.set(spec, id);
1381
+ derivedTableLookup.set(id, spec);
1382
+ Object.defineProperty(spec, DERIVED_ID_SYMBOL, {
1383
+ value: id,
1384
+ configurable: false,
1385
+ enumerable: false,
1386
+ writable: false
1387
+ });
1388
+ }
1389
+ var alias = aliasRaw.trim();
1390
+ derivedTableLookup.set(id, spec);
1391
+ Object.defineProperty(spec, 'toString', {
1392
+ value: function () { return "".concat(id, " ").concat(alias); },
1393
+ configurable: true,
1394
+ enumerable: false,
1395
+ writable: true
1396
+ });
1397
+ return spec;
1398
+ };
1399
+ var A = function (tableName, alias) {
1400
+ return "".concat(tableName, " ").concat(alias);
1401
+ };
1402
+ // Qualify a column constant (e.g. 'property_units.parcel_id') to an alias
1403
+ var F = function (qualifiedCol, alias) {
1404
+ return "".concat(alias, ".").concat(qualifiedCol.split('.').pop());
1405
+ };
1406
+ // Equal join condition using full-qualified column constants
1407
+ var fieldEq = function (leftCol, rightCol, leftAlias, rightAlias) {
1408
+ var _a;
1409
+ return (_a = {},
1410
+ _a[F(leftCol, leftAlias)] = F(rightCol, rightAlias),
1411
+ _a);
1412
+ };
1413
+ // ST_Distance_Sphere for aliased fields
1414
+ var distSphere = function (fromCol, toCol, fromAlias, toAlias) {
1415
+ return [C6C.ST_DISTANCE_SPHERE, F(fromCol, fromAlias), F(toCol, toAlias)];
1416
+ };
1417
+ // Build a bounding-box expression.
1418
+ //
1419
+ // Arguments must be provided in `(minLng, minLat, maxLng, maxLat)` order. The
1420
+ // helper does not attempt to swap or validate coordinates; if a minimum value
1421
+ // is greater than its corresponding maximum value, MySQL's `ST_MakeEnvelope`
1422
+ // returns `NULL`.
1423
+ var bbox = function (minLng, minLat, maxLng, maxLat) {
1424
+ return [C6C.ST_SRID, [C6C.ST_MAKEENVELOPE,
1425
+ [C6C.ST_POINT, minLng, minLat],
1426
+ [C6C.ST_POINT, maxLng, maxLat]], 4326];
1427
+ };
1428
+ // ST_Contains for map envelope/shape queries
1429
+ var stContains = function (envelope, shape) {
1430
+ return [C6C.ST_CONTAINS, envelope, shape];
1431
+ };
1432
+
1289
1433
  var ConditionBuilder = /** @class */ (function (_super) {
1290
1434
  __extends(ConditionBuilder, _super);
1291
1435
  function ConditionBuilder() {
1292
1436
  var _this = _super !== null && _super.apply(this, arguments) || this;
1293
1437
  _this.aliasMap = {};
1438
+ _this.derivedAliases = new Set();
1294
1439
  _this.OPERATORS = new Set([
1295
1440
  C6C.EQUAL, C6C.NOT_EQUAL, C6C.LESS_THAN, C6C.LESS_THAN_OR_EQUAL_TO,
1296
1441
  C6C.GREATER_THAN, C6C.GREATER_THAN_OR_EQUAL_TO,
@@ -1315,21 +1460,43 @@ var ConditionBuilder = /** @class */ (function (_super) {
1315
1460
  ConditionBuilder.prototype.initAlias = function (baseTable, joins) {
1316
1461
  var _a;
1317
1462
  this.aliasMap = (_a = {}, _a[baseTable] = baseTable, _a);
1463
+ this.derivedAliases = new Set();
1318
1464
  if (!joins)
1319
1465
  return;
1320
1466
  for (var joinType in joins) {
1321
1467
  for (var raw in joins[joinType]) {
1322
- var _b = raw.split(' '), table = _b[0], alias = _b[1];
1323
- this.aliasMap[alias || table] = table;
1468
+ var _b = raw.trim().split(/\s+/, 2), table = _b[0], alias = _b[1];
1469
+ if (!table)
1470
+ continue;
1471
+ this.registerAlias(alias || table, table);
1324
1472
  }
1325
1473
  }
1326
1474
  };
1475
+ ConditionBuilder.prototype.registerAlias = function (alias, table) {
1476
+ this.aliasMap[alias] = table;
1477
+ if (isDerivedTableKey(table)) {
1478
+ this.derivedAliases.add(alias);
1479
+ }
1480
+ };
1481
+ ConditionBuilder.prototype.assertValidIdentifier = function (identifier, context) {
1482
+ if (typeof identifier !== 'string')
1483
+ return;
1484
+ if (!identifier.includes('.'))
1485
+ return;
1486
+ var alias = identifier.split('.', 2)[0];
1487
+ if (!(alias in this.aliasMap)) {
1488
+ throw new Error("Unknown table or alias '".concat(alias, "' referenced in ").concat(context, ": '").concat(identifier, "'."));
1489
+ }
1490
+ };
1327
1491
  ConditionBuilder.prototype.isColumnRef = function (ref) {
1328
1492
  var _a, _b;
1329
1493
  if (typeof ref !== 'string' || !ref.includes('.'))
1330
1494
  return false;
1331
1495
  var _c = ref.split('.', 2), prefix = _c[0], column = _c[1];
1332
1496
  var tableName = this.aliasMap[prefix] || prefix;
1497
+ if (isDerivedTableKey(tableName) || this.derivedAliases.has(prefix)) {
1498
+ return true;
1499
+ }
1333
1500
  var table = (_b = (_a = this.config.C6) === null || _a === void 0 ? void 0 : _a.TABLES) === null || _b === void 0 ? void 0 : _b[tableName];
1334
1501
  if (!table)
1335
1502
  return false;
@@ -1358,6 +1525,9 @@ var ConditionBuilder = /** @class */ (function (_super) {
1358
1525
  }
1359
1526
  var _e = val.split('.'), prefix = _e[0], column = _e[1];
1360
1527
  var tableName = (_b = this.aliasMap[prefix]) !== null && _b !== void 0 ? _b : prefix;
1528
+ if (isDerivedTableKey(tableName) || this.derivedAliases.has(prefix)) {
1529
+ return true;
1530
+ }
1361
1531
  var table = (_d = (_c = this.config.C6) === null || _c === void 0 ? void 0 : _c.TABLES) === null || _d === void 0 ? void 0 : _d[tableName];
1362
1532
  if (!table || !table.COLUMNS)
1363
1533
  return false;
@@ -1400,6 +1570,26 @@ var ConditionBuilder = /** @class */ (function (_super) {
1400
1570
  // Normalize common variants
1401
1571
  var valueNorm = (value === C6C.NULL) ? null : value;
1402
1572
  var displayOp = typeof op === 'string' ? op.replace('_', ' ') : op;
1573
+ var extractSubSelect = function (input) {
1574
+ if (Array.isArray(input) && input.length >= 2 && input[0] === C6C.SUBSELECT) {
1575
+ return input[1];
1576
+ }
1577
+ if (input && typeof input === 'object' && C6C.SUBSELECT in input) {
1578
+ return input[C6C.SUBSELECT];
1579
+ }
1580
+ return undefined;
1581
+ };
1582
+ var rightSubSelectPayload = extractSubSelect(valueNorm);
1583
+ var buildSubSelect = function (payload) {
1584
+ if (!payload)
1585
+ return undefined;
1586
+ var builder = _this.buildScalarSubSelect;
1587
+ if (typeof builder !== 'function') {
1588
+ throw new Error('Scalar subselect handling requires JoinBuilder context.');
1589
+ }
1590
+ return builder.call(_this, payload, params);
1591
+ };
1592
+ var rightSubSelectSql = buildSubSelect(rightSubSelectPayload);
1403
1593
  // Support function-based expressions like [C6C.ST_DISTANCE_SPHERE, col1, col2]
1404
1594
  if (typeof column === 'string' &&
1405
1595
  _this.OPERATORS.has(column) &&
@@ -1426,7 +1616,7 @@ var ConditionBuilder = /** @class */ (function (_super) {
1426
1616
  var leftIsCol = _this.isColumnRef(column);
1427
1617
  var leftIsRef = _this.isTableReference(column);
1428
1618
  var rightIsCol = typeof value === 'string' && _this.isColumnRef(value);
1429
- if (!leftIsCol && !leftIsRef && !rightIsCol) {
1619
+ if (!leftIsCol && !leftIsRef && !rightIsCol && !rightSubSelectSql) {
1430
1620
  throw new Error("Potential SQL injection detected: '".concat(column, " ").concat(op, " ").concat(value, "'"));
1431
1621
  }
1432
1622
  _this.validateOperator(op);
@@ -1459,6 +1649,13 @@ var ConditionBuilder = /** @class */ (function (_super) {
1459
1649
  return matchClause;
1460
1650
  }
1461
1651
  if ((op === C6C.IN || op === C6C.NOT_IN) && Array.isArray(value)) {
1652
+ if (rightSubSelectSql) {
1653
+ if (!leftIsRef) {
1654
+ throw new Error("IN operator requires a table reference as the left operand. Column '".concat(column, "' is not a valid table reference."));
1655
+ }
1656
+ var normalized_1 = op.replace('_', ' ');
1657
+ return "( ".concat(column, " ").concat(normalized_1, " ").concat(rightSubSelectSql, " )");
1658
+ }
1462
1659
  var placeholders = value.map(function (v) {
1463
1660
  return _this.isColumnRef(v) ? v : _this.addParam(params, column, v);
1464
1661
  }).join(', ');
@@ -1478,10 +1675,13 @@ var ConditionBuilder = /** @class */ (function (_super) {
1478
1675
  }
1479
1676
  return "(".concat(column, ") ").concat(op.replace('_', ' '), " ").concat(_this.addParam(params, column, start), " AND ").concat(_this.addParam(params, column, end));
1480
1677
  }
1481
- var rightIsRef = _this.isTableReference(value);
1678
+ var rightIsRef = rightSubSelectSql ? false : _this.isTableReference(value);
1482
1679
  if (leftIsRef && rightIsRef) {
1483
1680
  return "(".concat(column, ") ").concat(displayOp, " ").concat(value);
1484
1681
  }
1682
+ if (leftIsRef && rightSubSelectSql) {
1683
+ return "(".concat(column, ") ").concat(displayOp, " ").concat(rightSubSelectSql);
1684
+ }
1485
1685
  if (leftIsRef && !rightIsRef) {
1486
1686
  return "(".concat(column, ") ").concat(displayOp, " ").concat(_this.addParam(params, column, valueNorm));
1487
1687
  }
@@ -1577,85 +1777,117 @@ var JoinBuilder = /** @class */ (function (_super) {
1577
1777
  function JoinBuilder() {
1578
1778
  return _super !== null && _super.apply(this, arguments) || this;
1579
1779
  }
1780
+ JoinBuilder.prototype.createSelectBuilder = function (_request) {
1781
+ throw new Error('Subclasses must implement createSelectBuilder to support derived table serialization.');
1782
+ };
1580
1783
  JoinBuilder.prototype.buildJoinClauses = function (joinArgs, params) {
1581
1784
  var sql = '';
1582
- for (var joinType in joinArgs) {
1785
+ var _loop_1 = function (joinType) {
1583
1786
  var joinKind = joinType.replace('_', ' ').toUpperCase();
1584
- for (var raw in joinArgs[joinType]) {
1585
- var _a = raw.split(' '), table = _a[0], alias = _a[1];
1586
- this.aliasMap[alias || table] = table;
1587
- var onClause = this.buildBooleanJoinedConditions(joinArgs[joinType][raw], true, params);
1588
- var joinSql = alias ? "`".concat(table, "` AS `").concat(alias, "`") : "`".concat(table, "`");
1589
- sql += " ".concat(joinKind, " JOIN ").concat(joinSql, " ON ").concat(onClause);
1787
+ var entries = [];
1788
+ var joinSection = joinArgs[joinType];
1789
+ if (joinSection instanceof Map) {
1790
+ joinSection.forEach(function (value, key) {
1791
+ entries.push([key, value]);
1792
+ });
1590
1793
  }
1794
+ else {
1795
+ for (var raw in joinSection) {
1796
+ entries.push([raw, joinSection[raw]]);
1797
+ }
1798
+ }
1799
+ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
1800
+ var _a = entries_1[_i], rawKey = _a[0], conditions = _a[1];
1801
+ var raw = typeof rawKey === 'string' ? rawKey : String(rawKey);
1802
+ var _b = raw.trim().split(/\s+/, 2), table = _b[0], aliasCandidate = _b[1];
1803
+ if (!table)
1804
+ continue;
1805
+ if (isDerivedTableKey(table)) {
1806
+ var derived = resolveDerivedTable(table);
1807
+ if (!derived) {
1808
+ throw new Error("Derived table '".concat(table, "' was not registered. Wrap the object with derivedTable(...) before using it in JOIN."));
1809
+ }
1810
+ var configuredAliasRaw = derived[C6C.AS];
1811
+ var configuredAlias = typeof configuredAliasRaw === 'string' ? configuredAliasRaw.trim() : '';
1812
+ var alias = (aliasCandidate !== null && aliasCandidate !== void 0 ? aliasCandidate : configuredAlias).trim();
1813
+ if (!alias) {
1814
+ throw new Error('Derived tables require an alias via C6C.AS.');
1815
+ }
1816
+ this_1.registerAlias(alias, table);
1817
+ var subRequest = derived[C6C.SUBSELECT];
1818
+ if (!subRequest || typeof subRequest !== 'object') {
1819
+ throw new Error('Derived tables must include a C6C.SUBSELECT payload.');
1820
+ }
1821
+ var fromTable = subRequest[C6C.FROM];
1822
+ if (typeof fromTable !== 'string' || fromTable.trim() === '') {
1823
+ throw new Error('Derived table subselects require a base table defined with C6C.FROM.');
1824
+ }
1825
+ var subBuilder = this_1.createSelectBuilder(subRequest);
1826
+ var _c = subBuilder.build(fromTable, true), subSql = _c.sql, subParams = _c.params;
1827
+ var normalizedSql = this_1.integrateSubSelectParams(subSql, subParams, params);
1828
+ var formatted = normalizedSql.trim().split('\n').map(function (line) { return " ".concat(line); }).join('\n');
1829
+ var joinSql = "(\n".concat(formatted, "\n) AS `").concat(alias, "`");
1830
+ var onClause = this_1.buildBooleanJoinedConditions(conditions, true, params);
1831
+ sql += " ".concat(joinKind, " JOIN ").concat(joinSql);
1832
+ if (onClause) {
1833
+ sql += " ON ".concat(onClause);
1834
+ }
1835
+ }
1836
+ else {
1837
+ var alias = aliasCandidate;
1838
+ if (alias) {
1839
+ this_1.registerAlias(alias, table);
1840
+ }
1841
+ var joinSql = alias ? "`".concat(table, "` AS `").concat(alias, "`") : "`".concat(table, "`");
1842
+ var onClause = this_1.buildBooleanJoinedConditions(conditions, true, params);
1843
+ sql += " ".concat(joinKind, " JOIN ").concat(joinSql);
1844
+ if (onClause) {
1845
+ sql += " ON ".concat(onClause);
1846
+ }
1847
+ }
1848
+ }
1849
+ };
1850
+ var this_1 = this;
1851
+ for (var joinType in joinArgs) {
1852
+ _loop_1(joinType);
1591
1853
  }
1592
1854
  this.config.verbose && console.log("[JOIN] ".concat(sql.trim()));
1593
1855
  return sql;
1594
1856
  };
1595
- return JoinBuilder;
1596
- }(ConditionBuilder));
1597
-
1598
- var DeleteQueryBuilder = /** @class */ (function (_super) {
1599
- __extends(DeleteQueryBuilder, _super);
1600
- function DeleteQueryBuilder() {
1601
- return _super !== null && _super.apply(this, arguments) || this;
1602
- }
1603
- DeleteQueryBuilder.prototype.build = function (table) {
1604
- this.aliasMap = {};
1605
- var params = this.useNamedParams ? {} : [];
1606
- this.initAlias(table, this.request.JOIN);
1607
- var sql = "DELETE `".concat(table, "` FROM `").concat(table, "`");
1608
- if (this.request.JOIN) {
1609
- sql += this.buildJoinClauses(this.request.JOIN, params);
1610
- }
1611
- if (this.request.WHERE) {
1612
- sql += this.buildWhereClause(this.request.WHERE, params);
1613
- }
1614
- return { sql: sql, params: params };
1615
- };
1616
- return DeleteQueryBuilder;
1617
- }(JoinBuilder));
1618
-
1619
- var PostQueryBuilder = /** @class */ (function (_super) {
1620
- __extends(PostQueryBuilder, _super);
1621
- function PostQueryBuilder() {
1622
- return _super !== null && _super.apply(this, arguments) || this;
1623
- }
1624
- PostQueryBuilder.prototype.trimTablePrefix = function (table, column) {
1625
- if (!column.includes('.'))
1626
- return column;
1627
- var _a = column.split('.', 2), prefix = _a[0], col = _a[1];
1628
- if (prefix !== table) {
1629
- throw new Error("Invalid prefixed column: '".concat(column, "'. Expected prefix '").concat(table, ".'"));
1857
+ JoinBuilder.prototype.integrateSubSelectParams = function (subSql, subParams, target) {
1858
+ var _a;
1859
+ if (!subParams)
1860
+ return subSql;
1861
+ if (this.useNamedParams) {
1862
+ var normalized = subSql;
1863
+ var extras = subParams;
1864
+ for (var _i = 0, _b = Object.keys(extras); _i < _b.length; _i++) {
1865
+ var key = _b[_i];
1866
+ var placeholder = this.addParam(target, '', extras[key]);
1867
+ var original = ":".concat(key);
1868
+ if (original !== placeholder) {
1869
+ normalized = normalized.split(original).join(placeholder);
1870
+ }
1871
+ }
1872
+ return normalized;
1630
1873
  }
1631
- return col;
1874
+ (_a = target).push.apply(_a, subParams);
1875
+ return subSql;
1632
1876
  };
1633
- PostQueryBuilder.prototype.build = function (table) {
1634
- var _this = this;
1635
- this.aliasMap = {};
1636
- var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
1637
- var body = verb in this.request ? this.request[verb] : this.request;
1638
- var keys = Object.keys(body);
1639
- var params = [];
1640
- var placeholders = [];
1641
- for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
1642
- var key = keys_1[_i];
1643
- var value = body[key];
1644
- var placeholder = this.addParam(params, key, value);
1645
- placeholders.push(placeholder);
1877
+ JoinBuilder.prototype.buildScalarSubSelect = function (subRequest, params) {
1878
+ if (!subRequest || typeof subRequest !== 'object') {
1879
+ throw new Error('Scalar subselect requires a C6C.SUBSELECT object payload.');
1646
1880
  }
1647
- 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 )");
1648
- if (C6C.UPDATE in this.request) {
1649
- var updateData = this.request[C6C.UPDATE];
1650
- if (!Array.isArray(updateData)) {
1651
- throw new Error("Update data must be an array of keys to update, got: ".concat(JSON.stringify(updateData)));
1652
- }
1653
- var updateClause = updateData.map(function (k) { return "`".concat(k, "` = VALUES(`").concat(k, "`)"); }).join(', ');
1654
- sql += " ON DUPLICATE KEY UPDATE ".concat(updateClause);
1881
+ var fromTable = subRequest[C6C.FROM];
1882
+ if (typeof fromTable !== 'string' || fromTable.trim() === '') {
1883
+ throw new Error('Scalar subselects require a base table specified with C6C.FROM.');
1655
1884
  }
1656
- return { sql: sql, params: params };
1885
+ var subBuilder = this.createSelectBuilder(subRequest);
1886
+ var _a = subBuilder.build(fromTable, true), subSql = _a.sql, subParams = _a.params;
1887
+ var normalized = this.integrateSubSelectParams(subSql, subParams, params).trim();
1888
+ return "(".concat(normalized, ")");
1657
1889
  };
1658
- return PostQueryBuilder;
1890
+ return JoinBuilder;
1659
1891
  }(ConditionBuilder));
1660
1892
 
1661
1893
  var PaginationBuilder = /** @class */ (function (_super) {
@@ -1676,18 +1908,36 @@ var PaginationBuilder = /** @class */ (function (_super) {
1676
1908
  * }
1677
1909
  * ```
1678
1910
  */
1679
- PaginationBuilder.prototype.buildPaginationClause = function (pagination) {
1911
+ PaginationBuilder.prototype.buildPaginationClause = function (pagination, params) {
1680
1912
  var _this = this;
1681
1913
  var sql = "";
1682
1914
  /* -------- ORDER BY -------- */
1683
1915
  if (pagination === null || pagination === void 0 ? void 0 : pagination[C6Constants.ORDER]) {
1684
1916
  var orderParts = [];
1685
- for (var _i = 0, _a = Object.entries(pagination[C6Constants.ORDER]); _i < _a.length; _i++) {
1686
- var _b = _a[_i], key = _b[0], val = _b[1];
1917
+ var _loop_1 = function (key, val) {
1918
+ if (typeof key === 'string' && key.includes('.')) {
1919
+ this_1.assertValidIdentifier(key, 'ORDER BY');
1920
+ }
1687
1921
  // FUNCTION CALL: val is an array of args
1688
1922
  if (Array.isArray(val)) {
1923
+ var identifierPathRegex_1 = /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/;
1924
+ var isNumericString_1 = function (s) { return /^-?\d+(?:\.\d+)?$/.test(s.trim()); };
1689
1925
  var args = val
1690
- .map(function (arg) { return Array.isArray(arg) ? _this.buildAggregateField(arg) : String(arg); })
1926
+ .map(function (arg) {
1927
+ if (Array.isArray(arg))
1928
+ return _this.buildAggregateField(arg, params);
1929
+ if (typeof arg === 'string') {
1930
+ if (identifierPathRegex_1.test(arg)) {
1931
+ _this.assertValidIdentifier(arg, 'ORDER BY argument');
1932
+ return arg;
1933
+ }
1934
+ // numeric-looking strings should be treated as literals
1935
+ if (isNumericString_1(arg))
1936
+ return arg;
1937
+ return arg;
1938
+ }
1939
+ return String(arg);
1940
+ })
1691
1941
  .join(", ");
1692
1942
  orderParts.push("".concat(key, "(").concat(args, ")"));
1693
1943
  }
@@ -1695,6 +1945,11 @@ var PaginationBuilder = /** @class */ (function (_super) {
1695
1945
  else {
1696
1946
  orderParts.push("".concat(key, " ").concat(String(val).toUpperCase()));
1697
1947
  }
1948
+ };
1949
+ var this_1 = this;
1950
+ for (var _i = 0, _a = Object.entries(pagination[C6Constants.ORDER]); _i < _a.length; _i++) {
1951
+ var _b = _a[_i], key = _b[0], val = _b[1];
1952
+ _loop_1(key, val);
1698
1953
  }
1699
1954
  if (orderParts.length)
1700
1955
  sql += " ORDER BY ".concat(orderParts.join(", "));
@@ -1724,6 +1979,9 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
1724
1979
  function SelectQueryBuilder() {
1725
1980
  return _super !== null && _super.apply(this, arguments) || this;
1726
1981
  }
1982
+ SelectQueryBuilder.prototype.createSelectBuilder = function (request) {
1983
+ return new SelectQueryBuilder(this.config, request, this.useNamedParams);
1984
+ };
1727
1985
  SelectQueryBuilder.prototype.build = function (table, isSubSelect) {
1728
1986
  var _this = this;
1729
1987
  var _a;
@@ -1738,7 +1996,7 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
1738
1996
  var params = this.useNamedParams ? {} : [];
1739
1997
  var selectList = (_a = args.SELECT) !== null && _a !== void 0 ? _a : ['*'];
1740
1998
  var selectFields = selectList
1741
- .map(function (f) { return _this.buildAggregateField(f); })
1999
+ .map(function (f) { return _this.buildAggregateField(f, params); })
1742
2000
  .join(', ');
1743
2001
  var sql = "SELECT ".concat(selectFields, " FROM `").concat(table, "`");
1744
2002
  if (args.JOIN) {
@@ -1757,7 +2015,7 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
1757
2015
  sql += " HAVING ".concat(this.buildBooleanJoinedConditions(args.HAVING, true, params));
1758
2016
  }
1759
2017
  if (args.PAGINATION) {
1760
- sql += this.buildPaginationClause(args.PAGINATION);
2018
+ sql += this.buildPaginationClause(args.PAGINATION, params);
1761
2019
  }
1762
2020
  else if (!isSubSelect) {
1763
2021
  sql += " LIMIT 100";
@@ -1768,11 +2026,80 @@ var SelectQueryBuilder = /** @class */ (function (_super) {
1768
2026
  return SelectQueryBuilder;
1769
2027
  }(PaginationBuilder));
1770
2028
 
2029
+ var DeleteQueryBuilder = /** @class */ (function (_super) {
2030
+ __extends(DeleteQueryBuilder, _super);
2031
+ function DeleteQueryBuilder() {
2032
+ return _super !== null && _super.apply(this, arguments) || this;
2033
+ }
2034
+ DeleteQueryBuilder.prototype.createSelectBuilder = function (request) {
2035
+ return new SelectQueryBuilder(this.config, request, this.useNamedParams);
2036
+ };
2037
+ DeleteQueryBuilder.prototype.build = function (table) {
2038
+ this.aliasMap = {};
2039
+ var params = this.useNamedParams ? {} : [];
2040
+ this.initAlias(table, this.request.JOIN);
2041
+ var sql = "DELETE `".concat(table, "` FROM `").concat(table, "`");
2042
+ if (this.request.JOIN) {
2043
+ sql += this.buildJoinClauses(this.request.JOIN, params);
2044
+ }
2045
+ if (this.request.WHERE) {
2046
+ sql += this.buildWhereClause(this.request.WHERE, params);
2047
+ }
2048
+ return { sql: sql, params: params };
2049
+ };
2050
+ return DeleteQueryBuilder;
2051
+ }(JoinBuilder));
2052
+
2053
+ var PostQueryBuilder = /** @class */ (function (_super) {
2054
+ __extends(PostQueryBuilder, _super);
2055
+ function PostQueryBuilder() {
2056
+ return _super !== null && _super.apply(this, arguments) || this;
2057
+ }
2058
+ PostQueryBuilder.prototype.trimTablePrefix = function (table, column) {
2059
+ if (!column.includes('.'))
2060
+ return column;
2061
+ var _a = column.split('.', 2), prefix = _a[0], col = _a[1];
2062
+ if (prefix !== table) {
2063
+ throw new Error("Invalid prefixed column: '".concat(column, "'. Expected prefix '").concat(table, ".'"));
2064
+ }
2065
+ return col;
2066
+ };
2067
+ PostQueryBuilder.prototype.build = function (table) {
2068
+ var _this = this;
2069
+ this.aliasMap = {};
2070
+ var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
2071
+ var body = verb in this.request ? this.request[verb] : this.request;
2072
+ var keys = Object.keys(body);
2073
+ var params = [];
2074
+ var placeholders = [];
2075
+ for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
2076
+ var key = keys_1[_i];
2077
+ var value = body[key];
2078
+ var placeholder = this.addParam(params, key, value);
2079
+ placeholders.push(placeholder);
2080
+ }
2081
+ 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 )");
2082
+ if (C6C.UPDATE in this.request) {
2083
+ var updateData = this.request[C6C.UPDATE];
2084
+ if (!Array.isArray(updateData)) {
2085
+ throw new Error("Update data must be an array of keys to update, got: ".concat(JSON.stringify(updateData)));
2086
+ }
2087
+ var updateClause = updateData.map(function (k) { return "`".concat(k, "` = VALUES(`").concat(k, "`)"); }).join(', ');
2088
+ sql += " ON DUPLICATE KEY UPDATE ".concat(updateClause);
2089
+ }
2090
+ return { sql: sql, params: params };
2091
+ };
2092
+ return PostQueryBuilder;
2093
+ }(ConditionBuilder));
2094
+
1771
2095
  var UpdateQueryBuilder = /** @class */ (function (_super) {
1772
2096
  __extends(UpdateQueryBuilder, _super);
1773
2097
  function UpdateQueryBuilder() {
1774
2098
  return _super !== null && _super.apply(this, arguments) || this;
1775
2099
  }
2100
+ UpdateQueryBuilder.prototype.createSelectBuilder = function (request) {
2101
+ return new SelectQueryBuilder(this.config, request, this.useNamedParams);
2102
+ };
1776
2103
  UpdateQueryBuilder.prototype.trimTablePrefix = function (table, column) {
1777
2104
  if (!column.includes('.'))
1778
2105
  return column;
@@ -1807,7 +2134,7 @@ var UpdateQueryBuilder = /** @class */ (function (_super) {
1807
2134
  sql += this.buildWhereClause(args.WHERE, params);
1808
2135
  }
1809
2136
  if (args.PAGINATION) {
1810
- sql += this.buildPaginationClause(args.PAGINATION);
2137
+ sql += this.buildPaginationClause(args.PAGINATION, params);
1811
2138
  }
1812
2139
  return { sql: sql, params: params };
1813
2140
  };
@@ -2203,41 +2530,6 @@ function ExpressHandler(_a) {
2203
2530
  }); };
2204
2531
  }
2205
2532
 
2206
- // Alias a table name with a given alias
2207
- var A = function (tableName, alias) {
2208
- return "".concat(tableName, " ").concat(alias);
2209
- };
2210
- // Qualify a column constant (e.g. 'property_units.parcel_id') to an alias
2211
- var F = function (qualifiedCol, alias) {
2212
- return "".concat(alias, ".").concat(qualifiedCol.split('.').pop());
2213
- };
2214
- // Equal join condition using full-qualified column constants
2215
- var fieldEq = function (leftCol, rightCol, leftAlias, rightAlias) {
2216
- var _a;
2217
- return (_a = {},
2218
- _a[F(leftCol, leftAlias)] = F(rightCol, rightAlias),
2219
- _a);
2220
- };
2221
- // ST_Distance_Sphere for aliased fields
2222
- var distSphere = function (fromCol, toCol, fromAlias, toAlias) {
2223
- return [C6C.ST_DISTANCE_SPHERE, F(fromCol, fromAlias), F(toCol, toAlias)];
2224
- };
2225
- // Build a bounding-box expression.
2226
- //
2227
- // Arguments must be provided in `(minLng, minLat, maxLng, maxLat)` order. The
2228
- // helper does not attempt to swap or validate coordinates; if a minimum value
2229
- // is greater than its corresponding maximum value, MySQL's `ST_MakeEnvelope`
2230
- // returns `NULL`.
2231
- var bbox = function (minLng, minLat, maxLng, maxLat) {
2232
- return [C6C.ST_SRID, [C6C.ST_MAKEENVELOPE,
2233
- [C6C.ST_POINT, minLng, minLat],
2234
- [C6C.ST_POINT, maxLng, maxLat]], 4326];
2235
- };
2236
- // ST_Contains for map envelope/shape queries
2237
- var stContains = function (envelope, shape) {
2238
- return [C6C.ST_CONTAINS, envelope, shape];
2239
- };
2240
-
2241
2533
  function determineRuntimeJsType(mysqlType) {
2242
2534
  var base = mysqlType.toLowerCase().split('(')[0];
2243
2535
  if ([
@@ -2337,5 +2629,5 @@ function isVerbose () {
2337
2629
  return ['true', '1', 'yes', 'on'].includes(envVerbose.toLowerCase());
2338
2630
  }
2339
2631
 
2340
- export { A, AggregateBuilder, C6C, C6Constants, ConditionBuilder, DELETE, DeleteQueryBuilder, Executor, ExpressHandler, F, GET, HttpExecutor, JoinBuilder, POST, PUT, PaginationBuilder, PostQueryBuilder, SelectQueryBuilder, SqlExecutor, TestRestfulResponse, UpdateQueryBuilder, apiRequestCache, axiosInstance, bbox, carbonNodeQsStringify, checkAllRequestsComplete, checkCache, clearCache, convertForRequestBody, convertHexIfBinary, determineRuntimeJsType, distSphere, eFetchDependencies, error, fieldEq, getEnvVar, getPrimaryKeyTypes, group, info, isLocal, isNode, isTest, isVerbose, normalizeSingularRequest, onError, onSuccess, removeInvalidKeys, removePrefixIfExists, restOrm, restRequest, sortAndSerializeQueryObject, stContains, timeout, toastOptions, toastOptionsDevs, userCustomClearCache, warn };
2632
+ export { A, AggregateBuilder, C6C, C6Constants, ConditionBuilder, DELETE, DeleteQueryBuilder, Executor, ExpressHandler, F, GET, HttpExecutor, JoinBuilder, POST, PUT, PaginationBuilder, PostQueryBuilder, SelectQueryBuilder, SqlExecutor, TestRestfulResponse, UpdateQueryBuilder, apiRequestCache, axiosInstance, bbox, carbonNodeQsStringify, checkAllRequestsComplete, checkCache, clearCache, convertForRequestBody, convertHexIfBinary, derivedTable, determineRuntimeJsType, distSphere, eFetchDependencies, error, fieldEq, getEnvVar, getPrimaryKeyTypes, group, info, isDerivedTableKey, isLocal, isNode, isTest, isVerbose, normalizeSingularRequest, onError, onSuccess, removeInvalidKeys, removePrefixIfExists, resolveDerivedTable, restOrm, restRequest, sortAndSerializeQueryObject, stContains, timeout, toastOptions, toastOptionsDevs, userCustomClearCache, warn };
2341
2633
  //# sourceMappingURL=index.esm.js.map