metal-orm 1.0.16 → 1.0.18

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 (64) hide show
  1. package/README.md +37 -40
  2. package/dist/decorators/index.cjs +344 -69
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +1 -1
  5. package/dist/decorators/index.d.ts +1 -1
  6. package/dist/decorators/index.js +344 -69
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +567 -181
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +66 -30
  11. package/dist/index.d.ts +66 -30
  12. package/dist/index.js +559 -181
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-BKZrMRCQ.d.cts → select-BuMpVcVt.d.cts} +265 -74
  15. package/dist/{select-BKZrMRCQ.d.ts → select-BuMpVcVt.d.ts} +265 -74
  16. package/package.json +5 -1
  17. package/src/codegen/naming-strategy.ts +15 -10
  18. package/src/core/ast/aggregate-functions.ts +50 -4
  19. package/src/core/ast/builders.ts +23 -3
  20. package/src/core/ast/expression-builders.ts +36 -16
  21. package/src/core/ast/expression-nodes.ts +17 -9
  22. package/src/core/ast/join-node.ts +5 -3
  23. package/src/core/ast/join.ts +16 -16
  24. package/src/core/ast/query.ts +44 -29
  25. package/src/core/ddl/dialects/mssql-schema-dialect.ts +18 -0
  26. package/src/core/ddl/dialects/mysql-schema-dialect.ts +11 -0
  27. package/src/core/ddl/dialects/postgres-schema-dialect.ts +9 -0
  28. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
  29. package/src/core/ddl/introspect/functions/postgres.ts +2 -6
  30. package/src/core/dialect/abstract.ts +12 -8
  31. package/src/core/dialect/base/sql-dialect.ts +58 -46
  32. package/src/core/dialect/mssql/functions.ts +24 -15
  33. package/src/core/dialect/mssql/index.ts +53 -28
  34. package/src/core/dialect/postgres/functions.ts +33 -24
  35. package/src/core/dialect/sqlite/functions.ts +19 -12
  36. package/src/core/dialect/sqlite/index.ts +22 -13
  37. package/src/core/functions/datetime.ts +2 -1
  38. package/src/core/functions/numeric.ts +2 -1
  39. package/src/core/functions/standard-strategy.ts +52 -12
  40. package/src/core/functions/text.ts +2 -1
  41. package/src/core/functions/types.ts +8 -8
  42. package/src/index.ts +5 -4
  43. package/src/orm/domain-event-bus.ts +43 -25
  44. package/src/orm/entity-meta.ts +40 -0
  45. package/src/orm/execution-context.ts +6 -0
  46. package/src/orm/hydration-context.ts +6 -4
  47. package/src/orm/orm-session.ts +35 -24
  48. package/src/orm/orm.ts +10 -10
  49. package/src/orm/query-logger.ts +15 -0
  50. package/src/orm/runtime-types.ts +60 -2
  51. package/src/orm/transaction-runner.ts +7 -0
  52. package/src/orm/unit-of-work.ts +1 -0
  53. package/src/query-builder/column-selector.ts +9 -7
  54. package/src/query-builder/insert-query-state.ts +13 -3
  55. package/src/query-builder/query-ast-service.ts +59 -38
  56. package/src/query-builder/relation-conditions.ts +38 -34
  57. package/src/query-builder/relation-manager.ts +8 -3
  58. package/src/query-builder/relation-service.ts +59 -46
  59. package/src/query-builder/select-helpers.ts +50 -0
  60. package/src/query-builder/select-query-state.ts +19 -7
  61. package/src/query-builder/select.ts +339 -167
  62. package/src/query-builder/update-query-state.ts +31 -9
  63. package/src/schema/column.ts +75 -39
  64. package/src/schema/types.ts +17 -6
@@ -298,6 +298,15 @@ var isWindowFunctionNode = (node) => node?.type === "WindowFunction";
298
298
  var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
299
299
 
300
300
  // src/core/ast/expression-builders.ts
301
+ var valueToOperand = (value) => {
302
+ if (isOperandNode(value)) {
303
+ return value;
304
+ }
305
+ return {
306
+ type: "Literal",
307
+ value
308
+ };
309
+ };
301
310
  var toNode = (col) => {
302
311
  if (isOperandNode(col)) return col;
303
312
  const def = col;
@@ -307,10 +316,10 @@ var toLiteralNode = (value) => ({
307
316
  type: "Literal",
308
317
  value
309
318
  });
319
+ var isLiteralValue = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
310
320
  var toOperand = (val) => {
311
- if (val === null) return { type: "Literal", value: null };
312
- if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
313
- return { type: "Literal", value: val };
321
+ if (isLiteralValue(val)) {
322
+ return valueToOperand(val);
314
323
  }
315
324
  return toNode(val);
316
325
  };
@@ -351,6 +360,24 @@ var notExists = (subquery) => ({
351
360
  subquery
352
361
  });
353
362
 
363
+ // src/core/sql/sql.ts
364
+ var JOIN_KINDS = {
365
+ /** INNER JOIN type */
366
+ INNER: "INNER",
367
+ /** LEFT JOIN type */
368
+ LEFT: "LEFT",
369
+ /** RIGHT JOIN type */
370
+ RIGHT: "RIGHT",
371
+ /** CROSS JOIN type */
372
+ CROSS: "CROSS"
373
+ };
374
+ var ORDER_DIRECTIONS = {
375
+ /** Ascending order */
376
+ ASC: "ASC",
377
+ /** Descending order */
378
+ DESC: "DESC"
379
+ };
380
+
354
381
  // src/core/ast/aggregate-functions.ts
355
382
  var buildAggregate = (name) => (col) => ({
356
383
  type: "Function",
@@ -360,14 +387,41 @@ var buildAggregate = (name) => (col) => ({
360
387
  var count = buildAggregate("COUNT");
361
388
  var sum = buildAggregate("SUM");
362
389
  var avg = buildAggregate("AVG");
390
+ var min = buildAggregate("MIN");
391
+ var max = buildAggregate("MAX");
392
+
393
+ // src/core/ast/builders.ts
394
+ var buildColumnNode = (table, column) => {
395
+ if (column.type === "Column") {
396
+ return column;
397
+ }
398
+ const def = column;
399
+ const baseTable = def.table ? table.alias && def.table === table.name ? table.alias : def.table : table.alias || table.name;
400
+ return {
401
+ type: "Column",
402
+ table: baseTable,
403
+ name: def.name
404
+ };
405
+ };
406
+ var derivedTable = (query, alias, columnAliases) => ({
407
+ type: "DerivedTable",
408
+ query,
409
+ alias,
410
+ columnAliases
411
+ });
363
412
 
364
413
  // src/core/functions/standard-strategy.ts
365
- var StandardFunctionStrategy = class {
414
+ var StandardFunctionStrategy = class _StandardFunctionStrategy {
366
415
  constructor() {
367
416
  this.renderers = /* @__PURE__ */ new Map();
368
417
  this.registerStandard();
369
418
  }
370
419
  registerStandard() {
420
+ this.add("COUNT", ({ compiledArgs }) => `COUNT(${compiledArgs.join(", ")})`);
421
+ this.add("SUM", ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
422
+ this.add("AVG", ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
423
+ this.add("MIN", ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
424
+ this.add("MAX", ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
371
425
  this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
372
426
  this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
373
427
  this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
@@ -394,6 +448,7 @@ var StandardFunctionStrategy = class {
394
448
  this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
395
449
  this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
396
450
  this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
451
+ this.add("GROUP_CONCAT", (ctx) => this.renderGroupConcat(ctx));
397
452
  }
398
453
  add(name, renderer) {
399
454
  this.renderers.set(name, renderer);
@@ -401,6 +456,36 @@ var StandardFunctionStrategy = class {
401
456
  getRenderer(name) {
402
457
  return this.renderers.get(name);
403
458
  }
459
+ renderGroupConcat(ctx) {
460
+ const arg = ctx.compiledArgs[0];
461
+ const orderClause = this.buildOrderByExpression(ctx);
462
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
463
+ const separatorClause = this.formatGroupConcatSeparator(ctx);
464
+ return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
465
+ }
466
+ buildOrderByExpression(ctx) {
467
+ const orderBy = ctx.node.orderBy;
468
+ if (!orderBy || orderBy.length === 0) {
469
+ return "";
470
+ }
471
+ const parts = orderBy.map((order) => `${ctx.compileOperand(order.column)} ${order.direction}`);
472
+ return `ORDER BY ${parts.join(", ")}`;
473
+ }
474
+ formatGroupConcatSeparator(ctx) {
475
+ if (!ctx.node.separator) {
476
+ return "";
477
+ }
478
+ return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
479
+ }
480
+ getGroupConcatSeparatorOperand(ctx) {
481
+ return ctx.node.separator ?? _StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
482
+ }
483
+ static {
484
+ this.DEFAULT_GROUP_CONCAT_SEPARATOR = {
485
+ type: "Literal",
486
+ value: ","
487
+ };
488
+ }
404
489
  };
405
490
 
406
491
  // src/core/dialect/abstract.ts
@@ -747,7 +832,11 @@ var Dialect = class _Dialect {
747
832
  const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
748
833
  const renderer = this.functionStrategy.getRenderer(fnNode.name);
749
834
  if (renderer) {
750
- return renderer({ node: fnNode, compiledArgs });
835
+ return renderer({
836
+ node: fnNode,
837
+ compiledArgs,
838
+ compileOperand: (operand) => this.compileOperand(operand, ctx)
839
+ });
751
840
  }
752
841
  return `${fnNode.name}(${compiledArgs.join(", ")})`;
753
842
  }
@@ -1010,7 +1099,7 @@ var SqlDialectBase = class extends Dialect {
1010
1099
  return this.returningStrategy.compileReturning(returning, ctx);
1011
1100
  }
1012
1101
  compileInsertColumnList(columns) {
1013
- return columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
1102
+ return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
1014
1103
  }
1015
1104
  compileInsertValues(values, ctx) {
1016
1105
  return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
@@ -1036,7 +1125,7 @@ var SqlDialectBase = class extends Dialect {
1036
1125
  compileUpdateAssignments(assignments, ctx) {
1037
1126
  return assignments.map((assignment) => {
1038
1127
  const col = assignment.column;
1039
- const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1128
+ const target = this.quoteIdentifier(col.name);
1040
1129
  const value = this.compileOperand(assignment.value, ctx);
1041
1130
  return `${target} = ${value}`;
1042
1131
  }).join(", ");
@@ -1068,12 +1157,29 @@ var SqlDialectBase = class extends Dialect {
1068
1157
  if (tableSource.type === "FunctionTable") {
1069
1158
  return this.compileFunctionTable(tableSource, ctx);
1070
1159
  }
1160
+ if (tableSource.type === "DerivedTable") {
1161
+ return this.compileDerivedTable(tableSource, ctx);
1162
+ }
1071
1163
  return this.compileTableSource(tableSource);
1072
1164
  }
1073
1165
  compileFunctionTable(fn, ctx) {
1074
1166
  return FunctionTableFormatter.format(fn, ctx, this);
1075
1167
  }
1168
+ compileDerivedTable(table, ctx) {
1169
+ if (!table.alias) {
1170
+ throw new Error("Derived tables must have an alias.");
1171
+ }
1172
+ const subquery = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
1173
+ const columns = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1174
+ return `(${subquery}) AS ${this.quoteIdentifier(table.alias)}${columns}`;
1175
+ }
1076
1176
  compileTableSource(table) {
1177
+ if (table.type === "FunctionTable") {
1178
+ return this.compileFunctionTable(table);
1179
+ }
1180
+ if (table.type === "DerivedTable") {
1181
+ return this.compileDerivedTable(table);
1182
+ }
1077
1183
  const base = this.compileTableName(table);
1078
1184
  return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1079
1185
  }
@@ -1170,6 +1276,14 @@ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1170
1276
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1171
1277
  return `DATE_TRUNC('${partClean}', ${date})`;
1172
1278
  });
1279
+ this.add("GROUP_CONCAT", (ctx) => {
1280
+ const arg = ctx.compiledArgs[0];
1281
+ const orderClause = this.buildOrderByExpression(ctx);
1282
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
1283
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1284
+ const separator = ctx.compileOperand(separatorOperand);
1285
+ return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
1286
+ });
1173
1287
  }
1174
1288
  };
1175
1289
 
@@ -1414,6 +1528,12 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1414
1528
  }
1415
1529
  return `date(${date}, 'start of ${partClean}')`;
1416
1530
  });
1531
+ this.add("GROUP_CONCAT", (ctx) => {
1532
+ const arg = ctx.compiledArgs[0];
1533
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1534
+ const separator = ctx.compileOperand(separatorOperand);
1535
+ return `GROUP_CONCAT(${arg}, ${separator})`;
1536
+ });
1417
1537
  }
1418
1538
  };
1419
1539
 
@@ -1448,6 +1568,12 @@ var SqliteDialect = class extends SqlDialectBase {
1448
1568
  const columns = this.formatReturningColumns(returning);
1449
1569
  return ` RETURNING ${columns}`;
1450
1570
  }
1571
+ formatReturningColumns(returning) {
1572
+ return returning.map((column) => {
1573
+ const alias = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : "";
1574
+ return `${this.quoteIdentifier(column.name)}${alias}`;
1575
+ }).join(", ");
1576
+ }
1451
1577
  supportsReturning() {
1452
1578
  return true;
1453
1579
  }
@@ -1530,6 +1656,14 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1530
1656
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1531
1657
  return `DATETRUNC(${partClean}, ${date})`;
1532
1658
  });
1659
+ this.add("GROUP_CONCAT", (ctx) => {
1660
+ const arg = ctx.compiledArgs[0];
1661
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1662
+ const separator = ctx.compileOperand(separatorOperand);
1663
+ const orderClause = this.buildOrderByExpression(ctx);
1664
+ const withinGroup = orderClause ? ` WITHIN GROUP (${orderClause})` : "";
1665
+ return `STRING_AGG(${arg}, ${separator})${withinGroup}`;
1666
+ });
1533
1667
  }
1534
1668
  };
1535
1669
 
@@ -1606,6 +1740,9 @@ var SqlServerDialect = class extends Dialect {
1606
1740
  return `UPDATE ${table} SET ${assignments}${whereClause};`;
1607
1741
  }
1608
1742
  compileDeleteAst(ast, ctx) {
1743
+ if (ast.from.type !== "Table") {
1744
+ throw new Error("DELETE only supports base tables in the MSSQL dialect.");
1745
+ }
1609
1746
  const table = this.quoteIdentifier(ast.from.name);
1610
1747
  const whereClause = this.compileWhere(ast.where, ctx);
1611
1748
  return `DELETE FROM ${table}${whereClause};`;
@@ -1629,9 +1766,9 @@ var SqlServerDialect = class extends Dialect {
1629
1766
  return expr;
1630
1767
  }).join(", ");
1631
1768
  const distinct = ast.distinct ? "DISTINCT " : "";
1632
- const from = `${this.quoteIdentifier(ast.from.name)}`;
1769
+ const from = this.compileTableSource(ast.from, ctx);
1633
1770
  const joins = ast.joins.map((j) => {
1634
- const table = this.quoteIdentifier(j.table.name);
1771
+ const table = this.compileTableSource(j.table, ctx);
1635
1772
  const cond = this.compileExpression(j.condition, ctx);
1636
1773
  return `${j.kind} JOIN ${table} ON ${cond}`;
1637
1774
  }).join(" ");
@@ -1661,6 +1798,21 @@ var SqlServerDialect = class extends Dialect {
1661
1798
  }
1662
1799
  return pagination;
1663
1800
  }
1801
+ compileTableSource(table, ctx) {
1802
+ if (table.type === "FunctionTable") {
1803
+ return FunctionTableFormatter.format(table, ctx, this);
1804
+ }
1805
+ if (table.type === "DerivedTable") {
1806
+ return this.compileDerivedTable(table, ctx);
1807
+ }
1808
+ const base = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}` : this.quoteIdentifier(table.name);
1809
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1810
+ }
1811
+ compileDerivedTable(table, ctx) {
1812
+ const sub = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
1813
+ const cols = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1814
+ return `(${sub}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
1815
+ }
1664
1816
  compileCtes(ast, ctx) {
1665
1817
  if (!ast.ctes || ast.ctes.length === 0) return "";
1666
1818
  const defs = ast.ctes.map((cte) => {
@@ -1789,6 +1941,17 @@ var SelectQueryState = class _SelectQueryState {
1789
1941
  joins: [...this.ast.joins ?? [], join]
1790
1942
  });
1791
1943
  }
1944
+ /**
1945
+ * Replaces the FROM clause.
1946
+ * @param from - Table source for the FROM clause
1947
+ * @returns New SelectQueryState with updated FROM
1948
+ */
1949
+ withFrom(from) {
1950
+ return this.clone({
1951
+ ...this.ast,
1952
+ from
1953
+ });
1954
+ }
1792
1955
  /**
1793
1956
  * Adds a WHERE clause to the query
1794
1957
  * @param predicate - WHERE predicate expression
@@ -1899,24 +2062,6 @@ var createJoinNode = (kind, tableName, condition, relationName) => ({
1899
2062
  meta: relationName ? { relationName } : void 0
1900
2063
  });
1901
2064
 
1902
- // src/core/sql/sql.ts
1903
- var JOIN_KINDS = {
1904
- /** INNER JOIN type */
1905
- INNER: "INNER",
1906
- /** LEFT JOIN type */
1907
- LEFT: "LEFT",
1908
- /** RIGHT JOIN type */
1909
- RIGHT: "RIGHT",
1910
- /** CROSS JOIN type */
1911
- CROSS: "CROSS"
1912
- };
1913
- var ORDER_DIRECTIONS = {
1914
- /** Ascending order */
1915
- ASC: "ASC",
1916
- /** Descending order */
1917
- DESC: "DESC"
1918
- };
1919
-
1920
2065
  // src/query-builder/hydration-manager.ts
1921
2066
  var HydrationManager = class _HydrationManager {
1922
2067
  /**
@@ -2299,19 +2444,6 @@ var buildDefaultHydrationPlan = (table) => ({
2299
2444
  relations: []
2300
2445
  });
2301
2446
 
2302
- // src/core/ast/builders.ts
2303
- var buildColumnNode = (table, column) => {
2304
- if (column.type === "Column") {
2305
- return column;
2306
- }
2307
- const def = column;
2308
- return {
2309
- type: "Column",
2310
- table: def.table || table.name,
2311
- name: def.name
2312
- };
2313
- };
2314
-
2315
2447
  // src/query-builder/raw-column-parser.ts
2316
2448
  var parseRawColumn = (col, tableName, ctes) => {
2317
2449
  if (col.includes("(")) {
@@ -2351,6 +2483,8 @@ var QueryAstService = class {
2351
2483
  const existingAliases = new Set(
2352
2484
  this.state.ast.columns.map((c) => c.alias || c.name)
2353
2485
  );
2486
+ const from = this.state.ast.from;
2487
+ const rootTableName = from.type === "Table" && from.alias ? from.alias : this.table.name;
2354
2488
  const newCols = Object.entries(columns).reduce((acc, [alias, val]) => {
2355
2489
  if (existingAliases.has(alias)) return acc;
2356
2490
  if (isExpressionSelectionNode(val)) {
@@ -2358,9 +2492,10 @@ var QueryAstService = class {
2358
2492
  return acc;
2359
2493
  }
2360
2494
  const colDef = val;
2495
+ const resolvedTable = colDef.table && colDef.table === this.table.name && from.type === "Table" && from.alias ? from.alias : colDef.table || rootTableName;
2361
2496
  acc.push({
2362
2497
  type: "Column",
2363
- table: colDef.table || this.table.name,
2498
+ table: resolvedTable,
2364
2499
  name: colDef.name,
2365
2500
  alias
2366
2501
  });
@@ -2375,7 +2510,9 @@ var QueryAstService = class {
2375
2510
  * @returns Column selection result with updated state and added columns
2376
2511
  */
2377
2512
  selectRaw(cols) {
2378
- const newCols = cols.map((col) => parseRawColumn(col, this.table.name, this.state.ast.ctes));
2513
+ const from = this.state.ast.from;
2514
+ const defaultTable = from.type === "Table" && from.alias ? from.alias : this.table.name;
2515
+ const newCols = cols.map((col) => parseRawColumn(col, defaultTable, this.state.ast.ctes));
2379
2516
  const nextState = this.state.withColumns(newCols);
2380
2517
  return { state: nextState, addedColumns: newCols };
2381
2518
  }
@@ -2411,6 +2548,14 @@ var QueryAstService = class {
2411
2548
  };
2412
2549
  return this.state.withSetOperation(op);
2413
2550
  }
2551
+ /**
2552
+ * Replaces the FROM clause for the current query.
2553
+ * @param from - Table source to use in the FROM clause
2554
+ * @returns Updated query state with new FROM
2555
+ */
2556
+ withFrom(from) {
2557
+ return this.state.withFrom(from);
2558
+ }
2414
2559
  /**
2415
2560
  * Selects a subquery as a column
2416
2561
  * @param alias - Alias for the subquery
@@ -2444,7 +2589,9 @@ var QueryAstService = class {
2444
2589
  * @returns Updated query state with GROUP BY clause
2445
2590
  */
2446
2591
  withGroupBy(col) {
2447
- const node = buildColumnNode(this.table, col);
2592
+ const from = this.state.ast.from;
2593
+ const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
2594
+ const node = buildColumnNode(tableRef, col);
2448
2595
  return this.state.withGroupBy([node]);
2449
2596
  }
2450
2597
  /**
@@ -2463,7 +2610,9 @@ var QueryAstService = class {
2463
2610
  * @returns Updated query state with ORDER BY clause
2464
2611
  */
2465
2612
  withOrderBy(col, direction) {
2466
- const node = buildColumnNode(this.table, col);
2613
+ const from = this.state.ast.from;
2614
+ const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
2615
+ const node = buildColumnNode(tableRef, col);
2467
2616
  return this.state.withOrderBy([{ type: "OrderBy", column: node, direction }]);
2468
2617
  }
2469
2618
  /**
@@ -2567,7 +2716,8 @@ var RelationProjectionHelper = class {
2567
2716
  var assertNever = (value) => {
2568
2717
  throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
2569
2718
  };
2570
- var baseRelationCondition = (root, relation) => {
2719
+ var baseRelationCondition = (root, relation, rootAlias) => {
2720
+ const rootTable = rootAlias || root.name;
2571
2721
  const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
2572
2722
  const localKey = relation.localKey || defaultLocalKey;
2573
2723
  switch (relation.type) {
@@ -2575,12 +2725,12 @@ var baseRelationCondition = (root, relation) => {
2575
2725
  case RelationKinds.HasOne:
2576
2726
  return eq(
2577
2727
  { type: "Column", table: relation.target.name, name: relation.foreignKey },
2578
- { type: "Column", table: root.name, name: localKey }
2728
+ { type: "Column", table: rootTable, name: localKey }
2579
2729
  );
2580
2730
  case RelationKinds.BelongsTo:
2581
2731
  return eq(
2582
2732
  { type: "Column", table: relation.target.name, name: localKey },
2583
- { type: "Column", table: root.name, name: relation.foreignKey }
2733
+ { type: "Column", table: rootTable, name: relation.foreignKey }
2584
2734
  );
2585
2735
  case RelationKinds.BelongsToMany:
2586
2736
  throw new Error("BelongsToMany relations do not support the standard join condition builder");
@@ -2588,12 +2738,13 @@ var baseRelationCondition = (root, relation) => {
2588
2738
  return assertNever(relation);
2589
2739
  }
2590
2740
  };
2591
- var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) => {
2741
+ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias) => {
2592
2742
  const rootKey = relation.localKey || findPrimaryKey(root);
2593
2743
  const targetKey = relation.targetKey || findPrimaryKey(relation.target);
2744
+ const rootTable = rootAlias || root.name;
2594
2745
  const pivotCondition = eq(
2595
2746
  { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToRoot },
2596
- { type: "Column", table: root.name, name: rootKey }
2747
+ { type: "Column", table: rootTable, name: rootKey }
2597
2748
  );
2598
2749
  const pivotJoin = createJoinNode(joinKind, relation.pivotTable.name, pivotCondition);
2599
2750
  let targetCondition = eq(
@@ -2611,12 +2762,12 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) =>
2611
2762
  );
2612
2763
  return [pivotJoin, targetJoin];
2613
2764
  };
2614
- var buildRelationJoinCondition = (root, relation, extra) => {
2615
- const base = baseRelationCondition(root, relation);
2765
+ var buildRelationJoinCondition = (root, relation, extra, rootAlias) => {
2766
+ const base = baseRelationCondition(root, relation, rootAlias);
2616
2767
  return extra ? and(base, extra) : base;
2617
2768
  };
2618
- var buildRelationCorrelation = (root, relation) => {
2619
- return baseRelationCondition(root, relation);
2769
+ var buildRelationCorrelation = (root, relation, rootAlias) => {
2770
+ return baseRelationCondition(root, relation, rootAlias);
2620
2771
  };
2621
2772
 
2622
2773
  // src/core/ast/join-metadata.ts
@@ -2660,7 +2811,7 @@ var RelationService = class {
2660
2811
  match(relationName, predicate) {
2661
2812
  const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
2662
2813
  const pk = findPrimaryKey(this.table);
2663
- const distinctCols = [{ type: "Column", table: this.table.name, name: pk }];
2814
+ const distinctCols = [{ type: "Column", table: this.rootTableName(), name: pk }];
2664
2815
  const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
2665
2816
  const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
2666
2817
  return { state: nextState, hydration: joined.hydration };
@@ -2747,9 +2898,13 @@ var RelationService = class {
2747
2898
  * @param ast - Query AST to modify
2748
2899
  * @returns Modified query AST with relation correlation
2749
2900
  */
2750
- applyRelationCorrelation(relationName, ast) {
2901
+ applyRelationCorrelation(relationName, ast, additionalCorrelation) {
2751
2902
  const relation = this.getRelation(relationName);
2752
- const correlation = buildRelationCorrelation(this.table, relation);
2903
+ const rootAlias = this.state.ast.from.type === "Table" ? this.state.ast.from.alias : void 0;
2904
+ let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
2905
+ if (additionalCorrelation) {
2906
+ correlation = and(correlation, additionalCorrelation);
2907
+ }
2753
2908
  const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
2754
2909
  return {
2755
2910
  ...ast,
@@ -2766,17 +2921,19 @@ var RelationService = class {
2766
2921
  */
2767
2922
  withJoin(state, relationName, joinKind, extraCondition) {
2768
2923
  const relation = this.getRelation(relationName);
2924
+ const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
2769
2925
  if (relation.type === RelationKinds.BelongsToMany) {
2770
2926
  const joins = buildBelongsToManyJoins(
2771
2927
  this.table,
2772
2928
  relationName,
2773
2929
  relation,
2774
2930
  joinKind,
2775
- extraCondition
2931
+ extraCondition,
2932
+ rootAlias
2776
2933
  );
2777
2934
  return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
2778
2935
  }
2779
- const condition = buildRelationJoinCondition(this.table, relation, extraCondition);
2936
+ const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
2780
2937
  const joinNode = createJoinNode(joinKind, relation.target.name, condition, relationName);
2781
2938
  return this.astService(state).withJoin(joinNode);
2782
2939
  }
@@ -2815,6 +2972,11 @@ var RelationService = class {
2815
2972
  astService(state = this.state) {
2816
2973
  return this.createQueryAstService(this.table, state);
2817
2974
  }
2975
+ rootTableName() {
2976
+ const from = this.state.ast.from;
2977
+ if (from.type === "Table" && from.alias) return from.alias;
2978
+ return this.table.name;
2979
+ }
2818
2980
  };
2819
2981
 
2820
2982
  // src/query-builder/select-query-builder-deps.ts
@@ -2889,7 +3051,9 @@ var ColumnSelector = class {
2889
3051
  * @returns Updated query context with DISTINCT clause
2890
3052
  */
2891
3053
  distinct(context, columns) {
2892
- const nodes = columns.map((col) => buildColumnNode(this.env.table, col));
3054
+ const from = context.state.ast.from;
3055
+ const tableRef = from.type === "Table" && from.alias ? { ...this.env.table, alias: from.alias } : this.env.table;
3056
+ const nodes = columns.map((col) => buildColumnNode(tableRef, col));
2893
3057
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
2894
3058
  const nextState = astService.withDistinct(nodes);
2895
3059
  return { state: nextState, hydration: context.hydration };
@@ -2946,8 +3110,8 @@ var RelationManager = class {
2946
3110
  * @param ast - Query AST to modify
2947
3111
  * @returns Modified query AST with relation correlation
2948
3112
  */
2949
- applyRelationCorrelation(context, relationName, ast) {
2950
- return this.createService(context).applyRelationCorrelation(relationName, ast);
3113
+ applyRelationCorrelation(context, relationName, ast, additionalCorrelation) {
3114
+ return this.createService(context).applyRelationCorrelation(relationName, ast, additionalCorrelation);
2951
3115
  }
2952
3116
  /**
2953
3117
  * Creates a relation service instance
@@ -3994,9 +4158,30 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
3994
4158
  clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
3995
4159
  return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
3996
4160
  }
4161
+ /**
4162
+ * Applies an alias to the root FROM table.
4163
+ * @param alias - Alias to apply
4164
+ */
4165
+ as(alias) {
4166
+ const from = this.context.state.ast.from;
4167
+ if (from.type !== "Table") {
4168
+ throw new Error("Cannot alias non-table FROM sources");
4169
+ }
4170
+ const nextFrom = { ...from, alias };
4171
+ const nextContext = this.applyAst(this.context, (service) => service.withFrom(nextFrom));
4172
+ return this.clone(nextContext);
4173
+ }
3997
4174
  resolveQueryNode(query) {
3998
4175
  return typeof query.getAST === "function" ? query.getAST() : query;
3999
4176
  }
4177
+ applyCorrelation(ast, correlation) {
4178
+ if (!correlation) return ast;
4179
+ const combinedWhere = ast.where ? and(correlation, ast.where) : correlation;
4180
+ return {
4181
+ ...ast,
4182
+ where: combinedWhere
4183
+ };
4184
+ }
4000
4185
  createChildBuilder(table) {
4001
4186
  return new _SelectQueryBuilder(table, void 0, void 0, this.env.deps);
4002
4187
  }
@@ -4025,6 +4210,21 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4025
4210
  select(columns) {
4026
4211
  return this.clone(this.columnSelector.select(this.context, columns));
4027
4212
  }
4213
+ /**
4214
+ * Selects columns from the root table by name (typed).
4215
+ * @param cols - Column names on the root table
4216
+ */
4217
+ selectColumns(...cols) {
4218
+ const selection = {};
4219
+ for (const key of cols) {
4220
+ const col = this.env.table.columns[key];
4221
+ if (!col) {
4222
+ throw new Error(`Column '${key}' not found on table '${this.env.table.name}'`);
4223
+ }
4224
+ selection[key] = col;
4225
+ }
4226
+ return this.select(selection);
4227
+ }
4028
4228
  /**
4029
4229
 
4030
4230
  * Selects raw column expressions
@@ -4073,6 +4273,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4073
4273
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
4074
4274
  return this.clone(nextContext);
4075
4275
  }
4276
+ /**
4277
+ * Replaces the FROM clause with a derived table (subquery with alias)
4278
+ * @param subquery - Subquery to use as the FROM source
4279
+ * @param alias - Alias for the derived table
4280
+ * @param columnAliases - Optional column alias list
4281
+ * @returns New query builder instance with updated FROM
4282
+ */
4283
+ fromSubquery(subquery, alias, columnAliases) {
4284
+ const subAst = this.resolveQueryNode(subquery);
4285
+ const fromNode = derivedTable(subAst, alias, columnAliases);
4286
+ const nextContext = this.applyAst(this.context, (service) => service.withFrom(fromNode));
4287
+ return this.clone(nextContext);
4288
+ }
4076
4289
  /**
4077
4290
 
4078
4291
  * Selects a subquery as a column
@@ -4088,6 +4301,21 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4088
4301
  const query = this.resolveQueryNode(sub);
4089
4302
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
4090
4303
  }
4304
+ /**
4305
+ * Adds a JOIN against a derived table (subquery with alias)
4306
+ * @param subquery - Subquery to join
4307
+ * @param alias - Alias for the derived table
4308
+ * @param condition - Join condition expression
4309
+ * @param joinKind - Join kind (defaults to INNER)
4310
+ * @param columnAliases - Optional column alias list for the derived table
4311
+ * @returns New query builder instance with the derived-table join
4312
+ */
4313
+ joinSubquery(subquery, alias, condition, joinKind = JOIN_KINDS.INNER, columnAliases) {
4314
+ const subAst = this.resolveQueryNode(subquery);
4315
+ const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
4316
+ const nextContext = this.applyAst(this.context, (service) => service.withJoin(joinNode));
4317
+ return this.clone(nextContext);
4318
+ }
4091
4319
  /**
4092
4320
 
4093
4321
  * Adds an INNER JOIN to the query
@@ -4185,6 +4413,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4185
4413
  nextLazy.add(relationName);
4186
4414
  return this.clone(this.context, nextLazy);
4187
4415
  }
4416
+ /**
4417
+ * Selects columns for a related table in a single hop.
4418
+ */
4419
+ selectRelationColumns(relationName, ...cols) {
4420
+ const relation = this.env.table.relations[relationName];
4421
+ if (!relation) {
4422
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4423
+ }
4424
+ const target = relation.target;
4425
+ for (const col of cols) {
4426
+ if (!target.columns[col]) {
4427
+ throw new Error(
4428
+ `Column '${col}' not found on related table '${target.name}' for relation '${relationName}'`
4429
+ );
4430
+ }
4431
+ }
4432
+ return this.include(relationName, { columns: cols });
4433
+ }
4434
+ /**
4435
+ * Convenience alias for selecting specific columns from a relation.
4436
+ */
4437
+ includePick(relationName, cols) {
4438
+ return this.selectRelationColumns(relationName, ...cols);
4439
+ }
4440
+ /**
4441
+ * Selects columns for the root table and relations from a single config object.
4442
+ */
4443
+ selectColumnsDeep(config) {
4444
+ let qb = this;
4445
+ if (config.root?.length) {
4446
+ qb = qb.selectColumns(...config.root);
4447
+ }
4448
+ for (const key of Object.keys(config)) {
4449
+ if (key === "root") continue;
4450
+ const relName = key;
4451
+ const cols = config[relName];
4452
+ if (!cols || !cols.length) continue;
4453
+ qb = qb.selectRelationColumns(relName, ...cols);
4454
+ }
4455
+ return qb;
4456
+ }
4188
4457
  getLazyRelations() {
4189
4458
  return Array.from(this.lazyRelations);
4190
4459
  }
@@ -4346,9 +4615,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4346
4615
  * @returns New query builder instance with the WHERE EXISTS condition
4347
4616
 
4348
4617
  */
4349
- whereExists(subquery) {
4618
+ whereExists(subquery, correlate) {
4350
4619
  const subAst = this.resolveQueryNode(subquery);
4351
- return this.where(exists(subAst));
4620
+ const correlated = this.applyCorrelation(subAst, correlate);
4621
+ return this.where(exists(correlated));
4352
4622
  }
4353
4623
  /**
4354
4624
 
@@ -4359,9 +4629,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4359
4629
  * @returns New query builder instance with the WHERE NOT EXISTS condition
4360
4630
 
4361
4631
  */
4362
- whereNotExists(subquery) {
4632
+ whereNotExists(subquery, correlate) {
4363
4633
  const subAst = this.resolveQueryNode(subquery);
4364
- return this.where(notExists(subAst));
4634
+ const correlated = this.applyCorrelation(subAst, correlate);
4635
+ return this.where(notExists(correlated));
4365
4636
  }
4366
4637
  /**
4367
4638
 
@@ -4374,17 +4645,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4374
4645
  * @returns New query builder instance with the relationship existence check
4375
4646
 
4376
4647
  */
4377
- whereHas(relationName, callback) {
4648
+ whereHas(relationName, callbackOrOptions, maybeOptions) {
4378
4649
  const relation = this.env.table.relations[relationName];
4379
4650
  if (!relation) {
4380
4651
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4381
4652
  }
4653
+ const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
4654
+ const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
4382
4655
  let subQb = this.createChildBuilder(relation.target);
4383
4656
  if (callback) {
4384
4657
  subQb = callback(subQb);
4385
4658
  }
4386
4659
  const subAst = subQb.getAST();
4387
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
4660
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
4388
4661
  return this.where(exists(finalSubAst));
4389
4662
  }
4390
4663
  /**
@@ -4398,17 +4671,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4398
4671
  * @returns New query builder instance with the relationship non-existence check
4399
4672
 
4400
4673
  */
4401
- whereHasNot(relationName, callback) {
4674
+ whereHasNot(relationName, callbackOrOptions, maybeOptions) {
4402
4675
  const relation = this.env.table.relations[relationName];
4403
4676
  if (!relation) {
4404
4677
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4405
4678
  }
4679
+ const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
4680
+ const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
4406
4681
  let subQb = this.createChildBuilder(relation.target);
4407
4682
  if (callback) {
4408
4683
  subQb = callback(subQb);
4409
4684
  }
4410
4685
  const subAst = subQb.getAST();
4411
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
4686
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
4412
4687
  return this.where(notExists(finalSubAst));
4413
4688
  }
4414
4689
  /**