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
@@ -332,6 +332,15 @@ var isWindowFunctionNode = (node) => node?.type === "WindowFunction";
332
332
  var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
333
333
 
334
334
  // src/core/ast/expression-builders.ts
335
+ var valueToOperand = (value) => {
336
+ if (isOperandNode(value)) {
337
+ return value;
338
+ }
339
+ return {
340
+ type: "Literal",
341
+ value
342
+ };
343
+ };
335
344
  var toNode = (col) => {
336
345
  if (isOperandNode(col)) return col;
337
346
  const def = col;
@@ -341,10 +350,10 @@ var toLiteralNode = (value) => ({
341
350
  type: "Literal",
342
351
  value
343
352
  });
353
+ var isLiteralValue = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
344
354
  var toOperand = (val) => {
345
- if (val === null) return { type: "Literal", value: null };
346
- if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
347
- return { type: "Literal", value: val };
355
+ if (isLiteralValue(val)) {
356
+ return valueToOperand(val);
348
357
  }
349
358
  return toNode(val);
350
359
  };
@@ -385,6 +394,24 @@ var notExists = (subquery) => ({
385
394
  subquery
386
395
  });
387
396
 
397
+ // src/core/sql/sql.ts
398
+ var JOIN_KINDS = {
399
+ /** INNER JOIN type */
400
+ INNER: "INNER",
401
+ /** LEFT JOIN type */
402
+ LEFT: "LEFT",
403
+ /** RIGHT JOIN type */
404
+ RIGHT: "RIGHT",
405
+ /** CROSS JOIN type */
406
+ CROSS: "CROSS"
407
+ };
408
+ var ORDER_DIRECTIONS = {
409
+ /** Ascending order */
410
+ ASC: "ASC",
411
+ /** Descending order */
412
+ DESC: "DESC"
413
+ };
414
+
388
415
  // src/core/ast/aggregate-functions.ts
389
416
  var buildAggregate = (name) => (col) => ({
390
417
  type: "Function",
@@ -394,14 +421,41 @@ var buildAggregate = (name) => (col) => ({
394
421
  var count = buildAggregate("COUNT");
395
422
  var sum = buildAggregate("SUM");
396
423
  var avg = buildAggregate("AVG");
424
+ var min = buildAggregate("MIN");
425
+ var max = buildAggregate("MAX");
426
+
427
+ // src/core/ast/builders.ts
428
+ var buildColumnNode = (table, column) => {
429
+ if (column.type === "Column") {
430
+ return column;
431
+ }
432
+ const def = column;
433
+ const baseTable = def.table ? table.alias && def.table === table.name ? table.alias : def.table : table.alias || table.name;
434
+ return {
435
+ type: "Column",
436
+ table: baseTable,
437
+ name: def.name
438
+ };
439
+ };
440
+ var derivedTable = (query, alias, columnAliases) => ({
441
+ type: "DerivedTable",
442
+ query,
443
+ alias,
444
+ columnAliases
445
+ });
397
446
 
398
447
  // src/core/functions/standard-strategy.ts
399
- var StandardFunctionStrategy = class {
448
+ var StandardFunctionStrategy = class _StandardFunctionStrategy {
400
449
  constructor() {
401
450
  this.renderers = /* @__PURE__ */ new Map();
402
451
  this.registerStandard();
403
452
  }
404
453
  registerStandard() {
454
+ this.add("COUNT", ({ compiledArgs }) => `COUNT(${compiledArgs.join(", ")})`);
455
+ this.add("SUM", ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
456
+ this.add("AVG", ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
457
+ this.add("MIN", ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
458
+ this.add("MAX", ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
405
459
  this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
406
460
  this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
407
461
  this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
@@ -428,6 +482,7 @@ var StandardFunctionStrategy = class {
428
482
  this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
429
483
  this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
430
484
  this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
485
+ this.add("GROUP_CONCAT", (ctx) => this.renderGroupConcat(ctx));
431
486
  }
432
487
  add(name, renderer) {
433
488
  this.renderers.set(name, renderer);
@@ -435,6 +490,36 @@ var StandardFunctionStrategy = class {
435
490
  getRenderer(name) {
436
491
  return this.renderers.get(name);
437
492
  }
493
+ renderGroupConcat(ctx) {
494
+ const arg = ctx.compiledArgs[0];
495
+ const orderClause = this.buildOrderByExpression(ctx);
496
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
497
+ const separatorClause = this.formatGroupConcatSeparator(ctx);
498
+ return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
499
+ }
500
+ buildOrderByExpression(ctx) {
501
+ const orderBy = ctx.node.orderBy;
502
+ if (!orderBy || orderBy.length === 0) {
503
+ return "";
504
+ }
505
+ const parts = orderBy.map((order) => `${ctx.compileOperand(order.column)} ${order.direction}`);
506
+ return `ORDER BY ${parts.join(", ")}`;
507
+ }
508
+ formatGroupConcatSeparator(ctx) {
509
+ if (!ctx.node.separator) {
510
+ return "";
511
+ }
512
+ return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
513
+ }
514
+ getGroupConcatSeparatorOperand(ctx) {
515
+ return ctx.node.separator ?? _StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
516
+ }
517
+ static {
518
+ this.DEFAULT_GROUP_CONCAT_SEPARATOR = {
519
+ type: "Literal",
520
+ value: ","
521
+ };
522
+ }
438
523
  };
439
524
 
440
525
  // src/core/dialect/abstract.ts
@@ -781,7 +866,11 @@ var Dialect = class _Dialect {
781
866
  const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
782
867
  const renderer = this.functionStrategy.getRenderer(fnNode.name);
783
868
  if (renderer) {
784
- return renderer({ node: fnNode, compiledArgs });
869
+ return renderer({
870
+ node: fnNode,
871
+ compiledArgs,
872
+ compileOperand: (operand) => this.compileOperand(operand, ctx)
873
+ });
785
874
  }
786
875
  return `${fnNode.name}(${compiledArgs.join(", ")})`;
787
876
  }
@@ -1044,7 +1133,7 @@ var SqlDialectBase = class extends Dialect {
1044
1133
  return this.returningStrategy.compileReturning(returning, ctx);
1045
1134
  }
1046
1135
  compileInsertColumnList(columns) {
1047
- return columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
1136
+ return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
1048
1137
  }
1049
1138
  compileInsertValues(values, ctx) {
1050
1139
  return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
@@ -1070,7 +1159,7 @@ var SqlDialectBase = class extends Dialect {
1070
1159
  compileUpdateAssignments(assignments, ctx) {
1071
1160
  return assignments.map((assignment) => {
1072
1161
  const col = assignment.column;
1073
- const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1162
+ const target = this.quoteIdentifier(col.name);
1074
1163
  const value = this.compileOperand(assignment.value, ctx);
1075
1164
  return `${target} = ${value}`;
1076
1165
  }).join(", ");
@@ -1102,12 +1191,29 @@ var SqlDialectBase = class extends Dialect {
1102
1191
  if (tableSource.type === "FunctionTable") {
1103
1192
  return this.compileFunctionTable(tableSource, ctx);
1104
1193
  }
1194
+ if (tableSource.type === "DerivedTable") {
1195
+ return this.compileDerivedTable(tableSource, ctx);
1196
+ }
1105
1197
  return this.compileTableSource(tableSource);
1106
1198
  }
1107
1199
  compileFunctionTable(fn, ctx) {
1108
1200
  return FunctionTableFormatter.format(fn, ctx, this);
1109
1201
  }
1202
+ compileDerivedTable(table, ctx) {
1203
+ if (!table.alias) {
1204
+ throw new Error("Derived tables must have an alias.");
1205
+ }
1206
+ const subquery = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
1207
+ const columns = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1208
+ return `(${subquery}) AS ${this.quoteIdentifier(table.alias)}${columns}`;
1209
+ }
1110
1210
  compileTableSource(table) {
1211
+ if (table.type === "FunctionTable") {
1212
+ return this.compileFunctionTable(table);
1213
+ }
1214
+ if (table.type === "DerivedTable") {
1215
+ return this.compileDerivedTable(table);
1216
+ }
1111
1217
  const base = this.compileTableName(table);
1112
1218
  return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1113
1219
  }
@@ -1204,6 +1310,14 @@ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1204
1310
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1205
1311
  return `DATE_TRUNC('${partClean}', ${date})`;
1206
1312
  });
1313
+ this.add("GROUP_CONCAT", (ctx) => {
1314
+ const arg = ctx.compiledArgs[0];
1315
+ const orderClause = this.buildOrderByExpression(ctx);
1316
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
1317
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1318
+ const separator = ctx.compileOperand(separatorOperand);
1319
+ return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
1320
+ });
1207
1321
  }
1208
1322
  };
1209
1323
 
@@ -1448,6 +1562,12 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1448
1562
  }
1449
1563
  return `date(${date}, 'start of ${partClean}')`;
1450
1564
  });
1565
+ this.add("GROUP_CONCAT", (ctx) => {
1566
+ const arg = ctx.compiledArgs[0];
1567
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1568
+ const separator = ctx.compileOperand(separatorOperand);
1569
+ return `GROUP_CONCAT(${arg}, ${separator})`;
1570
+ });
1451
1571
  }
1452
1572
  };
1453
1573
 
@@ -1482,6 +1602,12 @@ var SqliteDialect = class extends SqlDialectBase {
1482
1602
  const columns = this.formatReturningColumns(returning);
1483
1603
  return ` RETURNING ${columns}`;
1484
1604
  }
1605
+ formatReturningColumns(returning) {
1606
+ return returning.map((column) => {
1607
+ const alias = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : "";
1608
+ return `${this.quoteIdentifier(column.name)}${alias}`;
1609
+ }).join(", ");
1610
+ }
1485
1611
  supportsReturning() {
1486
1612
  return true;
1487
1613
  }
@@ -1564,6 +1690,14 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1564
1690
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1565
1691
  return `DATETRUNC(${partClean}, ${date})`;
1566
1692
  });
1693
+ this.add("GROUP_CONCAT", (ctx) => {
1694
+ const arg = ctx.compiledArgs[0];
1695
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1696
+ const separator = ctx.compileOperand(separatorOperand);
1697
+ const orderClause = this.buildOrderByExpression(ctx);
1698
+ const withinGroup = orderClause ? ` WITHIN GROUP (${orderClause})` : "";
1699
+ return `STRING_AGG(${arg}, ${separator})${withinGroup}`;
1700
+ });
1567
1701
  }
1568
1702
  };
1569
1703
 
@@ -1640,6 +1774,9 @@ var SqlServerDialect = class extends Dialect {
1640
1774
  return `UPDATE ${table} SET ${assignments}${whereClause};`;
1641
1775
  }
1642
1776
  compileDeleteAst(ast, ctx) {
1777
+ if (ast.from.type !== "Table") {
1778
+ throw new Error("DELETE only supports base tables in the MSSQL dialect.");
1779
+ }
1643
1780
  const table = this.quoteIdentifier(ast.from.name);
1644
1781
  const whereClause = this.compileWhere(ast.where, ctx);
1645
1782
  return `DELETE FROM ${table}${whereClause};`;
@@ -1663,9 +1800,9 @@ var SqlServerDialect = class extends Dialect {
1663
1800
  return expr;
1664
1801
  }).join(", ");
1665
1802
  const distinct = ast.distinct ? "DISTINCT " : "";
1666
- const from = `${this.quoteIdentifier(ast.from.name)}`;
1803
+ const from = this.compileTableSource(ast.from, ctx);
1667
1804
  const joins = ast.joins.map((j) => {
1668
- const table = this.quoteIdentifier(j.table.name);
1805
+ const table = this.compileTableSource(j.table, ctx);
1669
1806
  const cond = this.compileExpression(j.condition, ctx);
1670
1807
  return `${j.kind} JOIN ${table} ON ${cond}`;
1671
1808
  }).join(" ");
@@ -1695,6 +1832,21 @@ var SqlServerDialect = class extends Dialect {
1695
1832
  }
1696
1833
  return pagination;
1697
1834
  }
1835
+ compileTableSource(table, ctx) {
1836
+ if (table.type === "FunctionTable") {
1837
+ return FunctionTableFormatter.format(table, ctx, this);
1838
+ }
1839
+ if (table.type === "DerivedTable") {
1840
+ return this.compileDerivedTable(table, ctx);
1841
+ }
1842
+ const base = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}` : this.quoteIdentifier(table.name);
1843
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1844
+ }
1845
+ compileDerivedTable(table, ctx) {
1846
+ const sub = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
1847
+ const cols = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
1848
+ return `(${sub}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
1849
+ }
1698
1850
  compileCtes(ast, ctx) {
1699
1851
  if (!ast.ctes || ast.ctes.length === 0) return "";
1700
1852
  const defs = ast.ctes.map((cte) => {
@@ -1823,6 +1975,17 @@ var SelectQueryState = class _SelectQueryState {
1823
1975
  joins: [...this.ast.joins ?? [], join]
1824
1976
  });
1825
1977
  }
1978
+ /**
1979
+ * Replaces the FROM clause.
1980
+ * @param from - Table source for the FROM clause
1981
+ * @returns New SelectQueryState with updated FROM
1982
+ */
1983
+ withFrom(from) {
1984
+ return this.clone({
1985
+ ...this.ast,
1986
+ from
1987
+ });
1988
+ }
1826
1989
  /**
1827
1990
  * Adds a WHERE clause to the query
1828
1991
  * @param predicate - WHERE predicate expression
@@ -1933,24 +2096,6 @@ var createJoinNode = (kind, tableName, condition, relationName) => ({
1933
2096
  meta: relationName ? { relationName } : void 0
1934
2097
  });
1935
2098
 
1936
- // src/core/sql/sql.ts
1937
- var JOIN_KINDS = {
1938
- /** INNER JOIN type */
1939
- INNER: "INNER",
1940
- /** LEFT JOIN type */
1941
- LEFT: "LEFT",
1942
- /** RIGHT JOIN type */
1943
- RIGHT: "RIGHT",
1944
- /** CROSS JOIN type */
1945
- CROSS: "CROSS"
1946
- };
1947
- var ORDER_DIRECTIONS = {
1948
- /** Ascending order */
1949
- ASC: "ASC",
1950
- /** Descending order */
1951
- DESC: "DESC"
1952
- };
1953
-
1954
2099
  // src/query-builder/hydration-manager.ts
1955
2100
  var HydrationManager = class _HydrationManager {
1956
2101
  /**
@@ -2333,19 +2478,6 @@ var buildDefaultHydrationPlan = (table) => ({
2333
2478
  relations: []
2334
2479
  });
2335
2480
 
2336
- // src/core/ast/builders.ts
2337
- var buildColumnNode = (table, column) => {
2338
- if (column.type === "Column") {
2339
- return column;
2340
- }
2341
- const def = column;
2342
- return {
2343
- type: "Column",
2344
- table: def.table || table.name,
2345
- name: def.name
2346
- };
2347
- };
2348
-
2349
2481
  // src/query-builder/raw-column-parser.ts
2350
2482
  var parseRawColumn = (col, tableName, ctes) => {
2351
2483
  if (col.includes("(")) {
@@ -2385,6 +2517,8 @@ var QueryAstService = class {
2385
2517
  const existingAliases = new Set(
2386
2518
  this.state.ast.columns.map((c) => c.alias || c.name)
2387
2519
  );
2520
+ const from = this.state.ast.from;
2521
+ const rootTableName = from.type === "Table" && from.alias ? from.alias : this.table.name;
2388
2522
  const newCols = Object.entries(columns).reduce((acc, [alias, val]) => {
2389
2523
  if (existingAliases.has(alias)) return acc;
2390
2524
  if (isExpressionSelectionNode(val)) {
@@ -2392,9 +2526,10 @@ var QueryAstService = class {
2392
2526
  return acc;
2393
2527
  }
2394
2528
  const colDef = val;
2529
+ const resolvedTable = colDef.table && colDef.table === this.table.name && from.type === "Table" && from.alias ? from.alias : colDef.table || rootTableName;
2395
2530
  acc.push({
2396
2531
  type: "Column",
2397
- table: colDef.table || this.table.name,
2532
+ table: resolvedTable,
2398
2533
  name: colDef.name,
2399
2534
  alias
2400
2535
  });
@@ -2409,7 +2544,9 @@ var QueryAstService = class {
2409
2544
  * @returns Column selection result with updated state and added columns
2410
2545
  */
2411
2546
  selectRaw(cols) {
2412
- const newCols = cols.map((col) => parseRawColumn(col, this.table.name, this.state.ast.ctes));
2547
+ const from = this.state.ast.from;
2548
+ const defaultTable = from.type === "Table" && from.alias ? from.alias : this.table.name;
2549
+ const newCols = cols.map((col) => parseRawColumn(col, defaultTable, this.state.ast.ctes));
2413
2550
  const nextState = this.state.withColumns(newCols);
2414
2551
  return { state: nextState, addedColumns: newCols };
2415
2552
  }
@@ -2445,6 +2582,14 @@ var QueryAstService = class {
2445
2582
  };
2446
2583
  return this.state.withSetOperation(op);
2447
2584
  }
2585
+ /**
2586
+ * Replaces the FROM clause for the current query.
2587
+ * @param from - Table source to use in the FROM clause
2588
+ * @returns Updated query state with new FROM
2589
+ */
2590
+ withFrom(from) {
2591
+ return this.state.withFrom(from);
2592
+ }
2448
2593
  /**
2449
2594
  * Selects a subquery as a column
2450
2595
  * @param alias - Alias for the subquery
@@ -2478,7 +2623,9 @@ var QueryAstService = class {
2478
2623
  * @returns Updated query state with GROUP BY clause
2479
2624
  */
2480
2625
  withGroupBy(col) {
2481
- const node = buildColumnNode(this.table, col);
2626
+ const from = this.state.ast.from;
2627
+ const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
2628
+ const node = buildColumnNode(tableRef, col);
2482
2629
  return this.state.withGroupBy([node]);
2483
2630
  }
2484
2631
  /**
@@ -2497,7 +2644,9 @@ var QueryAstService = class {
2497
2644
  * @returns Updated query state with ORDER BY clause
2498
2645
  */
2499
2646
  withOrderBy(col, direction) {
2500
- const node = buildColumnNode(this.table, col);
2647
+ const from = this.state.ast.from;
2648
+ const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
2649
+ const node = buildColumnNode(tableRef, col);
2501
2650
  return this.state.withOrderBy([{ type: "OrderBy", column: node, direction }]);
2502
2651
  }
2503
2652
  /**
@@ -2601,7 +2750,8 @@ var RelationProjectionHelper = class {
2601
2750
  var assertNever = (value) => {
2602
2751
  throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
2603
2752
  };
2604
- var baseRelationCondition = (root, relation) => {
2753
+ var baseRelationCondition = (root, relation, rootAlias) => {
2754
+ const rootTable = rootAlias || root.name;
2605
2755
  const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
2606
2756
  const localKey = relation.localKey || defaultLocalKey;
2607
2757
  switch (relation.type) {
@@ -2609,12 +2759,12 @@ var baseRelationCondition = (root, relation) => {
2609
2759
  case RelationKinds.HasOne:
2610
2760
  return eq(
2611
2761
  { type: "Column", table: relation.target.name, name: relation.foreignKey },
2612
- { type: "Column", table: root.name, name: localKey }
2762
+ { type: "Column", table: rootTable, name: localKey }
2613
2763
  );
2614
2764
  case RelationKinds.BelongsTo:
2615
2765
  return eq(
2616
2766
  { type: "Column", table: relation.target.name, name: localKey },
2617
- { type: "Column", table: root.name, name: relation.foreignKey }
2767
+ { type: "Column", table: rootTable, name: relation.foreignKey }
2618
2768
  );
2619
2769
  case RelationKinds.BelongsToMany:
2620
2770
  throw new Error("BelongsToMany relations do not support the standard join condition builder");
@@ -2622,12 +2772,13 @@ var baseRelationCondition = (root, relation) => {
2622
2772
  return assertNever(relation);
2623
2773
  }
2624
2774
  };
2625
- var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) => {
2775
+ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias) => {
2626
2776
  const rootKey = relation.localKey || findPrimaryKey(root);
2627
2777
  const targetKey = relation.targetKey || findPrimaryKey(relation.target);
2778
+ const rootTable = rootAlias || root.name;
2628
2779
  const pivotCondition = eq(
2629
2780
  { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToRoot },
2630
- { type: "Column", table: root.name, name: rootKey }
2781
+ { type: "Column", table: rootTable, name: rootKey }
2631
2782
  );
2632
2783
  const pivotJoin = createJoinNode(joinKind, relation.pivotTable.name, pivotCondition);
2633
2784
  let targetCondition = eq(
@@ -2645,12 +2796,12 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) =>
2645
2796
  );
2646
2797
  return [pivotJoin, targetJoin];
2647
2798
  };
2648
- var buildRelationJoinCondition = (root, relation, extra) => {
2649
- const base = baseRelationCondition(root, relation);
2799
+ var buildRelationJoinCondition = (root, relation, extra, rootAlias) => {
2800
+ const base = baseRelationCondition(root, relation, rootAlias);
2650
2801
  return extra ? and(base, extra) : base;
2651
2802
  };
2652
- var buildRelationCorrelation = (root, relation) => {
2653
- return baseRelationCondition(root, relation);
2803
+ var buildRelationCorrelation = (root, relation, rootAlias) => {
2804
+ return baseRelationCondition(root, relation, rootAlias);
2654
2805
  };
2655
2806
 
2656
2807
  // src/core/ast/join-metadata.ts
@@ -2694,7 +2845,7 @@ var RelationService = class {
2694
2845
  match(relationName, predicate) {
2695
2846
  const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
2696
2847
  const pk = findPrimaryKey(this.table);
2697
- const distinctCols = [{ type: "Column", table: this.table.name, name: pk }];
2848
+ const distinctCols = [{ type: "Column", table: this.rootTableName(), name: pk }];
2698
2849
  const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
2699
2850
  const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
2700
2851
  return { state: nextState, hydration: joined.hydration };
@@ -2781,9 +2932,13 @@ var RelationService = class {
2781
2932
  * @param ast - Query AST to modify
2782
2933
  * @returns Modified query AST with relation correlation
2783
2934
  */
2784
- applyRelationCorrelation(relationName, ast) {
2935
+ applyRelationCorrelation(relationName, ast, additionalCorrelation) {
2785
2936
  const relation = this.getRelation(relationName);
2786
- const correlation = buildRelationCorrelation(this.table, relation);
2937
+ const rootAlias = this.state.ast.from.type === "Table" ? this.state.ast.from.alias : void 0;
2938
+ let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
2939
+ if (additionalCorrelation) {
2940
+ correlation = and(correlation, additionalCorrelation);
2941
+ }
2787
2942
  const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
2788
2943
  return {
2789
2944
  ...ast,
@@ -2800,17 +2955,19 @@ var RelationService = class {
2800
2955
  */
2801
2956
  withJoin(state, relationName, joinKind, extraCondition) {
2802
2957
  const relation = this.getRelation(relationName);
2958
+ const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
2803
2959
  if (relation.type === RelationKinds.BelongsToMany) {
2804
2960
  const joins = buildBelongsToManyJoins(
2805
2961
  this.table,
2806
2962
  relationName,
2807
2963
  relation,
2808
2964
  joinKind,
2809
- extraCondition
2965
+ extraCondition,
2966
+ rootAlias
2810
2967
  );
2811
2968
  return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
2812
2969
  }
2813
- const condition = buildRelationJoinCondition(this.table, relation, extraCondition);
2970
+ const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
2814
2971
  const joinNode = createJoinNode(joinKind, relation.target.name, condition, relationName);
2815
2972
  return this.astService(state).withJoin(joinNode);
2816
2973
  }
@@ -2849,6 +3006,11 @@ var RelationService = class {
2849
3006
  astService(state = this.state) {
2850
3007
  return this.createQueryAstService(this.table, state);
2851
3008
  }
3009
+ rootTableName() {
3010
+ const from = this.state.ast.from;
3011
+ if (from.type === "Table" && from.alias) return from.alias;
3012
+ return this.table.name;
3013
+ }
2852
3014
  };
2853
3015
 
2854
3016
  // src/query-builder/select-query-builder-deps.ts
@@ -2923,7 +3085,9 @@ var ColumnSelector = class {
2923
3085
  * @returns Updated query context with DISTINCT clause
2924
3086
  */
2925
3087
  distinct(context, columns) {
2926
- const nodes = columns.map((col) => buildColumnNode(this.env.table, col));
3088
+ const from = context.state.ast.from;
3089
+ const tableRef = from.type === "Table" && from.alias ? { ...this.env.table, alias: from.alias } : this.env.table;
3090
+ const nodes = columns.map((col) => buildColumnNode(tableRef, col));
2927
3091
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
2928
3092
  const nextState = astService.withDistinct(nodes);
2929
3093
  return { state: nextState, hydration: context.hydration };
@@ -2980,8 +3144,8 @@ var RelationManager = class {
2980
3144
  * @param ast - Query AST to modify
2981
3145
  * @returns Modified query AST with relation correlation
2982
3146
  */
2983
- applyRelationCorrelation(context, relationName, ast) {
2984
- return this.createService(context).applyRelationCorrelation(relationName, ast);
3147
+ applyRelationCorrelation(context, relationName, ast, additionalCorrelation) {
3148
+ return this.createService(context).applyRelationCorrelation(relationName, ast, additionalCorrelation);
2985
3149
  }
2986
3150
  /**
2987
3151
  * Creates a relation service instance
@@ -4028,9 +4192,30 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4028
4192
  clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
4029
4193
  return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
4030
4194
  }
4195
+ /**
4196
+ * Applies an alias to the root FROM table.
4197
+ * @param alias - Alias to apply
4198
+ */
4199
+ as(alias) {
4200
+ const from = this.context.state.ast.from;
4201
+ if (from.type !== "Table") {
4202
+ throw new Error("Cannot alias non-table FROM sources");
4203
+ }
4204
+ const nextFrom = { ...from, alias };
4205
+ const nextContext = this.applyAst(this.context, (service) => service.withFrom(nextFrom));
4206
+ return this.clone(nextContext);
4207
+ }
4031
4208
  resolveQueryNode(query) {
4032
4209
  return typeof query.getAST === "function" ? query.getAST() : query;
4033
4210
  }
4211
+ applyCorrelation(ast, correlation) {
4212
+ if (!correlation) return ast;
4213
+ const combinedWhere = ast.where ? and(correlation, ast.where) : correlation;
4214
+ return {
4215
+ ...ast,
4216
+ where: combinedWhere
4217
+ };
4218
+ }
4034
4219
  createChildBuilder(table) {
4035
4220
  return new _SelectQueryBuilder(table, void 0, void 0, this.env.deps);
4036
4221
  }
@@ -4059,6 +4244,21 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4059
4244
  select(columns) {
4060
4245
  return this.clone(this.columnSelector.select(this.context, columns));
4061
4246
  }
4247
+ /**
4248
+ * Selects columns from the root table by name (typed).
4249
+ * @param cols - Column names on the root table
4250
+ */
4251
+ selectColumns(...cols) {
4252
+ const selection = {};
4253
+ for (const key of cols) {
4254
+ const col = this.env.table.columns[key];
4255
+ if (!col) {
4256
+ throw new Error(`Column '${key}' not found on table '${this.env.table.name}'`);
4257
+ }
4258
+ selection[key] = col;
4259
+ }
4260
+ return this.select(selection);
4261
+ }
4062
4262
  /**
4063
4263
 
4064
4264
  * Selects raw column expressions
@@ -4107,6 +4307,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4107
4307
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
4108
4308
  return this.clone(nextContext);
4109
4309
  }
4310
+ /**
4311
+ * Replaces the FROM clause with a derived table (subquery with alias)
4312
+ * @param subquery - Subquery to use as the FROM source
4313
+ * @param alias - Alias for the derived table
4314
+ * @param columnAliases - Optional column alias list
4315
+ * @returns New query builder instance with updated FROM
4316
+ */
4317
+ fromSubquery(subquery, alias, columnAliases) {
4318
+ const subAst = this.resolveQueryNode(subquery);
4319
+ const fromNode = derivedTable(subAst, alias, columnAliases);
4320
+ const nextContext = this.applyAst(this.context, (service) => service.withFrom(fromNode));
4321
+ return this.clone(nextContext);
4322
+ }
4110
4323
  /**
4111
4324
 
4112
4325
  * Selects a subquery as a column
@@ -4122,6 +4335,21 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4122
4335
  const query = this.resolveQueryNode(sub);
4123
4336
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
4124
4337
  }
4338
+ /**
4339
+ * Adds a JOIN against a derived table (subquery with alias)
4340
+ * @param subquery - Subquery to join
4341
+ * @param alias - Alias for the derived table
4342
+ * @param condition - Join condition expression
4343
+ * @param joinKind - Join kind (defaults to INNER)
4344
+ * @param columnAliases - Optional column alias list for the derived table
4345
+ * @returns New query builder instance with the derived-table join
4346
+ */
4347
+ joinSubquery(subquery, alias, condition, joinKind = JOIN_KINDS.INNER, columnAliases) {
4348
+ const subAst = this.resolveQueryNode(subquery);
4349
+ const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
4350
+ const nextContext = this.applyAst(this.context, (service) => service.withJoin(joinNode));
4351
+ return this.clone(nextContext);
4352
+ }
4125
4353
  /**
4126
4354
 
4127
4355
  * Adds an INNER JOIN to the query
@@ -4219,6 +4447,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4219
4447
  nextLazy.add(relationName);
4220
4448
  return this.clone(this.context, nextLazy);
4221
4449
  }
4450
+ /**
4451
+ * Selects columns for a related table in a single hop.
4452
+ */
4453
+ selectRelationColumns(relationName, ...cols) {
4454
+ const relation = this.env.table.relations[relationName];
4455
+ if (!relation) {
4456
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4457
+ }
4458
+ const target = relation.target;
4459
+ for (const col of cols) {
4460
+ if (!target.columns[col]) {
4461
+ throw new Error(
4462
+ `Column '${col}' not found on related table '${target.name}' for relation '${relationName}'`
4463
+ );
4464
+ }
4465
+ }
4466
+ return this.include(relationName, { columns: cols });
4467
+ }
4468
+ /**
4469
+ * Convenience alias for selecting specific columns from a relation.
4470
+ */
4471
+ includePick(relationName, cols) {
4472
+ return this.selectRelationColumns(relationName, ...cols);
4473
+ }
4474
+ /**
4475
+ * Selects columns for the root table and relations from a single config object.
4476
+ */
4477
+ selectColumnsDeep(config) {
4478
+ let qb = this;
4479
+ if (config.root?.length) {
4480
+ qb = qb.selectColumns(...config.root);
4481
+ }
4482
+ for (const key of Object.keys(config)) {
4483
+ if (key === "root") continue;
4484
+ const relName = key;
4485
+ const cols = config[relName];
4486
+ if (!cols || !cols.length) continue;
4487
+ qb = qb.selectRelationColumns(relName, ...cols);
4488
+ }
4489
+ return qb;
4490
+ }
4222
4491
  getLazyRelations() {
4223
4492
  return Array.from(this.lazyRelations);
4224
4493
  }
@@ -4380,9 +4649,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4380
4649
  * @returns New query builder instance with the WHERE EXISTS condition
4381
4650
 
4382
4651
  */
4383
- whereExists(subquery) {
4652
+ whereExists(subquery, correlate) {
4384
4653
  const subAst = this.resolveQueryNode(subquery);
4385
- return this.where(exists(subAst));
4654
+ const correlated = this.applyCorrelation(subAst, correlate);
4655
+ return this.where(exists(correlated));
4386
4656
  }
4387
4657
  /**
4388
4658
 
@@ -4393,9 +4663,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4393
4663
  * @returns New query builder instance with the WHERE NOT EXISTS condition
4394
4664
 
4395
4665
  */
4396
- whereNotExists(subquery) {
4666
+ whereNotExists(subquery, correlate) {
4397
4667
  const subAst = this.resolveQueryNode(subquery);
4398
- return this.where(notExists(subAst));
4668
+ const correlated = this.applyCorrelation(subAst, correlate);
4669
+ return this.where(notExists(correlated));
4399
4670
  }
4400
4671
  /**
4401
4672
 
@@ -4408,17 +4679,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4408
4679
  * @returns New query builder instance with the relationship existence check
4409
4680
 
4410
4681
  */
4411
- whereHas(relationName, callback) {
4682
+ whereHas(relationName, callbackOrOptions, maybeOptions) {
4412
4683
  const relation = this.env.table.relations[relationName];
4413
4684
  if (!relation) {
4414
4685
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4415
4686
  }
4687
+ const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
4688
+ const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
4416
4689
  let subQb = this.createChildBuilder(relation.target);
4417
4690
  if (callback) {
4418
4691
  subQb = callback(subQb);
4419
4692
  }
4420
4693
  const subAst = subQb.getAST();
4421
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
4694
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
4422
4695
  return this.where(exists(finalSubAst));
4423
4696
  }
4424
4697
  /**
@@ -4432,17 +4705,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4432
4705
  * @returns New query builder instance with the relationship non-existence check
4433
4706
 
4434
4707
  */
4435
- whereHasNot(relationName, callback) {
4708
+ whereHasNot(relationName, callbackOrOptions, maybeOptions) {
4436
4709
  const relation = this.env.table.relations[relationName];
4437
4710
  if (!relation) {
4438
4711
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4439
4712
  }
4713
+ const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
4714
+ const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
4440
4715
  let subQb = this.createChildBuilder(relation.target);
4441
4716
  if (callback) {
4442
4717
  subQb = callback(subQb);
4443
4718
  }
4444
4719
  const subAst = subQb.getAST();
4445
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
4720
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
4446
4721
  return this.where(notExists(finalSubAst));
4447
4722
  }
4448
4723
  /**