metal-orm 1.0.17 → 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 (39) hide show
  1. package/README.md +4 -3
  2. package/dist/decorators/index.cjs +192 -46
  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 +192 -46
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +245 -66
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +16 -29
  11. package/dist/index.d.ts +16 -29
  12. package/dist/index.js +243 -66
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-BPCn6MOH.d.cts → select-BuMpVcVt.d.cts} +83 -11
  15. package/dist/{select-BPCn6MOH.d.ts → select-BuMpVcVt.d.ts} +83 -11
  16. package/package.json +4 -1
  17. package/src/codegen/naming-strategy.ts +15 -10
  18. package/src/core/ast/builders.ts +23 -3
  19. package/src/core/ast/expression-builders.ts +14 -1
  20. package/src/core/ast/expression-nodes.ts +11 -9
  21. package/src/core/ast/join-node.ts +5 -3
  22. package/src/core/ast/join.ts +16 -16
  23. package/src/core/ast/query.ts +44 -29
  24. package/src/core/ddl/dialects/mssql-schema-dialect.ts +18 -0
  25. package/src/core/ddl/dialects/mysql-schema-dialect.ts +11 -0
  26. package/src/core/ddl/dialects/postgres-schema-dialect.ts +9 -0
  27. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
  28. package/src/core/dialect/base/sql-dialect.ts +58 -46
  29. package/src/core/dialect/mssql/index.ts +53 -28
  30. package/src/core/dialect/sqlite/index.ts +22 -13
  31. package/src/query-builder/column-selector.ts +9 -7
  32. package/src/query-builder/query-ast-service.ts +59 -38
  33. package/src/query-builder/relation-conditions.ts +38 -34
  34. package/src/query-builder/relation-manager.ts +8 -3
  35. package/src/query-builder/relation-service.ts +59 -46
  36. package/src/query-builder/select-query-state.ts +19 -7
  37. package/src/query-builder/select.ts +215 -135
  38. package/src/schema/column.ts +75 -39
  39. package/src/schema/types.ts +1 -0
package/README.md CHANGED
@@ -105,9 +105,10 @@ If you like explicit model classes, you can add a thin decorator layer on top of
105
105
  - `@HasMany({ target, foreignKey, ... })`
106
106
  - `@HasOne({ target, foreignKey, ... })`
107
107
  - `@BelongsTo({ target, foreignKey, ... })`
108
- - `@BelongsToMany({ target, pivotTable, ... })`
109
- - `bootstrapEntities()` scans metadata, builds `TableDef`s, wires relations with the same `hasOne` / `hasMany` / `belongsTo` / `belongsToMany` helpers you would use manually, and returns the resulting tables.
110
- - `selectFromEntity(MyEntity)` lets you start a `SelectQueryBuilder` directly from the class.
108
+ - `@BelongsToMany({ target, pivotTable, ... })`
109
+ - `bootstrapEntities()` scans metadata, builds `TableDef`s, wires relations with the same `hasOne` / `hasMany` / `belongsTo` / `belongsToMany` helpers you would use manually, and returns the resulting tables.
110
+ - `selectFromEntity(MyEntity)` lets you start a `SelectQueryBuilder` directly from the class.
111
+ - **Generate entities from an existing DB**: `node scripts/generate-entities.mjs --dialect=postgres --url=$DATABASE_URL --schema=public --out=src/entities.ts` introspects your schema and spits out `@Entity` / `@Column` classes you can immediately `bootstrapEntities()` with.
111
112
 
112
113
  You don’t have to use decorators, but when you do, you’re still on the same AST + dialect + runtime foundation.
113
114
 
@@ -424,6 +424,26 @@ var avg = buildAggregate("AVG");
424
424
  var min = buildAggregate("MIN");
425
425
  var max = buildAggregate("MAX");
426
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
+ });
446
+
427
447
  // src/core/functions/standard-strategy.ts
428
448
  var StandardFunctionStrategy = class _StandardFunctionStrategy {
429
449
  constructor() {
@@ -1113,7 +1133,7 @@ var SqlDialectBase = class extends Dialect {
1113
1133
  return this.returningStrategy.compileReturning(returning, ctx);
1114
1134
  }
1115
1135
  compileInsertColumnList(columns) {
1116
- return columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
1136
+ return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
1117
1137
  }
1118
1138
  compileInsertValues(values, ctx) {
1119
1139
  return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
@@ -1139,7 +1159,7 @@ var SqlDialectBase = class extends Dialect {
1139
1159
  compileUpdateAssignments(assignments, ctx) {
1140
1160
  return assignments.map((assignment) => {
1141
1161
  const col = assignment.column;
1142
- const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
1162
+ const target = this.quoteIdentifier(col.name);
1143
1163
  const value = this.compileOperand(assignment.value, ctx);
1144
1164
  return `${target} = ${value}`;
1145
1165
  }).join(", ");
@@ -1171,12 +1191,29 @@ var SqlDialectBase = class extends Dialect {
1171
1191
  if (tableSource.type === "FunctionTable") {
1172
1192
  return this.compileFunctionTable(tableSource, ctx);
1173
1193
  }
1194
+ if (tableSource.type === "DerivedTable") {
1195
+ return this.compileDerivedTable(tableSource, ctx);
1196
+ }
1174
1197
  return this.compileTableSource(tableSource);
1175
1198
  }
1176
1199
  compileFunctionTable(fn, ctx) {
1177
1200
  return FunctionTableFormatter.format(fn, ctx, this);
1178
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
+ }
1179
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
+ }
1180
1217
  const base = this.compileTableName(table);
1181
1218
  return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
1182
1219
  }
@@ -1565,6 +1602,12 @@ var SqliteDialect = class extends SqlDialectBase {
1565
1602
  const columns = this.formatReturningColumns(returning);
1566
1603
  return ` RETURNING ${columns}`;
1567
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
+ }
1568
1611
  supportsReturning() {
1569
1612
  return true;
1570
1613
  }
@@ -1731,6 +1774,9 @@ var SqlServerDialect = class extends Dialect {
1731
1774
  return `UPDATE ${table} SET ${assignments}${whereClause};`;
1732
1775
  }
1733
1776
  compileDeleteAst(ast, ctx) {
1777
+ if (ast.from.type !== "Table") {
1778
+ throw new Error("DELETE only supports base tables in the MSSQL dialect.");
1779
+ }
1734
1780
  const table = this.quoteIdentifier(ast.from.name);
1735
1781
  const whereClause = this.compileWhere(ast.where, ctx);
1736
1782
  return `DELETE FROM ${table}${whereClause};`;
@@ -1754,9 +1800,9 @@ var SqlServerDialect = class extends Dialect {
1754
1800
  return expr;
1755
1801
  }).join(", ");
1756
1802
  const distinct = ast.distinct ? "DISTINCT " : "";
1757
- const from = `${this.quoteIdentifier(ast.from.name)}`;
1803
+ const from = this.compileTableSource(ast.from, ctx);
1758
1804
  const joins = ast.joins.map((j) => {
1759
- const table = this.quoteIdentifier(j.table.name);
1805
+ const table = this.compileTableSource(j.table, ctx);
1760
1806
  const cond = this.compileExpression(j.condition, ctx);
1761
1807
  return `${j.kind} JOIN ${table} ON ${cond}`;
1762
1808
  }).join(" ");
@@ -1786,6 +1832,21 @@ var SqlServerDialect = class extends Dialect {
1786
1832
  }
1787
1833
  return pagination;
1788
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
+ }
1789
1850
  compileCtes(ast, ctx) {
1790
1851
  if (!ast.ctes || ast.ctes.length === 0) return "";
1791
1852
  const defs = ast.ctes.map((cte) => {
@@ -1914,6 +1975,17 @@ var SelectQueryState = class _SelectQueryState {
1914
1975
  joins: [...this.ast.joins ?? [], join]
1915
1976
  });
1916
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
+ }
1917
1989
  /**
1918
1990
  * Adds a WHERE clause to the query
1919
1991
  * @param predicate - WHERE predicate expression
@@ -2406,19 +2478,6 @@ var buildDefaultHydrationPlan = (table) => ({
2406
2478
  relations: []
2407
2479
  });
2408
2480
 
2409
- // src/core/ast/builders.ts
2410
- var buildColumnNode = (table, column) => {
2411
- if (column.type === "Column") {
2412
- return column;
2413
- }
2414
- const def = column;
2415
- return {
2416
- type: "Column",
2417
- table: def.table || table.name,
2418
- name: def.name
2419
- };
2420
- };
2421
-
2422
2481
  // src/query-builder/raw-column-parser.ts
2423
2482
  var parseRawColumn = (col, tableName, ctes) => {
2424
2483
  if (col.includes("(")) {
@@ -2458,6 +2517,8 @@ var QueryAstService = class {
2458
2517
  const existingAliases = new Set(
2459
2518
  this.state.ast.columns.map((c) => c.alias || c.name)
2460
2519
  );
2520
+ const from = this.state.ast.from;
2521
+ const rootTableName = from.type === "Table" && from.alias ? from.alias : this.table.name;
2461
2522
  const newCols = Object.entries(columns).reduce((acc, [alias, val]) => {
2462
2523
  if (existingAliases.has(alias)) return acc;
2463
2524
  if (isExpressionSelectionNode(val)) {
@@ -2465,9 +2526,10 @@ var QueryAstService = class {
2465
2526
  return acc;
2466
2527
  }
2467
2528
  const colDef = val;
2529
+ const resolvedTable = colDef.table && colDef.table === this.table.name && from.type === "Table" && from.alias ? from.alias : colDef.table || rootTableName;
2468
2530
  acc.push({
2469
2531
  type: "Column",
2470
- table: colDef.table || this.table.name,
2532
+ table: resolvedTable,
2471
2533
  name: colDef.name,
2472
2534
  alias
2473
2535
  });
@@ -2482,7 +2544,9 @@ var QueryAstService = class {
2482
2544
  * @returns Column selection result with updated state and added columns
2483
2545
  */
2484
2546
  selectRaw(cols) {
2485
- 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));
2486
2550
  const nextState = this.state.withColumns(newCols);
2487
2551
  return { state: nextState, addedColumns: newCols };
2488
2552
  }
@@ -2518,6 +2582,14 @@ var QueryAstService = class {
2518
2582
  };
2519
2583
  return this.state.withSetOperation(op);
2520
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
+ }
2521
2593
  /**
2522
2594
  * Selects a subquery as a column
2523
2595
  * @param alias - Alias for the subquery
@@ -2551,7 +2623,9 @@ var QueryAstService = class {
2551
2623
  * @returns Updated query state with GROUP BY clause
2552
2624
  */
2553
2625
  withGroupBy(col) {
2554
- 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);
2555
2629
  return this.state.withGroupBy([node]);
2556
2630
  }
2557
2631
  /**
@@ -2570,7 +2644,9 @@ var QueryAstService = class {
2570
2644
  * @returns Updated query state with ORDER BY clause
2571
2645
  */
2572
2646
  withOrderBy(col, direction) {
2573
- 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);
2574
2650
  return this.state.withOrderBy([{ type: "OrderBy", column: node, direction }]);
2575
2651
  }
2576
2652
  /**
@@ -2674,7 +2750,8 @@ var RelationProjectionHelper = class {
2674
2750
  var assertNever = (value) => {
2675
2751
  throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
2676
2752
  };
2677
- var baseRelationCondition = (root, relation) => {
2753
+ var baseRelationCondition = (root, relation, rootAlias) => {
2754
+ const rootTable = rootAlias || root.name;
2678
2755
  const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
2679
2756
  const localKey = relation.localKey || defaultLocalKey;
2680
2757
  switch (relation.type) {
@@ -2682,12 +2759,12 @@ var baseRelationCondition = (root, relation) => {
2682
2759
  case RelationKinds.HasOne:
2683
2760
  return eq(
2684
2761
  { type: "Column", table: relation.target.name, name: relation.foreignKey },
2685
- { type: "Column", table: root.name, name: localKey }
2762
+ { type: "Column", table: rootTable, name: localKey }
2686
2763
  );
2687
2764
  case RelationKinds.BelongsTo:
2688
2765
  return eq(
2689
2766
  { type: "Column", table: relation.target.name, name: localKey },
2690
- { type: "Column", table: root.name, name: relation.foreignKey }
2767
+ { type: "Column", table: rootTable, name: relation.foreignKey }
2691
2768
  );
2692
2769
  case RelationKinds.BelongsToMany:
2693
2770
  throw new Error("BelongsToMany relations do not support the standard join condition builder");
@@ -2695,12 +2772,13 @@ var baseRelationCondition = (root, relation) => {
2695
2772
  return assertNever(relation);
2696
2773
  }
2697
2774
  };
2698
- var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) => {
2775
+ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias) => {
2699
2776
  const rootKey = relation.localKey || findPrimaryKey(root);
2700
2777
  const targetKey = relation.targetKey || findPrimaryKey(relation.target);
2778
+ const rootTable = rootAlias || root.name;
2701
2779
  const pivotCondition = eq(
2702
2780
  { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToRoot },
2703
- { type: "Column", table: root.name, name: rootKey }
2781
+ { type: "Column", table: rootTable, name: rootKey }
2704
2782
  );
2705
2783
  const pivotJoin = createJoinNode(joinKind, relation.pivotTable.name, pivotCondition);
2706
2784
  let targetCondition = eq(
@@ -2718,12 +2796,12 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra) =>
2718
2796
  );
2719
2797
  return [pivotJoin, targetJoin];
2720
2798
  };
2721
- var buildRelationJoinCondition = (root, relation, extra) => {
2722
- const base = baseRelationCondition(root, relation);
2799
+ var buildRelationJoinCondition = (root, relation, extra, rootAlias) => {
2800
+ const base = baseRelationCondition(root, relation, rootAlias);
2723
2801
  return extra ? and(base, extra) : base;
2724
2802
  };
2725
- var buildRelationCorrelation = (root, relation) => {
2726
- return baseRelationCondition(root, relation);
2803
+ var buildRelationCorrelation = (root, relation, rootAlias) => {
2804
+ return baseRelationCondition(root, relation, rootAlias);
2727
2805
  };
2728
2806
 
2729
2807
  // src/core/ast/join-metadata.ts
@@ -2767,7 +2845,7 @@ var RelationService = class {
2767
2845
  match(relationName, predicate) {
2768
2846
  const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
2769
2847
  const pk = findPrimaryKey(this.table);
2770
- const distinctCols = [{ type: "Column", table: this.table.name, name: pk }];
2848
+ const distinctCols = [{ type: "Column", table: this.rootTableName(), name: pk }];
2771
2849
  const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
2772
2850
  const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
2773
2851
  return { state: nextState, hydration: joined.hydration };
@@ -2854,9 +2932,13 @@ var RelationService = class {
2854
2932
  * @param ast - Query AST to modify
2855
2933
  * @returns Modified query AST with relation correlation
2856
2934
  */
2857
- applyRelationCorrelation(relationName, ast) {
2935
+ applyRelationCorrelation(relationName, ast, additionalCorrelation) {
2858
2936
  const relation = this.getRelation(relationName);
2859
- 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
+ }
2860
2942
  const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
2861
2943
  return {
2862
2944
  ...ast,
@@ -2873,17 +2955,19 @@ var RelationService = class {
2873
2955
  */
2874
2956
  withJoin(state, relationName, joinKind, extraCondition) {
2875
2957
  const relation = this.getRelation(relationName);
2958
+ const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
2876
2959
  if (relation.type === RelationKinds.BelongsToMany) {
2877
2960
  const joins = buildBelongsToManyJoins(
2878
2961
  this.table,
2879
2962
  relationName,
2880
2963
  relation,
2881
2964
  joinKind,
2882
- extraCondition
2965
+ extraCondition,
2966
+ rootAlias
2883
2967
  );
2884
2968
  return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
2885
2969
  }
2886
- const condition = buildRelationJoinCondition(this.table, relation, extraCondition);
2970
+ const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
2887
2971
  const joinNode = createJoinNode(joinKind, relation.target.name, condition, relationName);
2888
2972
  return this.astService(state).withJoin(joinNode);
2889
2973
  }
@@ -2922,6 +3006,11 @@ var RelationService = class {
2922
3006
  astService(state = this.state) {
2923
3007
  return this.createQueryAstService(this.table, state);
2924
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
+ }
2925
3014
  };
2926
3015
 
2927
3016
  // src/query-builder/select-query-builder-deps.ts
@@ -2996,7 +3085,9 @@ var ColumnSelector = class {
2996
3085
  * @returns Updated query context with DISTINCT clause
2997
3086
  */
2998
3087
  distinct(context, columns) {
2999
- 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));
3000
3091
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
3001
3092
  const nextState = astService.withDistinct(nodes);
3002
3093
  return { state: nextState, hydration: context.hydration };
@@ -3053,8 +3144,8 @@ var RelationManager = class {
3053
3144
  * @param ast - Query AST to modify
3054
3145
  * @returns Modified query AST with relation correlation
3055
3146
  */
3056
- applyRelationCorrelation(context, relationName, ast) {
3057
- return this.createService(context).applyRelationCorrelation(relationName, ast);
3147
+ applyRelationCorrelation(context, relationName, ast, additionalCorrelation) {
3148
+ return this.createService(context).applyRelationCorrelation(relationName, ast, additionalCorrelation);
3058
3149
  }
3059
3150
  /**
3060
3151
  * Creates a relation service instance
@@ -4101,9 +4192,30 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4101
4192
  clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
4102
4193
  return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
4103
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
+ }
4104
4208
  resolveQueryNode(query) {
4105
4209
  return typeof query.getAST === "function" ? query.getAST() : query;
4106
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
+ }
4107
4219
  createChildBuilder(table) {
4108
4220
  return new _SelectQueryBuilder(table, void 0, void 0, this.env.deps);
4109
4221
  }
@@ -4195,6 +4307,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4195
4307
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
4196
4308
  return this.clone(nextContext);
4197
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
+ }
4198
4323
  /**
4199
4324
 
4200
4325
  * Selects a subquery as a column
@@ -4210,6 +4335,21 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4210
4335
  const query = this.resolveQueryNode(sub);
4211
4336
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
4212
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
+ }
4213
4353
  /**
4214
4354
 
4215
4355
  * Adds an INNER JOIN to the query
@@ -4509,9 +4649,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4509
4649
  * @returns New query builder instance with the WHERE EXISTS condition
4510
4650
 
4511
4651
  */
4512
- whereExists(subquery) {
4652
+ whereExists(subquery, correlate) {
4513
4653
  const subAst = this.resolveQueryNode(subquery);
4514
- return this.where(exists(subAst));
4654
+ const correlated = this.applyCorrelation(subAst, correlate);
4655
+ return this.where(exists(correlated));
4515
4656
  }
4516
4657
  /**
4517
4658
 
@@ -4522,9 +4663,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4522
4663
  * @returns New query builder instance with the WHERE NOT EXISTS condition
4523
4664
 
4524
4665
  */
4525
- whereNotExists(subquery) {
4666
+ whereNotExists(subquery, correlate) {
4526
4667
  const subAst = this.resolveQueryNode(subquery);
4527
- return this.where(notExists(subAst));
4668
+ const correlated = this.applyCorrelation(subAst, correlate);
4669
+ return this.where(notExists(correlated));
4528
4670
  }
4529
4671
  /**
4530
4672
 
@@ -4537,17 +4679,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4537
4679
  * @returns New query builder instance with the relationship existence check
4538
4680
 
4539
4681
  */
4540
- whereHas(relationName, callback) {
4682
+ whereHas(relationName, callbackOrOptions, maybeOptions) {
4541
4683
  const relation = this.env.table.relations[relationName];
4542
4684
  if (!relation) {
4543
4685
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4544
4686
  }
4687
+ const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
4688
+ const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
4545
4689
  let subQb = this.createChildBuilder(relation.target);
4546
4690
  if (callback) {
4547
4691
  subQb = callback(subQb);
4548
4692
  }
4549
4693
  const subAst = subQb.getAST();
4550
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
4694
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
4551
4695
  return this.where(exists(finalSubAst));
4552
4696
  }
4553
4697
  /**
@@ -4561,17 +4705,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4561
4705
  * @returns New query builder instance with the relationship non-existence check
4562
4706
 
4563
4707
  */
4564
- whereHasNot(relationName, callback) {
4708
+ whereHasNot(relationName, callbackOrOptions, maybeOptions) {
4565
4709
  const relation = this.env.table.relations[relationName];
4566
4710
  if (!relation) {
4567
4711
  throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4568
4712
  }
4713
+ const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
4714
+ const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
4569
4715
  let subQb = this.createChildBuilder(relation.target);
4570
4716
  if (callback) {
4571
4717
  subQb = callback(subQb);
4572
4718
  }
4573
4719
  const subAst = subQb.getAST();
4574
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst);
4720
+ const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
4575
4721
  return this.where(notExists(finalSubAst));
4576
4722
  }
4577
4723
  /**