metal-orm 1.0.58 → 1.0.59

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 (40) hide show
  1. package/README.md +34 -31
  2. package/dist/index.cjs +1463 -1003
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +148 -129
  5. package/dist/index.d.ts +148 -129
  6. package/dist/index.js +1459 -1003
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/core/ddl/schema-generator.ts +44 -1
  10. package/src/decorators/bootstrap.ts +183 -146
  11. package/src/decorators/column-decorator.ts +8 -49
  12. package/src/decorators/decorator-metadata.ts +10 -46
  13. package/src/decorators/entity.ts +30 -40
  14. package/src/decorators/relations.ts +30 -56
  15. package/src/orm/entity-hydration.ts +72 -0
  16. package/src/orm/entity-meta.ts +13 -11
  17. package/src/orm/entity-metadata.ts +240 -238
  18. package/src/orm/entity-relation-cache.ts +39 -0
  19. package/src/orm/entity-relations.ts +207 -0
  20. package/src/orm/entity.ts +124 -410
  21. package/src/orm/execute.ts +4 -4
  22. package/src/orm/lazy-batch/belongs-to-many.ts +134 -0
  23. package/src/orm/lazy-batch/belongs-to.ts +108 -0
  24. package/src/orm/lazy-batch/has-many.ts +69 -0
  25. package/src/orm/lazy-batch/has-one.ts +68 -0
  26. package/src/orm/lazy-batch/shared.ts +125 -0
  27. package/src/orm/lazy-batch.ts +4 -492
  28. package/src/orm/relations/many-to-many.ts +2 -1
  29. package/src/query-builder/relation-cte-builder.ts +63 -0
  30. package/src/query-builder/relation-filter-utils.ts +159 -0
  31. package/src/query-builder/relation-include-strategies.ts +177 -0
  32. package/src/query-builder/relation-join-planner.ts +80 -0
  33. package/src/query-builder/relation-service.ts +119 -479
  34. package/src/query-builder/relation-types.ts +41 -10
  35. package/src/query-builder/select/projection-facet.ts +23 -23
  36. package/src/query-builder/select/select-operations.ts +145 -0
  37. package/src/query-builder/select.ts +351 -422
  38. package/src/schema/relation.ts +22 -18
  39. package/src/schema/table.ts +22 -9
  40. package/src/schema/types.ts +14 -12
package/dist/index.cjs CHANGED
@@ -143,6 +143,8 @@ __export(index_exports, {
143
143
  esel: () => esel,
144
144
  executeHydrated: () => executeHydrated,
145
145
  executeHydratedWithContexts: () => executeHydratedWithContexts,
146
+ executeSchemaSql: () => executeSchemaSql,
147
+ executeSchemaSqlFor: () => executeSchemaSqlFor,
146
148
  exists: () => exists,
147
149
  exp: () => exp,
148
150
  extract: () => extract,
@@ -151,6 +153,7 @@ __export(index_exports, {
151
153
  fromUnixTime: () => fromUnixTime,
152
154
  generateCreateTableSql: () => generateCreateTableSql,
153
155
  generateSchemaSql: () => generateSchemaSql,
156
+ generateSchemaSqlFor: () => generateSchemaSqlFor,
154
157
  getColumn: () => getColumn,
155
158
  getDecoratorMetadata: () => getDecoratorMetadata,
156
159
  getSchemaIntrospector: () => getSchemaIntrospector,
@@ -256,6 +259,7 @@ __export(index_exports, {
256
259
  second: () => second,
257
260
  sel: () => sel,
258
261
  selectFromEntity: () => selectFromEntity,
262
+ setRelations: () => setRelations,
259
263
  sha1: () => sha1,
260
264
  sha2: () => sha2,
261
265
  shiftLeft: () => shiftLeft,
@@ -311,6 +315,9 @@ var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
311
315
  collation: options.collation
312
316
  };
313
317
  };
318
+ function setRelations(table, relations) {
319
+ table.relations = relations;
320
+ }
314
321
  var TABLE_REF_CACHE = /* @__PURE__ */ new WeakMap();
315
322
  var withColumnProps = (table) => {
316
323
  const cached = TABLE_REF_CACHE.get(table);
@@ -954,6 +961,7 @@ var variance = buildAggregate("VARIANCE");
954
961
 
955
962
  // src/core/ast/expression-visitor.ts
956
963
  var DispatcherRegistry = class _DispatcherRegistry {
964
+ dispatchers;
957
965
  constructor(dispatchers = /* @__PURE__ */ new Map()) {
958
966
  this.dispatchers = dispatchers;
959
967
  }
@@ -1087,61 +1095,9 @@ var toTableRef = (table) => ({
1087
1095
  alias: hasAlias(table) ? table.alias : void 0
1088
1096
  });
1089
1097
 
1090
- // src/core/ast/builders.ts
1091
- var isColumnNode = (col2) => "type" in col2 && col2.type === "Column";
1092
- var resolveTableName = (def, table) => {
1093
- if (!def.table) {
1094
- return table.alias || table.name;
1095
- }
1096
- if (table.alias && def.table === table.name) {
1097
- return table.alias;
1098
- }
1099
- return def.table;
1100
- };
1101
- var buildColumnNode = (table, column) => {
1102
- if (isColumnNode(column)) {
1103
- return column;
1104
- }
1105
- const def = column;
1106
- const baseTable = resolveTableName(def, table);
1107
- return {
1108
- type: "Column",
1109
- table: baseTable,
1110
- name: def.name
1111
- };
1112
- };
1113
- var buildColumnNodes = (table, names) => names.map((name) => ({
1114
- type: "Column",
1115
- table: table.alias || table.name,
1116
- name
1117
- }));
1118
- var createTableNode = (table) => ({
1119
- type: "Table",
1120
- name: table.name,
1121
- schema: table.schema
1122
- });
1123
- var fnTable = (name, args = [], alias, opts) => ({
1124
- type: "FunctionTable",
1125
- name,
1126
- args,
1127
- alias,
1128
- lateral: opts?.lateral,
1129
- withOrdinality: opts?.withOrdinality,
1130
- columnAliases: opts?.columnAliases,
1131
- schema: opts?.schema
1132
- });
1133
- var derivedTable = (query, alias, columnAliases) => ({
1134
- type: "DerivedTable",
1135
- query,
1136
- alias,
1137
- columnAliases
1138
- });
1139
-
1140
1098
  // src/core/functions/function-registry.ts
1141
1099
  var FunctionRegistry = class {
1142
- constructor() {
1143
- this.renderers = /* @__PURE__ */ new Map();
1144
- }
1100
+ renderers = /* @__PURE__ */ new Map();
1145
1101
  /**
1146
1102
  * Registers or overrides a renderer for the given function name.
1147
1103
  */
@@ -1436,6 +1392,7 @@ function renderStandardGroupConcat(ctx) {
1436
1392
 
1437
1393
  // src/core/functions/standard-strategy.ts
1438
1394
  var StandardFunctionStrategy = class {
1395
+ registry;
1439
1396
  /**
1440
1397
  * Creates a new StandardFunctionStrategy and registers standard functions.
1441
1398
  */
@@ -1501,17 +1458,13 @@ var StandardFunctionStrategy = class {
1501
1458
  getGroupConcatSeparatorOperand(ctx) {
1502
1459
  return getGroupConcatSeparatorOperand(ctx);
1503
1460
  }
1504
- static {
1505
- /** Default separator for GROUP_CONCAT, a comma. */
1506
- this.DEFAULT_GROUP_CONCAT_SEPARATOR = DEFAULT_GROUP_CONCAT_SEPARATOR;
1507
- }
1461
+ /** Default separator for GROUP_CONCAT, a comma. */
1462
+ static DEFAULT_GROUP_CONCAT_SEPARATOR = DEFAULT_GROUP_CONCAT_SEPARATOR;
1508
1463
  };
1509
1464
 
1510
1465
  // src/core/functions/standard-table-strategy.ts
1511
1466
  var StandardTableFunctionStrategy = class {
1512
- constructor() {
1513
- this.renderers = /* @__PURE__ */ new Map();
1514
- }
1467
+ renderers = /* @__PURE__ */ new Map();
1515
1468
  add(key, renderer) {
1516
1469
  this.renderers.set(key, renderer);
1517
1470
  }
@@ -1690,6 +1643,10 @@ var Dialect = class _Dialect {
1690
1643
  const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
1691
1644
  return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
1692
1645
  }
1646
+ expressionCompilers;
1647
+ operandCompilers;
1648
+ functionStrategy;
1649
+ tableFunctionStrategy;
1693
1650
  constructor(functionStrategy, tableFunctionStrategy) {
1694
1651
  this.expressionCompilers = /* @__PURE__ */ new Map();
1695
1652
  this.operandCompilers = /* @__PURE__ */ new Map();
@@ -1705,10 +1662,7 @@ var Dialect = class _Dialect {
1705
1662
  */
1706
1663
  static create(functionStrategy, tableFunctionStrategy) {
1707
1664
  class TestDialect extends _Dialect {
1708
- constructor() {
1709
- super(...arguments);
1710
- this.dialect = "sqlite";
1711
- }
1665
+ dialect = "sqlite";
1712
1666
  quoteIdentifier(id) {
1713
1667
  return `"${id}"`;
1714
1668
  }
@@ -2148,11 +2102,8 @@ var OrderByCompiler = class {
2148
2102
 
2149
2103
  // src/core/dialect/base/sql-dialect.ts
2150
2104
  var SqlDialectBase = class extends Dialect {
2151
- constructor() {
2152
- super(...arguments);
2153
- this.paginationStrategy = new StandardLimitOffsetPagination();
2154
- this.returningStrategy = new NoReturningStrategy();
2155
- }
2105
+ paginationStrategy = new StandardLimitOffsetPagination();
2106
+ returningStrategy = new NoReturningStrategy();
2156
2107
  compileSelectAst(ast, ctx) {
2157
2108
  const hasSetOps = !!(ast.setOps && ast.setOps.length);
2158
2109
  const ctes = CteCompiler.compileCtes(
@@ -2540,12 +2491,12 @@ var PostgresTableFunctionStrategy = class extends StandardTableFunctionStrategy
2540
2491
 
2541
2492
  // src/core/dialect/postgres/index.ts
2542
2493
  var PostgresDialect = class extends SqlDialectBase {
2494
+ dialect = "postgres";
2543
2495
  /**
2544
2496
  * Creates a new PostgresDialect instance
2545
2497
  */
2546
2498
  constructor() {
2547
2499
  super(new PostgresFunctionStrategy(), new PostgresTableFunctionStrategy());
2548
- this.dialect = "postgres";
2549
2500
  this.registerExpressionCompiler("BitwiseExpression", (node, ctx) => {
2550
2501
  const left2 = this.compileOperand(node.left, ctx);
2551
2502
  const right2 = this.compileOperand(node.right, ctx);
@@ -2679,12 +2630,12 @@ var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
2679
2630
 
2680
2631
  // src/core/dialect/mysql/index.ts
2681
2632
  var MySqlDialect = class extends SqlDialectBase {
2633
+ dialect = "mysql";
2682
2634
  /**
2683
2635
  * Creates a new MySqlDialect instance
2684
2636
  */
2685
2637
  constructor() {
2686
2638
  super(new MysqlFunctionStrategy());
2687
- this.dialect = "mysql";
2688
2639
  }
2689
2640
  /**
2690
2641
  * Quotes an identifier using MySQL backtick syntax
@@ -2835,12 +2786,12 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
2835
2786
 
2836
2787
  // src/core/dialect/sqlite/index.ts
2837
2788
  var SqliteDialect = class extends SqlDialectBase {
2789
+ dialect = "sqlite";
2838
2790
  /**
2839
2791
  * Creates a new SqliteDialect instance
2840
2792
  */
2841
2793
  constructor() {
2842
2794
  super(new SqliteFunctionStrategy());
2843
- this.dialect = "sqlite";
2844
2795
  this.registerExpressionCompiler("BitwiseExpression", (node, ctx) => {
2845
2796
  const left2 = this.compileOperand(node.left, ctx);
2846
2797
  const right2 = this.compileOperand(node.right, ctx);
@@ -3013,12 +2964,12 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
3013
2964
 
3014
2965
  // src/core/dialect/mssql/index.ts
3015
2966
  var SqlServerDialect = class extends SqlDialectBase {
2967
+ dialect = "mssql";
3016
2968
  /**
3017
2969
  * Creates a new SqlServerDialect instance
3018
2970
  */
3019
2971
  constructor() {
3020
2972
  super(new MssqlFunctionStrategy());
3021
- this.dialect = "mssql";
3022
2973
  }
3023
2974
  /**
3024
2975
  * Quotes an identifier using SQL Server bracket syntax
@@ -3145,12 +3096,8 @@ var SqlServerDialect = class extends SqlDialectBase {
3145
3096
 
3146
3097
  // src/core/dialect/dialect-factory.ts
3147
3098
  var DialectFactory = class {
3148
- static {
3149
- this.registry = /* @__PURE__ */ new Map();
3150
- }
3151
- static {
3152
- this.defaultsInitialized = false;
3153
- }
3099
+ static registry = /* @__PURE__ */ new Map();
3100
+ static defaultsInitialized = false;
3154
3101
  static ensureDefaults() {
3155
3102
  if (this.defaultsInitialized) return;
3156
3103
  this.defaultsInitialized = true;
@@ -3209,8 +3156,66 @@ var resolveDialectInput = (dialect) => {
3209
3156
  return dialect;
3210
3157
  };
3211
3158
 
3159
+ // src/core/ast/builders.ts
3160
+ var isColumnNode = (col2) => "type" in col2 && col2.type === "Column";
3161
+ var resolveTableName = (def, table) => {
3162
+ if (!def.table) {
3163
+ return table.alias || table.name;
3164
+ }
3165
+ if (table.alias && def.table === table.name) {
3166
+ return table.alias;
3167
+ }
3168
+ return def.table;
3169
+ };
3170
+ var buildColumnNode = (table, column) => {
3171
+ if (isColumnNode(column)) {
3172
+ return column;
3173
+ }
3174
+ const def = column;
3175
+ const baseTable = resolveTableName(def, table);
3176
+ return {
3177
+ type: "Column",
3178
+ table: baseTable,
3179
+ name: def.name
3180
+ };
3181
+ };
3182
+ var buildColumnNodes = (table, names) => names.map((name) => ({
3183
+ type: "Column",
3184
+ table: table.alias || table.name,
3185
+ name
3186
+ }));
3187
+ var createTableNode = (table) => ({
3188
+ type: "Table",
3189
+ name: table.name,
3190
+ schema: table.schema
3191
+ });
3192
+ var fnTable = (name, args = [], alias, opts) => ({
3193
+ type: "FunctionTable",
3194
+ name,
3195
+ args,
3196
+ alias,
3197
+ lateral: opts?.lateral,
3198
+ withOrdinality: opts?.withOrdinality,
3199
+ columnAliases: opts?.columnAliases,
3200
+ schema: opts?.schema
3201
+ });
3202
+ var derivedTable = (query, alias, columnAliases) => ({
3203
+ type: "DerivedTable",
3204
+ query,
3205
+ alias,
3206
+ columnAliases
3207
+ });
3208
+
3212
3209
  // src/query-builder/select-query-state.ts
3213
3210
  var SelectQueryState = class _SelectQueryState {
3211
+ /**
3212
+ * Table definition for the query
3213
+ */
3214
+ table;
3215
+ /**
3216
+ * Abstract Syntax Tree (AST) representation of the query
3217
+ */
3218
+ ast;
3214
3219
  /**
3215
3220
  * Creates a new SelectQueryState instance
3216
3221
  * @param table - Table definition
@@ -4180,199 +4185,150 @@ var buildRelationCorrelation = (root, relation, rootAlias, targetTableName) => {
4180
4185
  // src/core/ast/join-metadata.ts
4181
4186
  var getJoinRelationName = (join) => join.meta?.relationName;
4182
4187
 
4183
- // src/query-builder/relation-service.ts
4184
- var hasRelationForeignKey = (relation) => relation.type !== RelationKinds.BelongsToMany;
4185
- var RelationService = class {
4186
- /**
4187
- * Creates a new RelationService instance
4188
- * @param table - Table definition
4189
- * @param state - Current query state
4190
- * @param hydration - Hydration manager
4191
- */
4192
- constructor(table, state, hydration, createQueryAstService) {
4193
- this.table = table;
4194
- this.state = state;
4195
- this.hydration = hydration;
4196
- this.createQueryAstService = createQueryAstService;
4197
- this.projectionHelper = new RelationProjectionHelper(
4198
- table,
4199
- (state2, hydration2, columns) => this.selectColumns(state2, hydration2, columns)
4200
- );
4201
- }
4202
- /**
4203
- * Joins a relation to the query
4204
- * @param relationName - Name of the relation to join
4205
- * @param joinKind - Type of join to use
4206
- * @param extraCondition - Additional join condition
4207
- * @returns Relation result with updated state and hydration
4208
- */
4209
- joinRelation(relationName, joinKind, extraCondition, tableSource) {
4210
- const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition, tableSource);
4211
- return { state: nextState, hydration: this.hydration };
4188
+ // src/query-builder/relation-filter-utils.ts
4189
+ var splitFilterExpressions = (filter, allowedTables) => {
4190
+ const terms = flattenAnd(filter);
4191
+ const selfFilters = [];
4192
+ const crossFilters = [];
4193
+ for (const term of terms) {
4194
+ if (isExpressionSelfContained(term, allowedTables)) {
4195
+ selfFilters.push(term);
4196
+ } else {
4197
+ crossFilters.push(term);
4198
+ }
4212
4199
  }
4213
- /**
4214
- * Matches records based on a relation with an optional predicate
4215
- * @param relationName - Name of the relation to match
4216
- * @param predicate - Optional predicate expression
4217
- * @returns Relation result with updated state and hydration
4218
- */
4219
- match(relationName, predicate) {
4220
- const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
4221
- const pk = findPrimaryKey(this.table);
4222
- const distinctCols = [{ type: "Column", table: this.rootTableName(), name: pk }];
4223
- const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
4224
- const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
4225
- return { state: nextState, hydration: joined.hydration };
4200
+ return { selfFilters, crossFilters };
4201
+ };
4202
+ var flattenAnd = (node) => {
4203
+ if (!node) return [];
4204
+ if (node.type === "LogicalExpression" && node.operator === "AND") {
4205
+ return node.operands.flatMap((operand) => flattenAnd(operand));
4226
4206
  }
4227
- /**
4228
- * Includes a relation in the query result
4229
- * @param relationName - Name of the relation to include
4230
- * @param options - Options for relation inclusion
4231
- * @returns Relation result with updated state and hydration
4232
- */
4233
- include(relationName, options) {
4234
- let state = this.state;
4235
- let hydration = this.hydration;
4236
- const relation = this.getRelation(relationName);
4237
- const aliasPrefix = options?.aliasPrefix ?? relationName;
4238
- const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
4239
- const { selfFilters, crossFilters } = this.splitFilterExpressions(
4240
- options?.filter,
4241
- /* @__PURE__ */ new Set([relation.target.name])
4242
- );
4243
- const canUseCte = !alreadyJoined && selfFilters.length > 0;
4244
- const joinFilters = [...crossFilters];
4245
- if (!canUseCte) {
4246
- joinFilters.push(...selfFilters);
4247
- }
4248
- const joinCondition = this.combineWithAnd(joinFilters);
4249
- let tableSourceOverride;
4250
- if (canUseCte) {
4251
- const cteInfo = this.createFilteredRelationCte(state, relationName, relation, selfFilters);
4252
- state = cteInfo.state;
4253
- tableSourceOverride = cteInfo.table;
4254
- }
4255
- if (!alreadyJoined) {
4256
- state = this.withJoin(
4257
- state,
4258
- relationName,
4259
- options?.joinKind ?? JOIN_KINDS.LEFT,
4260
- joinCondition,
4261
- tableSourceOverride
4262
- );
4207
+ return [node];
4208
+ };
4209
+ var isExpressionSelfContained = (expr, allowedTables) => {
4210
+ const collector = collectReferencedTables(expr);
4211
+ if (collector.hasSubquery) return false;
4212
+ if (collector.tables.size === 0) return true;
4213
+ for (const table of collector.tables) {
4214
+ if (!allowedTables.has(table)) {
4215
+ return false;
4263
4216
  }
4264
- const projectionResult = this.projectionHelper.ensureBaseProjection(state, hydration);
4265
- state = projectionResult.state;
4266
- hydration = projectionResult.hydration;
4267
- if (hasRelationForeignKey(relation)) {
4268
- const fkColumn = this.table.columns[relation.foreignKey];
4269
- if (fkColumn) {
4270
- const hasForeignKeySelected = state.ast.columns.some((col2) => {
4271
- if (col2.type !== "Column") return false;
4272
- const node = col2;
4273
- const alias = node.alias ?? node.name;
4274
- return alias === relation.foreignKey;
4275
- });
4276
- if (!hasForeignKeySelected) {
4277
- const fkSelectionResult = this.selectColumns(state, hydration, {
4278
- [relation.foreignKey]: fkColumn
4279
- });
4280
- state = fkSelectionResult.state;
4281
- hydration = fkSelectionResult.hydration;
4282
- }
4217
+ }
4218
+ return true;
4219
+ };
4220
+ var collectReferencedTables = (expr) => {
4221
+ const collector = {
4222
+ tables: /* @__PURE__ */ new Set(),
4223
+ hasSubquery: false
4224
+ };
4225
+ collectFromExpression(expr, collector);
4226
+ return collector;
4227
+ };
4228
+ var collectFromExpression = (expr, collector) => {
4229
+ switch (expr.type) {
4230
+ case "BinaryExpression":
4231
+ collectFromOperand(expr.left, collector);
4232
+ collectFromOperand(expr.right, collector);
4233
+ break;
4234
+ case "LogicalExpression":
4235
+ expr.operands.forEach((operand) => collectFromExpression(operand, collector));
4236
+ break;
4237
+ case "NullExpression":
4238
+ collectFromOperand(expr.left, collector);
4239
+ break;
4240
+ case "InExpression":
4241
+ collectFromOperand(expr.left, collector);
4242
+ if (Array.isArray(expr.right)) {
4243
+ expr.right.forEach((value) => collectFromOperand(value, collector));
4244
+ } else {
4245
+ collector.hasSubquery = true;
4283
4246
  }
4284
- }
4285
- const requestedColumns = options?.columns?.length ? [...options.columns] : Object.keys(relation.target.columns);
4286
- const targetPrimaryKey = findPrimaryKey(relation.target);
4287
- if (!requestedColumns.includes(targetPrimaryKey)) {
4288
- requestedColumns.push(targetPrimaryKey);
4289
- }
4290
- const targetColumns = requestedColumns;
4291
- const buildTypedSelection = (columns, prefix, keys, missingMsg) => {
4292
- return keys.reduce((acc, key) => {
4293
- const def = columns[key];
4294
- if (!def) {
4295
- throw new Error(missingMsg(key));
4296
- }
4297
- acc[makeRelationAlias(prefix, key)] = def;
4298
- return acc;
4299
- }, {});
4300
- };
4301
- const targetSelection = buildTypedSelection(
4302
- relation.target.columns,
4303
- aliasPrefix,
4304
- targetColumns,
4305
- (key) => `Column '${key}' not found on relation '${relationName}'`
4306
- );
4307
- if (relation.type !== RelationKinds.BelongsToMany) {
4308
- const relationSelectionResult2 = this.selectColumns(state, hydration, targetSelection);
4309
- state = relationSelectionResult2.state;
4310
- hydration = relationSelectionResult2.hydration;
4311
- hydration = hydration.onRelationIncluded(
4312
- state,
4313
- relation,
4314
- relationName,
4315
- aliasPrefix,
4316
- targetColumns
4317
- );
4318
- return { state, hydration };
4319
- }
4320
- const many = relation;
4321
- const pivotAliasPrefix = options?.pivot?.aliasPrefix ?? `${aliasPrefix}_pivot`;
4322
- const pivotPk = many.pivotPrimaryKey || findPrimaryKey(many.pivotTable);
4323
- const pivotColumns = options?.pivot?.columns ?? many.defaultPivotColumns ?? buildDefaultPivotColumns(many, pivotPk);
4324
- const pivotSelection = buildTypedSelection(
4325
- many.pivotTable.columns,
4326
- pivotAliasPrefix,
4327
- pivotColumns,
4328
- (key) => `Column '${key}' not found on pivot table '${many.pivotTable.name}'`
4329
- );
4330
- const combinedSelection = {
4331
- ...targetSelection,
4332
- ...pivotSelection
4333
- };
4334
- const relationSelectionResult = this.selectColumns(state, hydration, combinedSelection);
4335
- state = relationSelectionResult.state;
4336
- hydration = relationSelectionResult.hydration;
4337
- hydration = hydration.onRelationIncluded(
4338
- state,
4339
- relation,
4340
- relationName,
4341
- aliasPrefix,
4342
- targetColumns,
4343
- { aliasPrefix: pivotAliasPrefix, columns: pivotColumns }
4344
- );
4345
- return { state, hydration };
4247
+ break;
4248
+ case "ExistsExpression":
4249
+ collector.hasSubquery = true;
4250
+ break;
4251
+ case "BetweenExpression":
4252
+ collectFromOperand(expr.left, collector);
4253
+ collectFromOperand(expr.lower, collector);
4254
+ collectFromOperand(expr.upper, collector);
4255
+ break;
4256
+ case "ArithmeticExpression":
4257
+ case "BitwiseExpression":
4258
+ collectFromOperand(expr.left, collector);
4259
+ collectFromOperand(expr.right, collector);
4260
+ break;
4261
+ default:
4262
+ break;
4346
4263
  }
4347
- /**
4348
- * Applies relation correlation to a query AST
4349
- * @param relationName - Name of the relation
4350
- * @param ast - Query AST to modify
4351
- * @returns Modified query AST with relation correlation
4352
- */
4353
- applyRelationCorrelation(relationName, ast, additionalCorrelation) {
4354
- const relation = this.getRelation(relationName);
4355
- const rootAlias = this.state.ast.from.type === "Table" ? this.state.ast.from.alias : void 0;
4356
- let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
4357
- if (additionalCorrelation) {
4358
- correlation = and(correlation, additionalCorrelation);
4359
- }
4360
- const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
4361
- return {
4362
- ...ast,
4363
- where: whereInSubquery
4364
- };
4264
+ };
4265
+ var collectFromOperand = (node, collector) => {
4266
+ switch (node.type) {
4267
+ case "Column":
4268
+ collector.tables.add(node.table);
4269
+ break;
4270
+ case "Function":
4271
+ node.args.forEach((arg) => collectFromOperand(arg, collector));
4272
+ if (node.separator) {
4273
+ collectFromOperand(node.separator, collector);
4274
+ }
4275
+ if (node.orderBy) {
4276
+ node.orderBy.forEach((order) => collectFromOrderingTerm(order.term, collector));
4277
+ }
4278
+ break;
4279
+ case "JsonPath":
4280
+ collectFromOperand(node.column, collector);
4281
+ break;
4282
+ case "ScalarSubquery":
4283
+ collector.hasSubquery = true;
4284
+ break;
4285
+ case "CaseExpression":
4286
+ node.conditions.forEach(({ when, then }) => {
4287
+ collectFromExpression(when, collector);
4288
+ collectFromOperand(then, collector);
4289
+ });
4290
+ if (node.else) {
4291
+ collectFromOperand(node.else, collector);
4292
+ }
4293
+ break;
4294
+ case "Cast":
4295
+ collectFromOperand(node.expression, collector);
4296
+ break;
4297
+ case "WindowFunction":
4298
+ node.args.forEach((arg) => collectFromOperand(arg, collector));
4299
+ node.partitionBy?.forEach((part) => collectFromOperand(part, collector));
4300
+ node.orderBy?.forEach((order) => collectFromOrderingTerm(order.term, collector));
4301
+ break;
4302
+ case "Collate":
4303
+ collectFromOperand(node.expression, collector);
4304
+ break;
4305
+ case "ArithmeticExpression":
4306
+ case "BitwiseExpression":
4307
+ collectFromOperand(node.left, collector);
4308
+ collectFromOperand(node.right, collector);
4309
+ break;
4310
+ case "Literal":
4311
+ case "AliasRef":
4312
+ break;
4313
+ default:
4314
+ break;
4365
4315
  }
4366
- /**
4367
- * Creates a join node for a relation
4368
- * @param state - Current query state
4369
- * @param relationName - Name of the relation
4370
- * @param joinKind - Type of join to use
4371
- * @param extraCondition - Additional join condition
4372
- * @returns Updated query state with join
4373
- */
4374
- withJoin(state, relationName, joinKind, extraCondition, tableSource) {
4375
- const relation = this.getRelation(relationName);
4316
+ };
4317
+ var collectFromOrderingTerm = (term, collector) => {
4318
+ if (isOperandNode(term)) {
4319
+ collectFromOperand(term, collector);
4320
+ return;
4321
+ }
4322
+ collectFromExpression(term, collector);
4323
+ };
4324
+
4325
+ // src/query-builder/relation-join-planner.ts
4326
+ var RelationJoinPlanner = class {
4327
+ constructor(table, createQueryAstService) {
4328
+ this.table = table;
4329
+ this.createQueryAstService = createQueryAstService;
4330
+ }
4331
+ withJoin(state, relationName, relation, joinKind, extraCondition, tableSource) {
4376
4332
  const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
4377
4333
  if (relation.type === RelationKinds.BelongsToMany) {
4378
4334
  const targetTableSource = tableSource ?? {
@@ -4409,167 +4365,31 @@ var RelationService = class {
4409
4365
  const joinNode = createJoinNode(joinKind, targetTable, condition, relationName);
4410
4366
  return this.astService(state).withJoin(joinNode);
4411
4367
  }
4412
- /**
4413
- * Selects columns for a relation
4414
- * @param state - Current query state
4415
- * @param hydration - Hydration manager
4416
- * @param columns - Columns to select
4417
- * @returns Relation result with updated state and hydration
4418
- */
4419
- selectColumns(state, hydration, columns) {
4420
- const { state: nextState, addedColumns } = this.astService(state).select(columns);
4421
- return {
4422
- state: nextState,
4423
- hydration: hydration.onColumnsSelected(nextState, addedColumns)
4424
- };
4425
- }
4426
- combineWithAnd(expressions) {
4427
- if (expressions.length === 0) return void 0;
4428
- if (expressions.length === 1) return expressions[0];
4429
- return {
4430
- type: "LogicalExpression",
4431
- operator: "AND",
4432
- operands: expressions
4433
- };
4434
- }
4435
- splitFilterExpressions(filter, allowedTables) {
4436
- const terms = this.flattenAnd(filter);
4437
- const selfFilters = [];
4438
- const crossFilters = [];
4439
- for (const term of terms) {
4440
- if (this.isExpressionSelfContained(term, allowedTables)) {
4441
- selfFilters.push(term);
4442
- } else {
4443
- crossFilters.push(term);
4444
- }
4445
- }
4446
- return { selfFilters, crossFilters };
4447
- }
4448
- flattenAnd(node) {
4449
- if (!node) return [];
4450
- if (node.type === "LogicalExpression" && node.operator === "AND") {
4451
- return node.operands.flatMap((operand) => this.flattenAnd(operand));
4452
- }
4453
- return [node];
4368
+ astService(state) {
4369
+ return this.createQueryAstService(this.table, state);
4454
4370
  }
4455
- isExpressionSelfContained(expr, allowedTables) {
4456
- const collector = this.collectReferencedTables(expr);
4457
- if (collector.hasSubquery) return false;
4458
- if (collector.tables.size === 0) return true;
4459
- for (const table of collector.tables) {
4460
- if (!allowedTables.has(table)) {
4461
- return false;
4462
- }
4371
+ resolveTargetTableName(target, relation) {
4372
+ if (target.type === "Table") {
4373
+ return target.alias ?? target.name;
4463
4374
  }
4464
- return true;
4465
- }
4466
- collectReferencedTables(expr) {
4467
- const collector = {
4468
- tables: /* @__PURE__ */ new Set(),
4469
- hasSubquery: false
4470
- };
4471
- this.collectFromExpression(expr, collector);
4472
- return collector;
4473
- }
4474
- collectFromExpression(expr, collector) {
4475
- switch (expr.type) {
4476
- case "BinaryExpression":
4477
- this.collectFromOperand(expr.left, collector);
4478
- this.collectFromOperand(expr.right, collector);
4479
- break;
4480
- case "LogicalExpression":
4481
- expr.operands.forEach((operand) => this.collectFromExpression(operand, collector));
4482
- break;
4483
- case "NullExpression":
4484
- this.collectFromOperand(expr.left, collector);
4485
- break;
4486
- case "InExpression":
4487
- this.collectFromOperand(expr.left, collector);
4488
- if (Array.isArray(expr.right)) {
4489
- expr.right.forEach((value) => this.collectFromOperand(value, collector));
4490
- } else {
4491
- collector.hasSubquery = true;
4492
- }
4493
- break;
4494
- case "ExistsExpression":
4495
- collector.hasSubquery = true;
4496
- break;
4497
- case "BetweenExpression":
4498
- this.collectFromOperand(expr.left, collector);
4499
- this.collectFromOperand(expr.lower, collector);
4500
- this.collectFromOperand(expr.upper, collector);
4501
- break;
4502
- case "ArithmeticExpression":
4503
- case "BitwiseExpression":
4504
- this.collectFromOperand(expr.left, collector);
4505
- this.collectFromOperand(expr.right, collector);
4506
- break;
4507
- default:
4508
- break;
4375
+ if (target.type === "DerivedTable") {
4376
+ return target.alias;
4509
4377
  }
4510
- }
4511
- collectFromOperand(node, collector) {
4512
- switch (node.type) {
4513
- case "Column":
4514
- collector.tables.add(node.table);
4515
- break;
4516
- case "Function":
4517
- node.args.forEach((arg) => this.collectFromOperand(arg, collector));
4518
- if (node.separator) {
4519
- this.collectFromOperand(node.separator, collector);
4520
- }
4521
- if (node.orderBy) {
4522
- node.orderBy.forEach((order) => this.collectFromOrderingTerm(order.term, collector));
4523
- }
4524
- break;
4525
- case "JsonPath":
4526
- this.collectFromOperand(node.column, collector);
4527
- break;
4528
- case "ScalarSubquery":
4529
- collector.hasSubquery = true;
4530
- break;
4531
- case "CaseExpression":
4532
- node.conditions.forEach(({ when, then }) => {
4533
- this.collectFromExpression(when, collector);
4534
- this.collectFromOperand(then, collector);
4535
- });
4536
- if (node.else) {
4537
- this.collectFromOperand(node.else, collector);
4538
- }
4539
- break;
4540
- case "Cast":
4541
- this.collectFromOperand(node.expression, collector);
4542
- break;
4543
- case "WindowFunction":
4544
- node.args.forEach((arg) => this.collectFromOperand(arg, collector));
4545
- node.partitionBy?.forEach((part) => this.collectFromOperand(part, collector));
4546
- node.orderBy?.forEach((order) => this.collectFromOrderingTerm(order.term, collector));
4547
- break;
4548
- case "Collate":
4549
- this.collectFromOperand(node.expression, collector);
4550
- break;
4551
- case "ArithmeticExpression":
4552
- case "BitwiseExpression":
4553
- this.collectFromOperand(node.left, collector);
4554
- this.collectFromOperand(node.right, collector);
4555
- break;
4556
- case "Literal":
4557
- case "AliasRef":
4558
- break;
4559
- default:
4560
- break;
4378
+ if (target.type === "FunctionTable") {
4379
+ return target.alias ?? relation.target.name;
4561
4380
  }
4381
+ return relation.target.name;
4562
4382
  }
4563
- collectFromOrderingTerm(term, collector) {
4564
- if (isOperandNode(term)) {
4565
- this.collectFromOperand(term, collector);
4566
- return;
4567
- }
4568
- this.collectFromExpression(term, collector);
4383
+ };
4384
+
4385
+ // src/query-builder/relation-cte-builder.ts
4386
+ var RelationCteBuilder = class {
4387
+ constructor(table, createQueryAstService) {
4388
+ this.table = table;
4389
+ this.createQueryAstService = createQueryAstService;
4569
4390
  }
4570
- createFilteredRelationCte(state, relationName, relation, filters) {
4391
+ createFilteredRelationCte(state, relationName, relation, predicate) {
4571
4392
  const cteName = this.generateUniqueCteName(state, relationName);
4572
- const predicate = this.combineWithAnd(filters);
4573
4393
  if (!predicate) {
4574
4394
  throw new Error("Unable to build filter CTE without predicates.");
4575
4395
  }
@@ -4603,82 +4423,339 @@ var RelationService = class {
4603
4423
  }
4604
4424
  return candidate;
4605
4425
  }
4606
- resolveTargetTableName(target, relation) {
4607
- if (target.type === "Table") {
4608
- return target.alias ?? target.name;
4609
- }
4610
- if (target.type === "DerivedTable") {
4611
- return target.alias;
4612
- }
4613
- if (target.type === "FunctionTable") {
4614
- return target.alias ?? relation.target.name;
4615
- }
4616
- return relation.target.name;
4426
+ astService(state) {
4427
+ return this.createQueryAstService(this.table, state);
4617
4428
  }
4618
- /**
4619
- * Gets a relation definition by name
4620
- * @param relationName - Name of the relation
4621
- * @returns Relation definition
4622
- * @throws Error if relation is not found
4623
- */
4624
- getRelation(relationName) {
4625
- const relation = this.table.relations[relationName];
4626
- if (!relation) {
4627
- throw new Error(`Relation '${relationName}' not found on table '${this.table.name}'`);
4429
+ };
4430
+
4431
+ // src/query-builder/relation-include-strategies.ts
4432
+ var buildTypedSelection = (columns, prefix, keys, missingMsg) => {
4433
+ return keys.reduce((acc, key) => {
4434
+ const def = columns[key];
4435
+ if (!def) {
4436
+ throw new Error(missingMsg(key));
4628
4437
  }
4629
- return relation;
4438
+ acc[makeRelationAlias(prefix, key)] = def;
4439
+ return acc;
4440
+ }, {});
4441
+ };
4442
+ var resolveTargetColumns = (relation, options) => {
4443
+ const requestedColumns = options?.columns?.length ? [...options.columns] : Object.keys(relation.target.columns);
4444
+ const targetPrimaryKey = findPrimaryKey(relation.target);
4445
+ if (!requestedColumns.includes(targetPrimaryKey)) {
4446
+ requestedColumns.push(targetPrimaryKey);
4630
4447
  }
4631
- /**
4632
- * Creates a QueryAstService instance
4633
- * @param state - Current query state
4634
- * @returns QueryAstService instance
4635
- */
4636
- astService(state = this.state) {
4637
- return this.createQueryAstService(this.table, state);
4448
+ return requestedColumns;
4449
+ };
4450
+ var ensureRootForeignKeySelected = (context, relation) => {
4451
+ const fkColumn = context.rootTable.columns[relation.foreignKey];
4452
+ if (!fkColumn) {
4453
+ return { state: context.state, hydration: context.hydration };
4638
4454
  }
4639
- rootTableName() {
4640
- const from = this.state.ast.from;
4641
- if (from.type === "Table" && from.alias) return from.alias;
4642
- return this.table.name;
4455
+ const hasForeignKeySelected = context.state.ast.columns.some((col2) => {
4456
+ if (col2.type !== "Column") return false;
4457
+ const node = col2;
4458
+ const alias = node.alias ?? node.name;
4459
+ return alias === relation.foreignKey;
4460
+ });
4461
+ if (hasForeignKeySelected) {
4462
+ return { state: context.state, hydration: context.hydration };
4643
4463
  }
4464
+ return context.selectColumns(context.state, context.hydration, {
4465
+ [relation.foreignKey]: fkColumn
4466
+ });
4467
+ };
4468
+ var standardIncludeStrategy = (context) => {
4469
+ const relation = context.relation;
4470
+ let { state, hydration } = context;
4471
+ const fkSelectionResult = ensureRootForeignKeySelected(context, relation);
4472
+ state = fkSelectionResult.state;
4473
+ hydration = fkSelectionResult.hydration;
4474
+ const targetColumns = resolveTargetColumns(relation, context.options);
4475
+ const targetSelection = buildTypedSelection(
4476
+ relation.target.columns,
4477
+ context.aliasPrefix,
4478
+ targetColumns,
4479
+ (key) => `Column '${key}' not found on relation '${context.relationName}'`
4480
+ );
4481
+ const relationSelectionResult = context.selectColumns(state, hydration, targetSelection);
4482
+ state = relationSelectionResult.state;
4483
+ hydration = relationSelectionResult.hydration;
4484
+ hydration = hydration.onRelationIncluded(
4485
+ state,
4486
+ relation,
4487
+ context.relationName,
4488
+ context.aliasPrefix,
4489
+ targetColumns
4490
+ );
4491
+ return { state, hydration };
4492
+ };
4493
+ var belongsToManyStrategy = (context) => {
4494
+ const relation = context.relation;
4495
+ let { state, hydration } = context;
4496
+ const targetColumns = resolveTargetColumns(relation, context.options);
4497
+ const targetSelection = buildTypedSelection(
4498
+ relation.target.columns,
4499
+ context.aliasPrefix,
4500
+ targetColumns,
4501
+ (key) => `Column '${key}' not found on relation '${context.relationName}'`
4502
+ );
4503
+ const pivotAliasPrefix = context.options?.pivot?.aliasPrefix ?? `${context.aliasPrefix}_pivot`;
4504
+ const pivotPk = relation.pivotPrimaryKey || findPrimaryKey(relation.pivotTable);
4505
+ const defaultPivotColumns = relation.defaultPivotColumns ?? buildDefaultPivotColumns(relation, pivotPk);
4506
+ const pivotColumns = context.options?.pivot?.columns ? [...context.options.pivot.columns] : [...defaultPivotColumns];
4507
+ const pivotSelection = buildTypedSelection(
4508
+ relation.pivotTable.columns,
4509
+ pivotAliasPrefix,
4510
+ pivotColumns,
4511
+ (key) => `Column '${key}' not found on pivot table '${relation.pivotTable.name}'`
4512
+ );
4513
+ const combinedSelection = {
4514
+ ...targetSelection,
4515
+ ...pivotSelection
4516
+ };
4517
+ const relationSelectionResult = context.selectColumns(state, hydration, combinedSelection);
4518
+ state = relationSelectionResult.state;
4519
+ hydration = relationSelectionResult.hydration;
4520
+ hydration = hydration.onRelationIncluded(
4521
+ state,
4522
+ relation,
4523
+ context.relationName,
4524
+ context.aliasPrefix,
4525
+ targetColumns,
4526
+ { aliasPrefix: pivotAliasPrefix, columns: pivotColumns }
4527
+ );
4528
+ return { state, hydration };
4529
+ };
4530
+ var relationIncludeStrategies = {
4531
+ [RelationKinds.HasMany]: standardIncludeStrategy,
4532
+ [RelationKinds.HasOne]: standardIncludeStrategy,
4533
+ [RelationKinds.BelongsTo]: standardIncludeStrategy,
4534
+ [RelationKinds.BelongsToMany]: belongsToManyStrategy
4644
4535
  };
4645
4536
 
4646
- // src/query-builder/column-selector.ts
4647
- var ColumnSelector = class {
4537
+ // src/query-builder/relation-service.ts
4538
+ var RelationService = class {
4648
4539
  /**
4649
- * Creates a new ColumnSelector instance
4650
- * @param env - Query builder environment
4540
+ * Creates a new RelationService instance
4541
+ * @param table - Table definition
4542
+ * @param state - Current query state
4543
+ * @param hydration - Hydration manager
4651
4544
  */
4652
- constructor(env) {
4653
- this.env = env;
4545
+ constructor(table, state, hydration, createQueryAstService) {
4546
+ this.table = table;
4547
+ this.state = state;
4548
+ this.hydration = hydration;
4549
+ this.createQueryAstService = createQueryAstService;
4550
+ this.projectionHelper = new RelationProjectionHelper(
4551
+ table,
4552
+ (state2, hydration2, columns) => this.selectColumns(state2, hydration2, columns)
4553
+ );
4554
+ this.joinPlanner = new RelationJoinPlanner(table, createQueryAstService);
4555
+ this.cteBuilder = new RelationCteBuilder(table, createQueryAstService);
4654
4556
  }
4557
+ projectionHelper;
4558
+ joinPlanner;
4559
+ cteBuilder;
4655
4560
  /**
4656
- * Selects columns for the query
4657
- * @param context - Current query context
4658
- * @param columns - Columns to select
4659
- * @returns Updated query context with selected columns
4561
+ * Joins a relation to the query
4562
+ * @param relationName - Name of the relation to join
4563
+ * @param joinKind - Type of join to use
4564
+ * @param extraCondition - Additional join condition
4565
+ * @returns Relation result with updated state and hydration
4660
4566
  */
4661
- select(context, columns) {
4662
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
4663
- const { state: nextState, addedColumns } = astService.select(columns);
4664
- return {
4665
- state: nextState,
4666
- hydration: context.hydration.onColumnsSelected(nextState, addedColumns)
4667
- };
4567
+ joinRelation(relationName, joinKind, extraCondition, tableSource) {
4568
+ const relation = this.getRelation(relationName);
4569
+ const nextState = this.joinPlanner.withJoin(
4570
+ this.state,
4571
+ relationName,
4572
+ relation,
4573
+ joinKind,
4574
+ extraCondition,
4575
+ tableSource
4576
+ );
4577
+ return { state: nextState, hydration: this.hydration };
4668
4578
  }
4669
4579
  /**
4670
- * Selects raw column expressions
4671
- * @param context - Current query context
4672
- * @param columns - Raw column expressions
4673
- * @returns Updated query context with raw column selections
4580
+ * Matches records based on a relation with an optional predicate
4581
+ * @param relationName - Name of the relation to match
4582
+ * @param predicate - Optional predicate expression
4583
+ * @returns Relation result with updated state and hydration
4674
4584
  */
4675
- selectRaw(context, columns) {
4676
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
4677
- const nextState = astService.selectRaw(columns).state;
4678
- return { state: nextState, hydration: context.hydration };
4585
+ match(relationName, predicate) {
4586
+ const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
4587
+ const pk = findPrimaryKey(this.table);
4588
+ const distinctCols = [{ type: "Column", table: this.rootTableName(), name: pk }];
4589
+ const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
4590
+ const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
4591
+ return { state: nextState, hydration: joined.hydration };
4679
4592
  }
4680
4593
  /**
4681
- * Selects a subquery as a column
4594
+ * Includes a relation in the query result
4595
+ * @param relationName - Name of the relation to include
4596
+ * @param options - Options for relation inclusion
4597
+ * @returns Relation result with updated state and hydration
4598
+ */
4599
+ include(relationName, options) {
4600
+ let state = this.state;
4601
+ let hydration = this.hydration;
4602
+ const relation = this.getRelation(relationName);
4603
+ const aliasPrefix = options?.aliasPrefix ?? relationName;
4604
+ const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
4605
+ const { selfFilters, crossFilters } = splitFilterExpressions(
4606
+ options?.filter,
4607
+ /* @__PURE__ */ new Set([relation.target.name])
4608
+ );
4609
+ const canUseCte = !alreadyJoined && selfFilters.length > 0;
4610
+ const joinFilters = [...crossFilters];
4611
+ if (!canUseCte) {
4612
+ joinFilters.push(...selfFilters);
4613
+ }
4614
+ const joinCondition = this.combineWithAnd(joinFilters);
4615
+ let tableSourceOverride;
4616
+ if (canUseCte) {
4617
+ const predicate = this.combineWithAnd(selfFilters);
4618
+ const cteInfo = this.cteBuilder.createFilteredRelationCte(
4619
+ state,
4620
+ relationName,
4621
+ relation,
4622
+ predicate
4623
+ );
4624
+ state = cteInfo.state;
4625
+ tableSourceOverride = cteInfo.table;
4626
+ }
4627
+ if (!alreadyJoined) {
4628
+ state = this.joinPlanner.withJoin(
4629
+ state,
4630
+ relationName,
4631
+ relation,
4632
+ options?.joinKind ?? JOIN_KINDS.LEFT,
4633
+ joinCondition,
4634
+ tableSourceOverride
4635
+ );
4636
+ }
4637
+ const projectionResult = this.projectionHelper.ensureBaseProjection(state, hydration);
4638
+ state = projectionResult.state;
4639
+ hydration = projectionResult.hydration;
4640
+ const strategy = relationIncludeStrategies[relation.type];
4641
+ const result = strategy({
4642
+ rootTable: this.table,
4643
+ state,
4644
+ hydration,
4645
+ relation,
4646
+ relationName,
4647
+ aliasPrefix,
4648
+ options,
4649
+ selectColumns: (nextState, nextHydration, columns) => this.selectColumns(nextState, nextHydration, columns)
4650
+ });
4651
+ return { state: result.state, hydration: result.hydration };
4652
+ }
4653
+ /**
4654
+ * Applies relation correlation to a query AST
4655
+ * @param relationName - Name of the relation
4656
+ * @param ast - Query AST to modify
4657
+ * @returns Modified query AST with relation correlation
4658
+ */
4659
+ applyRelationCorrelation(relationName, ast, additionalCorrelation) {
4660
+ const relation = this.getRelation(relationName);
4661
+ const rootAlias = this.state.ast.from.type === "Table" ? this.state.ast.from.alias : void 0;
4662
+ let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
4663
+ if (additionalCorrelation) {
4664
+ correlation = and(correlation, additionalCorrelation);
4665
+ }
4666
+ const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
4667
+ return {
4668
+ ...ast,
4669
+ where: whereInSubquery
4670
+ };
4671
+ }
4672
+ /**
4673
+ * Selects columns for a relation
4674
+ * @param state - Current query state
4675
+ * @param hydration - Hydration manager
4676
+ * @param columns - Columns to select
4677
+ * @returns Relation result with updated state and hydration
4678
+ */
4679
+ selectColumns(state, hydration, columns) {
4680
+ const { state: nextState, addedColumns } = this.astService(state).select(columns);
4681
+ return {
4682
+ state: nextState,
4683
+ hydration: hydration.onColumnsSelected(nextState, addedColumns)
4684
+ };
4685
+ }
4686
+ combineWithAnd(expressions) {
4687
+ if (expressions.length === 0) return void 0;
4688
+ if (expressions.length === 1) return expressions[0];
4689
+ return {
4690
+ type: "LogicalExpression",
4691
+ operator: "AND",
4692
+ operands: expressions
4693
+ };
4694
+ }
4695
+ /**
4696
+ * Gets a relation definition by name
4697
+ * @param relationName - Name of the relation
4698
+ * @returns Relation definition
4699
+ * @throws Error if relation is not found
4700
+ */
4701
+ getRelation(relationName) {
4702
+ const relation = this.table.relations[relationName];
4703
+ if (!relation) {
4704
+ throw new Error(`Relation '${relationName}' not found on table '${this.table.name}'`);
4705
+ }
4706
+ return relation;
4707
+ }
4708
+ /**
4709
+ * Creates a QueryAstService instance
4710
+ * @param state - Current query state
4711
+ * @returns QueryAstService instance
4712
+ */
4713
+ astService(state = this.state) {
4714
+ return this.createQueryAstService(this.table, state);
4715
+ }
4716
+ rootTableName() {
4717
+ const from = this.state.ast.from;
4718
+ if (from.type === "Table" && from.alias) return from.alias;
4719
+ return this.table.name;
4720
+ }
4721
+ };
4722
+
4723
+ // src/query-builder/column-selector.ts
4724
+ var ColumnSelector = class {
4725
+ /**
4726
+ * Creates a new ColumnSelector instance
4727
+ * @param env - Query builder environment
4728
+ */
4729
+ constructor(env) {
4730
+ this.env = env;
4731
+ }
4732
+ /**
4733
+ * Selects columns for the query
4734
+ * @param context - Current query context
4735
+ * @param columns - Columns to select
4736
+ * @returns Updated query context with selected columns
4737
+ */
4738
+ select(context, columns) {
4739
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
4740
+ const { state: nextState, addedColumns } = astService.select(columns);
4741
+ return {
4742
+ state: nextState,
4743
+ hydration: context.hydration.onColumnsSelected(nextState, addedColumns)
4744
+ };
4745
+ }
4746
+ /**
4747
+ * Selects raw column expressions
4748
+ * @param context - Current query context
4749
+ * @param columns - Raw column expressions
4750
+ * @returns Updated query context with raw column selections
4751
+ */
4752
+ selectRaw(context, columns) {
4753
+ const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
4754
+ const nextState = astService.selectRaw(columns).state;
4755
+ return { state: nextState, hydration: context.hydration };
4756
+ }
4757
+ /**
4758
+ * Selects a subquery as a column
4682
4759
  * @param context - Current query context
4683
4760
  * @param alias - Alias for the subquery
4684
4761
  * @param query - Subquery to select
@@ -4903,8 +4980,52 @@ var hasEntityMeta = (entity) => {
4903
4980
  return Boolean(getEntityMeta(entity));
4904
4981
  };
4905
4982
 
4906
- // src/orm/relations/has-many.ts
4983
+ // src/orm/entity-hydration.ts
4907
4984
  var toKey2 = (value) => value === null || value === void 0 ? "" : String(value);
4985
+ var populateHydrationCache = (entity, row, meta) => {
4986
+ for (const relationName of Object.keys(meta.table.relations)) {
4987
+ const relation = meta.table.relations[relationName];
4988
+ const data = row[relationName];
4989
+ if (relation.type === RelationKinds.HasOne) {
4990
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
4991
+ const rootValue = entity[localKey];
4992
+ if (rootValue === void 0 || rootValue === null) continue;
4993
+ if (!data || typeof data !== "object") continue;
4994
+ const cache = /* @__PURE__ */ new Map();
4995
+ cache.set(toKey2(rootValue), data);
4996
+ meta.relationHydration.set(relationName, cache);
4997
+ meta.relationCache.set(relationName, Promise.resolve(cache));
4998
+ continue;
4999
+ }
5000
+ if (!Array.isArray(data)) continue;
5001
+ if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
5002
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
5003
+ const rootValue = entity[localKey];
5004
+ if (rootValue === void 0 || rootValue === null) continue;
5005
+ const cache = /* @__PURE__ */ new Map();
5006
+ cache.set(toKey2(rootValue), data);
5007
+ meta.relationHydration.set(relationName, cache);
5008
+ meta.relationCache.set(relationName, Promise.resolve(cache));
5009
+ continue;
5010
+ }
5011
+ if (relation.type === RelationKinds.BelongsTo) {
5012
+ const targetKey = relation.localKey || findPrimaryKey(relation.target);
5013
+ const cache = /* @__PURE__ */ new Map();
5014
+ for (const item of data) {
5015
+ const pkValue = item[targetKey];
5016
+ if (pkValue === void 0 || pkValue === null) continue;
5017
+ cache.set(toKey2(pkValue), item);
5018
+ }
5019
+ if (cache.size) {
5020
+ meta.relationHydration.set(relationName, cache);
5021
+ meta.relationCache.set(relationName, Promise.resolve(cache));
5022
+ }
5023
+ }
5024
+ }
5025
+ };
5026
+
5027
+ // src/orm/relations/has-many.ts
5028
+ var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
4908
5029
  var hideInternal = (obj, keys) => {
4909
5030
  for (const key of keys) {
4910
5031
  Object.defineProperty(obj, key, {
@@ -4938,13 +5059,13 @@ var DefaultHasManyCollection = class {
4938
5059
  this.loader = loader;
4939
5060
  this.createEntity = createEntity;
4940
5061
  this.localKey = localKey;
4941
- this.loaded = false;
4942
- this.items = [];
4943
- this.added = /* @__PURE__ */ new Set();
4944
- this.removed = /* @__PURE__ */ new Set();
4945
5062
  hideInternal(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
4946
5063
  this.hydrateFromCache();
4947
5064
  }
5065
+ loaded = false;
5066
+ items = [];
5067
+ added = /* @__PURE__ */ new Set();
5068
+ removed = /* @__PURE__ */ new Set();
4948
5069
  /**
4949
5070
  * Loads the related entities if not already loaded.
4950
5071
  * @returns Promise resolving to the array of child entities
@@ -4952,7 +5073,7 @@ var DefaultHasManyCollection = class {
4952
5073
  async load() {
4953
5074
  if (this.loaded) return this.items;
4954
5075
  const map = await this.loader();
4955
- const key = toKey2(this.root[this.localKey]);
5076
+ const key = toKey3(this.root[this.localKey]);
4956
5077
  const rows = map.get(key) ?? [];
4957
5078
  this.items = rows.map((row) => this.createEntity(row));
4958
5079
  this.loaded = true;
@@ -5064,7 +5185,7 @@ var DefaultHasManyCollection = class {
5064
5185
  };
5065
5186
 
5066
5187
  // src/orm/relations/has-one.ts
5067
- var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
5188
+ var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
5068
5189
  var hideInternal2 = (obj, keys) => {
5069
5190
  for (const key of keys) {
5070
5191
  Object.defineProperty(obj, key, {
@@ -5097,8 +5218,6 @@ var DefaultHasOneReference = class {
5097
5218
  this.loader = loader;
5098
5219
  this.createEntity = createEntity;
5099
5220
  this.localKey = localKey;
5100
- this.loaded = false;
5101
- this.current = null;
5102
5221
  hideInternal2(this, [
5103
5222
  "ctx",
5104
5223
  "meta",
@@ -5112,6 +5231,8 @@ var DefaultHasOneReference = class {
5112
5231
  ]);
5113
5232
  this.populateFromHydrationCache();
5114
5233
  }
5234
+ loaded = false;
5235
+ current = null;
5115
5236
  async load() {
5116
5237
  if (this.loaded) return this.current;
5117
5238
  const map = await this.loader();
@@ -5120,7 +5241,7 @@ var DefaultHasOneReference = class {
5120
5241
  this.loaded = true;
5121
5242
  return this.current;
5122
5243
  }
5123
- const row = map.get(toKey3(keyValue));
5244
+ const row = map.get(toKey4(keyValue));
5124
5245
  this.current = row ? this.createEntity(row) : null;
5125
5246
  this.loaded = true;
5126
5247
  return this.current;
@@ -5192,7 +5313,7 @@ var DefaultHasOneReference = class {
5192
5313
  };
5193
5314
 
5194
5315
  // src/orm/relations/belongs-to.ts
5195
- var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
5316
+ var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
5196
5317
  var hideInternal3 = (obj, keys) => {
5197
5318
  for (const key of keys) {
5198
5319
  Object.defineProperty(obj, key, {
@@ -5225,11 +5346,11 @@ var DefaultBelongsToReference = class {
5225
5346
  this.loader = loader;
5226
5347
  this.createEntity = createEntity;
5227
5348
  this.targetKey = targetKey;
5228
- this.loaded = false;
5229
- this.current = null;
5230
5349
  hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
5231
5350
  this.populateFromHydrationCache();
5232
5351
  }
5352
+ loaded = false;
5353
+ current = null;
5233
5354
  async load() {
5234
5355
  if (this.loaded) return this.current;
5235
5356
  const map = await this.loader();
@@ -5237,7 +5358,7 @@ var DefaultBelongsToReference = class {
5237
5358
  if (fkValue === null || fkValue === void 0) {
5238
5359
  this.current = null;
5239
5360
  } else {
5240
- const row = map.get(toKey4(fkValue));
5361
+ const row = map.get(toKey5(fkValue));
5241
5362
  this.current = row ? this.createEntity(row) : null;
5242
5363
  }
5243
5364
  this.loaded = true;
@@ -5294,7 +5415,7 @@ var DefaultBelongsToReference = class {
5294
5415
  };
5295
5416
 
5296
5417
  // src/orm/relations/many-to-many.ts
5297
- var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
5418
+ var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
5298
5419
  var hideInternal4 = (obj, keys) => {
5299
5420
  for (const key of keys) {
5300
5421
  Object.defineProperty(obj, key, {
@@ -5327,11 +5448,11 @@ var DefaultManyToManyCollection = class {
5327
5448
  this.loader = loader;
5328
5449
  this.createEntity = createEntity;
5329
5450
  this.localKey = localKey;
5330
- this.loaded = false;
5331
- this.items = [];
5332
5451
  hideInternal4(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
5333
5452
  this.hydrateFromCache();
5334
5453
  }
5454
+ loaded = false;
5455
+ items = [];
5335
5456
  /**
5336
5457
  * Loads the collection items if not already loaded.
5337
5458
  * @returns A promise that resolves to the array of target entities.
@@ -5339,7 +5460,7 @@ var DefaultManyToManyCollection = class {
5339
5460
  async load() {
5340
5461
  if (this.loaded) return this.items;
5341
5462
  const map = await this.loader();
5342
- const key = toKey5(this.root[this.localKey]);
5463
+ const key = toKey6(this.root[this.localKey]);
5343
5464
  const rows = map.get(key) ?? [];
5344
5465
  this.items = rows.map((row) => {
5345
5466
  const entity = this.createEntity(row);
@@ -5421,15 +5542,15 @@ var DefaultManyToManyCollection = class {
5421
5542
  */
5422
5543
  async syncByIds(ids) {
5423
5544
  await this.load();
5424
- const normalized = new Set(ids.map((id) => toKey5(id)));
5425
- const currentIds = new Set(this.items.map((item) => toKey5(this.extractId(item))));
5545
+ const normalized = new Set(ids.map((id) => toKey6(id)));
5546
+ const currentIds = new Set(this.items.map((item) => toKey6(this.extractId(item))));
5426
5547
  for (const id of normalized) {
5427
5548
  if (!currentIds.has(id)) {
5428
5549
  this.attach(id);
5429
5550
  }
5430
5551
  }
5431
5552
  for (const item of [...this.items]) {
5432
- const itemId = toKey5(this.extractId(item));
5553
+ const itemId = toKey6(this.extractId(item));
5433
5554
  if (!normalized.has(itemId)) {
5434
5555
  this.detach(item);
5435
5556
  }
@@ -5476,7 +5597,7 @@ var DefaultManyToManyCollection = class {
5476
5597
  }
5477
5598
  };
5478
5599
 
5479
- // src/orm/lazy-batch.ts
5600
+ // src/orm/lazy-batch/shared.ts
5480
5601
  var hasColumns = (columns) => Boolean(columns && columns.length > 0);
5481
5602
  var buildColumnSelection = (table, columns, missingMsg) => {
5482
5603
  return columns.reduce((acc, column) => {
@@ -5517,7 +5638,7 @@ var executeQuery = async (ctx, qb) => {
5517
5638
  const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
5518
5639
  return rowsFromResults(results);
5519
5640
  };
5520
- var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
5641
+ var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
5521
5642
  var collectKeysFromRoots = (roots, key) => {
5522
5643
  const collected = /* @__PURE__ */ new Set();
5523
5644
  for (const tracked of roots) {
@@ -5542,7 +5663,7 @@ var groupRowsByMany = (rows, keyColumn) => {
5542
5663
  for (const row of rows) {
5543
5664
  const value = row[keyColumn];
5544
5665
  if (value === null || value === void 0) continue;
5545
- const key = toKey6(value);
5666
+ const key = toKey7(value);
5546
5667
  const bucket = grouped.get(key) ?? [];
5547
5668
  bucket.push(row);
5548
5669
  grouped.set(key, bucket);
@@ -5554,13 +5675,15 @@ var groupRowsByUnique = (rows, keyColumn) => {
5554
5675
  for (const row of rows) {
5555
5676
  const value = row[keyColumn];
5556
5677
  if (value === null || value === void 0) continue;
5557
- const key = toKey6(value);
5678
+ const key = toKey7(value);
5558
5679
  if (!lookup.has(key)) {
5559
5680
  lookup.set(key, row);
5560
5681
  }
5561
5682
  }
5562
5683
  return lookup;
5563
5684
  };
5685
+
5686
+ // src/orm/lazy-batch/has-many.ts
5564
5687
  var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options) => {
5565
5688
  const localKey = relation.localKey || findPrimaryKey(rootTable);
5566
5689
  const roots = ctx.getEntitiesForTable(rootTable);
@@ -5593,6 +5716,8 @@ var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options
5593
5716
  }
5594
5717
  return filtered;
5595
5718
  };
5719
+
5720
+ // src/orm/lazy-batch/has-one.ts
5596
5721
  var loadHasOneRelation = async (ctx, rootTable, relationName, relation, options) => {
5597
5722
  const localKey = relation.localKey || findPrimaryKey(rootTable);
5598
5723
  const roots = ctx.getEntitiesForTable(rootTable);
@@ -5625,6 +5750,8 @@ var loadHasOneRelation = async (ctx, rootTable, relationName, relation, options)
5625
5750
  }
5626
5751
  return filtered;
5627
5752
  };
5753
+
5754
+ // src/orm/lazy-batch/belongs-to.ts
5628
5755
  var loadBelongsToRelation = async (ctx, rootTable, relationName, relation, options) => {
5629
5756
  const roots = ctx.getEntitiesForTable(rootTable);
5630
5757
  const getForeignKeys = () => collectKeysFromRoots(roots, relation.foreignKey);
@@ -5693,6 +5820,8 @@ var loadBelongsToRelation = async (ctx, rootTable, relationName, relation, optio
5693
5820
  }
5694
5821
  return filtered;
5695
5822
  };
5823
+
5824
+ // src/orm/lazy-batch/belongs-to-many.ts
5696
5825
  var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, options) => {
5697
5826
  const rootKey = relation.localKey || findPrimaryKey(rootTable);
5698
5827
  const roots = ctx.getEntitiesForTable(rootTable);
@@ -5731,12 +5860,12 @@ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, o
5731
5860
  if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
5732
5861
  continue;
5733
5862
  }
5734
- const bucket = rootLookup.get(toKey6(rootValue)) ?? [];
5863
+ const bucket = rootLookup.get(toKey7(rootValue)) ?? [];
5735
5864
  bucket.push({
5736
5865
  targetId: targetValue,
5737
5866
  pivot: pivotVisibleColumns.size ? filterRow(pivot, pivotVisibleColumns) : {}
5738
5867
  });
5739
- rootLookup.set(toKey6(rootValue), bucket);
5868
+ rootLookup.set(toKey7(rootValue), bucket);
5740
5869
  targetIds.add(targetValue);
5741
5870
  }
5742
5871
  if (!targetIds.size) {
@@ -5755,14 +5884,21 @@ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, o
5755
5884
  targetSelectedColumns,
5756
5885
  (column) => `Column '${column}' not found on relation '${relationName}'`
5757
5886
  );
5758
- const targetRows = await fetchRowsForKeys(ctx, relation.target, targetPkColumn, targetIds, targetSelection, options?.filter);
5887
+ const targetRows = await fetchRowsForKeys(
5888
+ ctx,
5889
+ relation.target,
5890
+ targetPkColumn,
5891
+ targetIds,
5892
+ targetSelection,
5893
+ options?.filter
5894
+ );
5759
5895
  const targetMap = groupRowsByUnique(targetRows, targetKey);
5760
5896
  const targetVisibleColumns = new Set(targetSelectedColumns);
5761
5897
  const result = /* @__PURE__ */ new Map();
5762
5898
  for (const [rootId, entries] of rootLookup.entries()) {
5763
5899
  const bucket = [];
5764
5900
  for (const entry of entries) {
5765
- const targetRow = targetMap.get(toKey6(entry.targetId));
5901
+ const targetRow = targetMap.get(toKey7(entry.targetId));
5766
5902
  if (!targetRow) continue;
5767
5903
  bucket.push({
5768
5904
  ...targetRequestedColumns ? filterRow(targetRow, targetVisibleColumns) : targetRow,
@@ -5774,7 +5910,7 @@ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, o
5774
5910
  return result;
5775
5911
  };
5776
5912
 
5777
- // src/orm/entity.ts
5913
+ // src/orm/entity-relation-cache.ts
5778
5914
  var relationLoaderCache = (meta, relationName, factory) => {
5779
5915
  if (meta.relationCache.has(relationName)) {
5780
5916
  return meta.relationCache.get(relationName);
@@ -5795,118 +5931,16 @@ var relationLoaderCache = (meta, relationName, factory) => {
5795
5931
  }
5796
5932
  return promise;
5797
5933
  };
5798
- var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
5799
- const target = { ...row };
5800
- const meta = {
5801
- ctx,
5802
- table,
5803
- lazyRelations: [...lazyRelations],
5804
- lazyRelationOptions: new Map(lazyRelationOptions),
5805
- relationCache: /* @__PURE__ */ new Map(),
5806
- relationHydration: /* @__PURE__ */ new Map(),
5807
- relationWrappers: /* @__PURE__ */ new Map()
5808
- };
5809
- Object.defineProperty(target, ENTITY_META, {
5810
- value: meta,
5811
- enumerable: false,
5812
- writable: false
5813
- });
5814
- const handler = {
5815
- get(targetObj, prop, receiver) {
5816
- if (prop === ENTITY_META) {
5817
- return meta;
5818
- }
5819
- if (prop === "$load") {
5820
- return async (relationName) => {
5821
- const wrapper = getRelationWrapper(meta, relationName, receiver);
5822
- if (wrapper && typeof wrapper.load === "function") {
5823
- return wrapper.load();
5824
- }
5825
- return void 0;
5826
- };
5934
+
5935
+ // src/orm/entity-relations.ts
5936
+ var proxifyRelationWrapper = (wrapper) => {
5937
+ return new Proxy(wrapper, {
5938
+ get(target, prop, receiver) {
5939
+ if (typeof prop === "symbol") {
5940
+ return Reflect.get(target, prop, receiver);
5827
5941
  }
5828
- if (typeof prop === "string" && table.relations[prop]) {
5829
- return getRelationWrapper(meta, prop, receiver);
5830
- }
5831
- return Reflect.get(targetObj, prop, receiver);
5832
- },
5833
- set(targetObj, prop, value, receiver) {
5834
- const result = Reflect.set(targetObj, prop, value, receiver);
5835
- if (typeof prop === "string" && table.columns[prop]) {
5836
- ctx.markDirty(receiver);
5837
- }
5838
- return result;
5839
- }
5840
- };
5841
- const proxy = new Proxy(target, handler);
5842
- populateHydrationCache(proxy, row, meta);
5843
- return proxy;
5844
- };
5845
- var createEntityFromRow = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
5846
- const pkName = findPrimaryKey(table);
5847
- const pkValue = row[pkName];
5848
- if (pkValue !== void 0 && pkValue !== null) {
5849
- const tracked = ctx.getEntity(table, pkValue);
5850
- if (tracked) return tracked;
5851
- }
5852
- const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
5853
- if (pkValue !== void 0 && pkValue !== null) {
5854
- ctx.trackManaged(table, pkValue, entity);
5855
- } else {
5856
- ctx.trackNew(table, entity);
5857
- }
5858
- return entity;
5859
- };
5860
- var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
5861
- var populateHydrationCache = (entity, row, meta) => {
5862
- for (const relationName of Object.keys(meta.table.relations)) {
5863
- const relation = meta.table.relations[relationName];
5864
- const data = row[relationName];
5865
- if (relation.type === RelationKinds.HasOne) {
5866
- const localKey = relation.localKey || findPrimaryKey(meta.table);
5867
- const rootValue = entity[localKey];
5868
- if (rootValue === void 0 || rootValue === null) continue;
5869
- if (!data || typeof data !== "object") continue;
5870
- const cache = /* @__PURE__ */ new Map();
5871
- cache.set(toKey7(rootValue), data);
5872
- meta.relationHydration.set(relationName, cache);
5873
- meta.relationCache.set(relationName, Promise.resolve(cache));
5874
- continue;
5875
- }
5876
- if (!Array.isArray(data)) continue;
5877
- if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
5878
- const localKey = relation.localKey || findPrimaryKey(meta.table);
5879
- const rootValue = entity[localKey];
5880
- if (rootValue === void 0 || rootValue === null) continue;
5881
- const cache = /* @__PURE__ */ new Map();
5882
- cache.set(toKey7(rootValue), data);
5883
- meta.relationHydration.set(relationName, cache);
5884
- meta.relationCache.set(relationName, Promise.resolve(cache));
5885
- continue;
5886
- }
5887
- if (relation.type === RelationKinds.BelongsTo) {
5888
- const targetKey = relation.localKey || findPrimaryKey(relation.target);
5889
- const cache = /* @__PURE__ */ new Map();
5890
- for (const item of data) {
5891
- const pkValue = item[targetKey];
5892
- if (pkValue === void 0 || pkValue === null) continue;
5893
- cache.set(toKey7(pkValue), item);
5894
- }
5895
- if (cache.size) {
5896
- meta.relationHydration.set(relationName, cache);
5897
- meta.relationCache.set(relationName, Promise.resolve(cache));
5898
- }
5899
- }
5900
- }
5901
- };
5902
- var proxifyRelationWrapper = (wrapper) => {
5903
- return new Proxy(wrapper, {
5904
- get(target, prop, receiver) {
5905
- if (typeof prop === "symbol") {
5906
- return Reflect.get(target, prop, receiver);
5907
- }
5908
- if (prop in target) {
5909
- return Reflect.get(target, prop, receiver);
5942
+ if (prop in target) {
5943
+ return Reflect.get(target, prop, receiver);
5910
5944
  }
5911
5945
  const getItems = target.getItems;
5912
5946
  if (typeof getItems === "function") {
@@ -5951,98 +5985,93 @@ var proxifyRelationWrapper = (wrapper) => {
5951
5985
  }
5952
5986
  });
5953
5987
  };
5954
- var getRelationWrapper = (meta, relationName, owner) => {
5955
- if (meta.relationWrappers.has(relationName)) {
5956
- return meta.relationWrappers.get(relationName);
5988
+ var getRelationWrapper = (meta, relationName, owner, createEntity) => {
5989
+ const relationKey = relationName;
5990
+ if (meta.relationWrappers.has(relationKey)) {
5991
+ return meta.relationWrappers.get(relationKey);
5957
5992
  }
5958
- const relation = meta.table.relations[relationName];
5993
+ const relation = meta.table.relations[relationKey];
5959
5994
  if (!relation) return void 0;
5960
- const wrapper = instantiateWrapper(meta, relationName, relation, owner);
5995
+ const wrapper = instantiateWrapper(meta, relationKey, relation, owner, createEntity);
5961
5996
  if (!wrapper) return void 0;
5962
5997
  const proxied = proxifyRelationWrapper(wrapper);
5963
- meta.relationWrappers.set(relationName, proxied);
5998
+ meta.relationWrappers.set(relationKey, proxied);
5964
5999
  return proxied;
5965
6000
  };
5966
- var instantiateWrapper = (meta, relationName, relation, owner) => {
6001
+ var instantiateWrapper = (meta, relationName, relation, owner, createEntity) => {
6002
+ const metaBase = meta;
5967
6003
  const lazyOptions = meta.lazyRelationOptions.get(relationName);
6004
+ const loadCached = (factory) => relationLoaderCache(metaBase, relationName, factory);
5968
6005
  switch (relation.type) {
5969
6006
  case RelationKinds.HasOne: {
5970
6007
  const hasOne2 = relation;
5971
6008
  const localKey = hasOne2.localKey || findPrimaryKey(meta.table);
5972
- const loader = () => relationLoaderCache(
5973
- meta,
5974
- relationName,
6009
+ const loader = () => loadCached(
5975
6010
  () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2, lazyOptions)
5976
6011
  );
5977
6012
  return new DefaultHasOneReference(
5978
6013
  meta.ctx,
5979
- meta,
6014
+ metaBase,
5980
6015
  owner,
5981
6016
  relationName,
5982
6017
  hasOne2,
5983
6018
  meta.table,
5984
6019
  loader,
5985
- (row) => createEntityFromRow(meta.ctx, hasOne2.target, row),
6020
+ (row) => createEntity(hasOne2.target, row),
5986
6021
  localKey
5987
6022
  );
5988
6023
  }
5989
6024
  case RelationKinds.HasMany: {
5990
6025
  const hasMany2 = relation;
5991
6026
  const localKey = hasMany2.localKey || findPrimaryKey(meta.table);
5992
- const loader = () => relationLoaderCache(
5993
- meta,
5994
- relationName,
6027
+ const loader = () => loadCached(
5995
6028
  () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2, lazyOptions)
5996
6029
  );
5997
6030
  return new DefaultHasManyCollection(
5998
6031
  meta.ctx,
5999
- meta,
6032
+ metaBase,
6000
6033
  owner,
6001
6034
  relationName,
6002
6035
  hasMany2,
6003
6036
  meta.table,
6004
6037
  loader,
6005
- (row) => createEntityFromRow(meta.ctx, relation.target, row),
6038
+ (row) => createEntity(relation.target, row),
6006
6039
  localKey
6007
6040
  );
6008
6041
  }
6009
6042
  case RelationKinds.BelongsTo: {
6010
6043
  const belongsTo2 = relation;
6011
6044
  const targetKey = belongsTo2.localKey || findPrimaryKey(belongsTo2.target);
6012
- const loader = () => relationLoaderCache(
6013
- meta,
6014
- relationName,
6045
+ const loader = () => loadCached(
6015
6046
  () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2, lazyOptions)
6016
6047
  );
6017
6048
  return new DefaultBelongsToReference(
6018
6049
  meta.ctx,
6019
- meta,
6050
+ metaBase,
6020
6051
  owner,
6021
6052
  relationName,
6022
6053
  belongsTo2,
6023
6054
  meta.table,
6024
6055
  loader,
6025
- (row) => createEntityFromRow(meta.ctx, relation.target, row),
6056
+ (row) => createEntity(relation.target, row),
6026
6057
  targetKey
6027
6058
  );
6028
6059
  }
6029
6060
  case RelationKinds.BelongsToMany: {
6030
6061
  const many = relation;
6031
6062
  const localKey = many.localKey || findPrimaryKey(meta.table);
6032
- const loader = () => relationLoaderCache(
6033
- meta,
6034
- relationName,
6063
+ const loader = () => loadCached(
6035
6064
  () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many, lazyOptions)
6036
6065
  );
6037
6066
  return new DefaultManyToManyCollection(
6038
6067
  meta.ctx,
6039
- meta,
6068
+ metaBase,
6040
6069
  owner,
6041
6070
  relationName,
6042
6071
  many,
6043
6072
  meta.table,
6044
6073
  loader,
6045
- (row) => createEntityFromRow(meta.ctx, relation.target, row),
6074
+ (row) => createEntity(relation.target, row),
6046
6075
  localKey
6047
6076
  );
6048
6077
  }
@@ -6051,103 +6080,570 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
6051
6080
  }
6052
6081
  };
6053
6082
 
6054
- // src/orm/execute.ts
6055
- var flattenResults = (results) => {
6056
- const rows = [];
6057
- for (const result of results) {
6058
- const { columns, values } = result;
6059
- for (const valueRow of values) {
6060
- const row = {};
6061
- columns.forEach((column, idx) => {
6062
- row[column] = valueRow[idx];
6063
- });
6064
- rows.push(row);
6065
- }
6083
+ // src/orm/entity.ts
6084
+ var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
6085
+ const target = { ...row };
6086
+ const meta = {
6087
+ ctx,
6088
+ table,
6089
+ lazyRelations: [...lazyRelations],
6090
+ lazyRelationOptions: new Map(lazyRelationOptions),
6091
+ relationCache: /* @__PURE__ */ new Map(),
6092
+ relationHydration: /* @__PURE__ */ new Map(),
6093
+ relationWrappers: /* @__PURE__ */ new Map()
6094
+ };
6095
+ const createRelationEntity = (relationTable, relationRow) => createEntityFromRow(meta.ctx, relationTable, relationRow);
6096
+ Object.defineProperty(target, ENTITY_META, {
6097
+ value: meta,
6098
+ enumerable: false,
6099
+ writable: false
6100
+ });
6101
+ const handler = {
6102
+ get(targetObj, prop, receiver) {
6103
+ if (prop === ENTITY_META) {
6104
+ return meta;
6105
+ }
6106
+ if (prop === "$load") {
6107
+ return async (relationName) => {
6108
+ const wrapper = getRelationWrapper(meta, relationName, receiver, createRelationEntity);
6109
+ if (wrapper && typeof wrapper.load === "function") {
6110
+ return wrapper.load();
6111
+ }
6112
+ return void 0;
6113
+ };
6114
+ }
6115
+ if (typeof prop === "string" && table.relations[prop]) {
6116
+ return getRelationWrapper(meta, prop, receiver, createRelationEntity);
6117
+ }
6118
+ return Reflect.get(targetObj, prop, receiver);
6119
+ },
6120
+ set(targetObj, prop, value, receiver) {
6121
+ const result = Reflect.set(targetObj, prop, value, receiver);
6122
+ if (typeof prop === "string" && table.columns[prop]) {
6123
+ ctx.markDirty(receiver);
6124
+ }
6125
+ return result;
6126
+ }
6127
+ };
6128
+ const proxy = new Proxy(target, handler);
6129
+ populateHydrationCache(proxy, row, meta);
6130
+ return proxy;
6131
+ };
6132
+ var createEntityFromRow = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
6133
+ const pkName = findPrimaryKey(table);
6134
+ const pkValue = row[pkName];
6135
+ if (pkValue !== void 0 && pkValue !== null) {
6136
+ const tracked = ctx.getEntity(table, pkValue);
6137
+ if (tracked) return tracked;
6138
+ }
6139
+ const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
6140
+ if (pkValue !== void 0 && pkValue !== null) {
6141
+ ctx.trackManaged(table, pkValue, entity);
6142
+ } else {
6143
+ ctx.trackNew(table, entity);
6144
+ }
6145
+ return entity;
6146
+ };
6147
+
6148
+ // src/orm/execute.ts
6149
+ var flattenResults = (results) => {
6150
+ const rows = [];
6151
+ for (const result of results) {
6152
+ const { columns, values } = result;
6153
+ for (const valueRow of values) {
6154
+ const row = {};
6155
+ columns.forEach((column, idx) => {
6156
+ row[column] = valueRow[idx];
6157
+ });
6158
+ rows.push(row);
6159
+ }
6160
+ }
6161
+ return rows;
6162
+ };
6163
+ var executeWithContexts = async (execCtx, entityCtx, qb) => {
6164
+ const ast = qb.getAST();
6165
+ const compiled = execCtx.dialect.compileSelect(ast);
6166
+ const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
6167
+ const rows = flattenResults(executed);
6168
+ const lazyRelations = qb.getLazyRelations();
6169
+ const lazyRelationOptions = qb.getLazyRelationOptions();
6170
+ if (ast.setOps && ast.setOps.length > 0) {
6171
+ const proxies = rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
6172
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
6173
+ return proxies;
6174
+ }
6175
+ const hydrated = hydrateRows(rows, qb.getHydrationPlan());
6176
+ const entities = hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
6177
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
6178
+ return entities;
6179
+ };
6180
+ async function executeHydrated(session, qb) {
6181
+ return executeWithContexts(session.getExecutionContext(), session, qb);
6182
+ }
6183
+ async function executeHydratedWithContexts(execCtx, hydCtx, qb) {
6184
+ const entityCtx = hydCtx.entityContext;
6185
+ if (!entityCtx) {
6186
+ throw new Error("Hydration context is missing an EntityContext");
6187
+ }
6188
+ return executeWithContexts(execCtx, entityCtx, qb);
6189
+ }
6190
+ var loadLazyRelationsForTable = async (ctx, table, lazyRelations, lazyRelationOptions) => {
6191
+ if (!lazyRelations.length) return;
6192
+ const tracked = ctx.getEntitiesForTable(table);
6193
+ if (!tracked.length) return;
6194
+ const meta = getEntityMeta(tracked[0].entity);
6195
+ if (!meta) return;
6196
+ for (const relationName of lazyRelations) {
6197
+ const relation = table.relations[relationName];
6198
+ if (!relation) continue;
6199
+ const key = relationName;
6200
+ const options = lazyRelationOptions.get(key);
6201
+ if (!options) {
6202
+ continue;
6203
+ }
6204
+ switch (relation.type) {
6205
+ case RelationKinds.HasOne:
6206
+ await relationLoaderCache(
6207
+ meta,
6208
+ key,
6209
+ () => loadHasOneRelation(ctx, table, key, relation, options)
6210
+ );
6211
+ break;
6212
+ case RelationKinds.HasMany:
6213
+ await relationLoaderCache(
6214
+ meta,
6215
+ key,
6216
+ () => loadHasManyRelation(ctx, table, key, relation, options)
6217
+ );
6218
+ break;
6219
+ case RelationKinds.BelongsTo:
6220
+ await relationLoaderCache(
6221
+ meta,
6222
+ key,
6223
+ () => loadBelongsToRelation(ctx, table, key, relation, options)
6224
+ );
6225
+ break;
6226
+ case RelationKinds.BelongsToMany:
6227
+ await relationLoaderCache(
6228
+ meta,
6229
+ key,
6230
+ () => loadBelongsToManyRelation(ctx, table, key, relation, options)
6231
+ );
6232
+ break;
6233
+ }
6234
+ }
6235
+ };
6236
+
6237
+ // src/query-builder/query-resolution.ts
6238
+ function resolveSelectQuery(query) {
6239
+ const candidate = query;
6240
+ return typeof candidate.getAST === "function" && candidate.getAST ? candidate.getAST() : query;
6241
+ }
6242
+
6243
+ // src/query-builder/select/select-operations.ts
6244
+ function applyOrderBy(context, predicateFacet, term, directionOrOptions) {
6245
+ const options = typeof directionOrOptions === "string" ? { direction: directionOrOptions } : directionOrOptions;
6246
+ const dir = options.direction ?? ORDER_DIRECTIONS.ASC;
6247
+ return predicateFacet.orderBy(context, term, dir, options.nulls, options.collation);
6248
+ }
6249
+ async function executeCount(context, env, session) {
6250
+ const unpagedAst = {
6251
+ ...context.state.ast,
6252
+ orderBy: void 0,
6253
+ limit: void 0,
6254
+ offset: void 0
6255
+ };
6256
+ const nextState = new SelectQueryState(env.table, unpagedAst);
6257
+ const nextContext = {
6258
+ ...context,
6259
+ state: nextState
6260
+ };
6261
+ const subAst = nextContext.hydration.applyToAst(nextState.ast);
6262
+ const countQuery = {
6263
+ type: "SelectQuery",
6264
+ from: derivedTable(subAst, "__metal_count"),
6265
+ columns: [{ type: "Function", name: "COUNT", args: [], alias: "total" }],
6266
+ joins: []
6267
+ };
6268
+ const execCtx = session.getExecutionContext();
6269
+ const compiled = execCtx.dialect.compileSelect(countQuery);
6270
+ const results = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
6271
+ const value = results[0]?.values?.[0]?.[0];
6272
+ if (typeof value === "number") return value;
6273
+ if (typeof value === "bigint") return Number(value);
6274
+ if (typeof value === "string") return Number(value);
6275
+ return value === null || value === void 0 ? 0 : Number(value);
6276
+ }
6277
+ async function executePagedQuery(builder, session, options, countCallback) {
6278
+ const { page, pageSize } = options;
6279
+ if (!Number.isInteger(page) || page < 1) {
6280
+ throw new Error("executePaged: page must be an integer >= 1");
6281
+ }
6282
+ if (!Number.isInteger(pageSize) || pageSize < 1) {
6283
+ throw new Error("executePaged: pageSize must be an integer >= 1");
6284
+ }
6285
+ const offset = (page - 1) * pageSize;
6286
+ const [items, totalItems] = await Promise.all([
6287
+ builder.limit(pageSize).offset(offset).execute(session),
6288
+ countCallback(session)
6289
+ ]);
6290
+ return { items, totalItems };
6291
+ }
6292
+ function buildWhereHasPredicate(env, context, relationFacet, createChildBuilder, relationName, callbackOrOptions, maybeOptions, negate = false) {
6293
+ const relation = env.table.relations[relationName];
6294
+ if (!relation) {
6295
+ throw new Error(`Relation '${relationName}' not found on table '${env.table.name}'`);
6296
+ }
6297
+ const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
6298
+ const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
6299
+ let subQb = createChildBuilder(relation.target);
6300
+ if (callback) {
6301
+ subQb = callback(subQb);
6302
+ }
6303
+ const subAst = subQb.getAST();
6304
+ const finalSubAst = relationFacet.applyRelationCorrelation(
6305
+ context,
6306
+ relationName,
6307
+ subAst,
6308
+ options?.correlate
6309
+ );
6310
+ return negate ? notExists(finalSubAst) : exists(finalSubAst);
6311
+ }
6312
+
6313
+ // src/query-builder/select/from-facet.ts
6314
+ var SelectFromFacet = class {
6315
+ /**
6316
+ * Creates a new SelectFromFacet instance
6317
+ * @param env - Query builder environment
6318
+ * @param createAstService - Function to create AST service
6319
+ */
6320
+ constructor(env, createAstService) {
6321
+ this.env = env;
6322
+ this.createAstService = createAstService;
6323
+ }
6324
+ /**
6325
+ * Applies an alias to the FROM table
6326
+ * @param context - Current query context
6327
+ * @param alias - Alias to apply
6328
+ * @returns Updated query context with aliased FROM
6329
+ */
6330
+ as(context, alias) {
6331
+ const from = context.state.ast.from;
6332
+ if (from.type !== "Table") {
6333
+ throw new Error("Cannot alias non-table FROM sources");
6334
+ }
6335
+ const nextFrom = { ...from, alias };
6336
+ const astService = this.createAstService(context.state);
6337
+ const nextState = astService.withFrom(nextFrom);
6338
+ return { state: nextState, hydration: context.hydration };
6339
+ }
6340
+ /**
6341
+ * Sets the FROM clause to a subquery
6342
+ * @param context - Current query context
6343
+ * @param subAst - Subquery AST
6344
+ * @param alias - Alias for the subquery
6345
+ * @param columnAliases - Optional column aliases
6346
+ * @returns Updated query context with subquery FROM
6347
+ */
6348
+ fromSubquery(context, subAst, alias, columnAliases) {
6349
+ const fromNode = derivedTable(subAst, alias, columnAliases);
6350
+ const astService = this.createAstService(context.state);
6351
+ const nextState = astService.withFrom(fromNode);
6352
+ return { state: nextState, hydration: context.hydration };
6353
+ }
6354
+ /**
6355
+ * Sets the FROM clause to a function table
6356
+ * @param context - Current query context
6357
+ * @param name - Function name
6358
+ * @param args - Function arguments
6359
+ * @param alias - Optional alias for the function table
6360
+ * @param options - Optional function table options
6361
+ * @returns Updated query context with function table FROM
6362
+ */
6363
+ fromFunctionTable(context, name, args, alias, options) {
6364
+ const functionTable = fnTable(name, args, alias, options);
6365
+ const astService = this.createAstService(context.state);
6366
+ const nextState = astService.withFrom(functionTable);
6367
+ return { state: nextState, hydration: context.hydration };
6368
+ }
6369
+ };
6370
+
6371
+ // src/query-builder/select/join-facet.ts
6372
+ var SelectJoinFacet = class {
6373
+ constructor(env, createAstService) {
6374
+ this.env = env;
6375
+ this.createAstService = createAstService;
6376
+ }
6377
+ applyJoin(context, table, condition, kind) {
6378
+ const joinNode = createJoinNode(kind, { type: "Table", name: table.name, schema: table.schema }, condition);
6379
+ const astService = this.createAstService(context.state);
6380
+ const nextState = astService.withJoin(joinNode);
6381
+ return { state: nextState, hydration: context.hydration };
6382
+ }
6383
+ joinSubquery(context, subAst, alias, condition, joinKind, columnAliases) {
6384
+ const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
6385
+ const astService = this.createAstService(context.state);
6386
+ const nextState = astService.withJoin(joinNode);
6387
+ return { state: nextState, hydration: context.hydration };
6388
+ }
6389
+ joinFunctionTable(context, name, args, alias, condition, joinKind, options) {
6390
+ const functionTable = fnTable(name, args, alias, options);
6391
+ const joinNode = createJoinNode(joinKind, functionTable, condition);
6392
+ const astService = this.createAstService(context.state);
6393
+ const nextState = astService.withJoin(joinNode);
6394
+ return { state: nextState, hydration: context.hydration };
6395
+ }
6396
+ };
6397
+
6398
+ // src/query-builder/select/projection-facet.ts
6399
+ var SelectProjectionFacet = class {
6400
+ /**
6401
+ * Creates a new SelectProjectionFacet instance
6402
+ * @param columnSelector - Column selector dependency
6403
+ */
6404
+ constructor(columnSelector) {
6405
+ this.columnSelector = columnSelector;
6406
+ }
6407
+ /**
6408
+ * Selects columns for the query
6409
+ * @param context - Current query context
6410
+ * @param columns - Columns to select
6411
+ * @returns Updated query context with selected columns
6412
+ */
6413
+ select(context, columns) {
6414
+ return this.columnSelector.select(context, columns);
6415
+ }
6416
+ /**
6417
+ * Selects raw column expressions
6418
+ * @param context - Current query context
6419
+ * @param cols - Raw column expressions
6420
+ * @returns Updated query context with raw column selections
6421
+ */
6422
+ selectRaw(context, cols) {
6423
+ return this.columnSelector.selectRaw(context, cols);
6424
+ }
6425
+ /**
6426
+ * Selects a subquery as a column
6427
+ * @param context - Current query context
6428
+ * @param alias - Alias for the subquery
6429
+ * @param query - Subquery to select
6430
+ * @returns Updated query context with subquery selection
6431
+ */
6432
+ selectSubquery(context, alias, query) {
6433
+ return this.columnSelector.selectSubquery(context, alias, query);
6434
+ }
6435
+ /**
6436
+ * Adds DISTINCT clause to the query
6437
+ * @param context - Current query context
6438
+ * @param cols - Columns to make distinct
6439
+ * @returns Updated query context with DISTINCT clause
6440
+ */
6441
+ distinct(context, cols) {
6442
+ return this.columnSelector.distinct(context, cols);
6443
+ }
6444
+ };
6445
+
6446
+ // src/query-builder/select/predicate-facet.ts
6447
+ var SelectPredicateFacet = class {
6448
+ /**
6449
+ * Creates a new SelectPredicateFacet instance
6450
+ * @param env - Query builder environment
6451
+ * @param createAstService - Function to create AST service
6452
+ */
6453
+ constructor(env, createAstService) {
6454
+ this.env = env;
6455
+ this.createAstService = createAstService;
6456
+ }
6457
+ /**
6458
+ * Adds a WHERE condition to the query
6459
+ * @param context - Current query context
6460
+ * @param expr - WHERE expression
6461
+ * @returns Updated query context with WHERE condition
6462
+ */
6463
+ where(context, expr) {
6464
+ const astService = this.createAstService(context.state);
6465
+ const nextState = astService.withWhere(expr);
6466
+ return { state: nextState, hydration: context.hydration };
6467
+ }
6468
+ /**
6469
+ * Adds a GROUP BY clause to the query
6470
+ * @param context - Current query context
6471
+ * @param term - Column or ordering term to group by
6472
+ * @returns Updated query context with GROUP BY clause
6473
+ */
6474
+ groupBy(context, term) {
6475
+ const astService = this.createAstService(context.state);
6476
+ const nextState = astService.withGroupBy(term);
6477
+ return { state: nextState, hydration: context.hydration };
6478
+ }
6479
+ /**
6480
+ * Adds a HAVING condition to the query
6481
+ * @param context - Current query context
6482
+ * @param expr - HAVING expression
6483
+ * @returns Updated query context with HAVING condition
6484
+ */
6485
+ having(context, expr) {
6486
+ const astService = this.createAstService(context.state);
6487
+ const nextState = astService.withHaving(expr);
6488
+ return { state: nextState, hydration: context.hydration };
6489
+ }
6490
+ /**
6491
+ * Adds an ORDER BY clause to the query
6492
+ * @param context - Current query context
6493
+ * @param term - Column or ordering term to order by
6494
+ * @param direction - Order direction
6495
+ * @param nulls - Nulls ordering
6496
+ * @param collation - Collation
6497
+ * @returns Updated query context with ORDER BY clause
6498
+ */
6499
+ orderBy(context, term, direction, nulls, collation) {
6500
+ const astService = this.createAstService(context.state);
6501
+ const nextState = astService.withOrderBy(term, direction, nulls, collation);
6502
+ return { state: nextState, hydration: context.hydration };
6503
+ }
6504
+ /**
6505
+ * Adds a LIMIT clause to the query
6506
+ * @param context - Current query context
6507
+ * @param n - Maximum number of rows
6508
+ * @returns Updated query context with LIMIT clause
6509
+ */
6510
+ limit(context, n) {
6511
+ const astService = this.createAstService(context.state);
6512
+ const nextState = astService.withLimit(n);
6513
+ return { state: nextState, hydration: context.hydration };
6514
+ }
6515
+ /**
6516
+ * Adds an OFFSET clause to the query
6517
+ * @param context - Current query context
6518
+ * @param n - Number of rows to skip
6519
+ * @returns Updated query context with OFFSET clause
6520
+ */
6521
+ offset(context, n) {
6522
+ const astService = this.createAstService(context.state);
6523
+ const nextState = astService.withOffset(n);
6524
+ return { state: nextState, hydration: context.hydration };
6525
+ }
6526
+ };
6527
+
6528
+ // src/query-builder/select/cte-facet.ts
6529
+ var SelectCTEFacet = class {
6530
+ /**
6531
+ * Creates a new SelectCTEFacet instance
6532
+ * @param env - Query builder environment
6533
+ * @param createAstService - Function to create AST service
6534
+ */
6535
+ constructor(env, createAstService) {
6536
+ this.env = env;
6537
+ this.createAstService = createAstService;
6538
+ }
6539
+ /**
6540
+ * Adds a Common Table Expression to the query
6541
+ * @param context - Current query context
6542
+ * @param name - CTE name
6543
+ * @param subAst - CTE query AST
6544
+ * @param columns - Optional column names
6545
+ * @param recursive - Whether the CTE is recursive
6546
+ * @returns Updated query context with CTE
6547
+ */
6548
+ withCTE(context, name, subAst, columns, recursive) {
6549
+ const astService = this.createAstService(context.state);
6550
+ const nextState = astService.withCte(name, subAst, columns, recursive);
6551
+ return { state: nextState, hydration: context.hydration };
6552
+ }
6553
+ };
6554
+
6555
+ // src/query-builder/select/setop-facet.ts
6556
+ var SelectSetOpFacet = class {
6557
+ /**
6558
+ * Creates a new SelectSetOpFacet instance
6559
+ * @param env - Query builder environment
6560
+ * @param createAstService - Function to create AST service
6561
+ */
6562
+ constructor(env, createAstService) {
6563
+ this.env = env;
6564
+ this.createAstService = createAstService;
6066
6565
  }
6067
- return rows;
6068
- };
6069
- var executeWithContexts = async (execCtx, entityCtx, qb) => {
6070
- const ast = qb.getAST();
6071
- const compiled = execCtx.dialect.compileSelect(ast);
6072
- const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
6073
- const rows = flattenResults(executed);
6074
- const lazyRelations = qb.getLazyRelations();
6075
- const lazyRelationOptions = qb.getLazyRelationOptions();
6076
- if (ast.setOps && ast.setOps.length > 0) {
6077
- const proxies = rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
6078
- await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
6079
- return proxies;
6566
+ /**
6567
+ * Applies a set operation to the query
6568
+ * @param context - Current query context
6569
+ * @param operator - Set operation kind
6570
+ * @param subAst - Subquery AST to combine
6571
+ * @returns Updated query context with set operation
6572
+ */
6573
+ applySetOperation(context, operator, subAst) {
6574
+ const astService = this.createAstService(context.state);
6575
+ const nextState = astService.withSetOperation(operator, subAst);
6576
+ return { state: nextState, hydration: context.hydration };
6080
6577
  }
6081
- const hydrated = hydrateRows(rows, qb.getHydrationPlan());
6082
- const entities = hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
6083
- await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
6084
- return entities;
6085
6578
  };
6086
- async function executeHydrated(session, qb) {
6087
- return executeWithContexts(session.getExecutionContext(), session, qb);
6088
- }
6089
- async function executeHydratedWithContexts(execCtx, hydCtx, qb) {
6090
- const entityCtx = hydCtx.entityContext;
6091
- if (!entityCtx) {
6092
- throw new Error("Hydration context is missing an EntityContext");
6579
+
6580
+ // src/query-builder/select/relation-facet.ts
6581
+ var SelectRelationFacet = class {
6582
+ /**
6583
+ * Creates a new SelectRelationFacet instance
6584
+ * @param relationManager - Relation manager dependency
6585
+ */
6586
+ constructor(relationManager) {
6587
+ this.relationManager = relationManager;
6093
6588
  }
6094
- return executeWithContexts(execCtx, entityCtx, qb);
6095
- }
6096
- var loadLazyRelationsForTable = async (ctx, table, lazyRelations, lazyRelationOptions) => {
6097
- if (!lazyRelations.length) return;
6098
- const tracked = ctx.getEntitiesForTable(table);
6099
- if (!tracked.length) return;
6100
- const meta = getEntityMeta(tracked[0].entity);
6101
- if (!meta) return;
6102
- for (const relationName of lazyRelations) {
6103
- const relation = table.relations[relationName];
6104
- if (!relation) continue;
6105
- const key = relationName;
6106
- const options = lazyRelationOptions.get(key);
6107
- if (!options) {
6108
- continue;
6109
- }
6110
- switch (relation.type) {
6111
- case RelationKinds.HasOne:
6112
- await relationLoaderCache(
6113
- meta,
6114
- key,
6115
- () => loadHasOneRelation(ctx, table, key, relation, options)
6116
- );
6117
- break;
6118
- case RelationKinds.HasMany:
6119
- await relationLoaderCache(
6120
- meta,
6121
- key,
6122
- () => loadHasManyRelation(ctx, table, key, relation, options)
6123
- );
6124
- break;
6125
- case RelationKinds.BelongsTo:
6126
- await relationLoaderCache(
6127
- meta,
6128
- key,
6129
- () => loadBelongsToRelation(ctx, table, key, relation, options)
6130
- );
6131
- break;
6132
- case RelationKinds.BelongsToMany:
6133
- await relationLoaderCache(
6134
- meta,
6135
- key,
6136
- () => loadBelongsToManyRelation(ctx, table, key, relation, options)
6137
- );
6138
- break;
6139
- }
6589
+ /**
6590
+ * Matches records based on a relationship
6591
+ * @param context - Current query context
6592
+ * @param relationName - Name of the relationship
6593
+ * @param predicate - Optional predicate
6594
+ * @returns Updated query context with relation match
6595
+ */
6596
+ match(context, relationName, predicate) {
6597
+ return this.relationManager.match(context, relationName, predicate);
6598
+ }
6599
+ /**
6600
+ * Joins a related table
6601
+ * @param context - Current query context
6602
+ * @param relationName - Name of the relationship
6603
+ * @param joinKind - Type of join
6604
+ * @param extraCondition - Optional additional condition
6605
+ * @returns Updated query context with relation join
6606
+ */
6607
+ joinRelation(context, relationName, joinKind, extraCondition) {
6608
+ return this.relationManager.joinRelation(context, relationName, joinKind, extraCondition);
6609
+ }
6610
+ /**
6611
+ * Includes related data in the query results
6612
+ * @param context - Current query context
6613
+ * @param relationName - Name of the relationship to include
6614
+ * @param options - Optional include options
6615
+ * @returns Updated query context with relation inclusion
6616
+ */
6617
+ include(context, relationName, options) {
6618
+ return this.relationManager.include(context, relationName, options);
6619
+ }
6620
+ /**
6621
+ * Applies correlation for relation-based subqueries
6622
+ * @param context - Current query context
6623
+ * @param relationName - Name of the relationship
6624
+ * @param subAst - Subquery AST
6625
+ * @param extraCorrelate - Optional additional correlation
6626
+ * @returns Modified subquery AST with correlation
6627
+ */
6628
+ applyRelationCorrelation(context, relationName, subAst, extraCorrelate) {
6629
+ return this.relationManager.applyRelationCorrelation(context, relationName, subAst, extraCorrelate);
6140
6630
  }
6141
6631
  };
6142
6632
 
6143
- // src/query-builder/query-resolution.ts
6144
- function resolveSelectQuery(query) {
6145
- const candidate = query;
6146
- return typeof candidate.getAST === "function" && candidate.getAST ? candidate.getAST() : query;
6147
- }
6148
-
6149
6633
  // src/query-builder/select.ts
6150
6634
  var SelectQueryBuilder = class _SelectQueryBuilder {
6635
+ env;
6636
+ context;
6637
+ columnSelector;
6638
+ fromFacet;
6639
+ joinFacet;
6640
+ projectionFacet;
6641
+ predicateFacet;
6642
+ cteFacet;
6643
+ setOpFacet;
6644
+ relationFacet;
6645
+ lazyRelations;
6646
+ lazyRelationOptions;
6151
6647
  /**
6152
6648
  * Creates a new SelectQueryBuilder instance
6153
6649
  * @param table - Table definition to query
@@ -6158,6 +6654,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6158
6654
  constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions) {
6159
6655
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
6160
6656
  this.env = { table, deps };
6657
+ const createAstService = (nextState) => deps.createQueryAstService(table, nextState);
6161
6658
  const initialState = state ?? deps.createState(table);
6162
6659
  const initialHydration = hydration ?? deps.createHydration(table);
6163
6660
  this.context = {
@@ -6167,7 +6664,14 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6167
6664
  this.lazyRelations = new Set(lazyRelations ?? []);
6168
6665
  this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
6169
6666
  this.columnSelector = deps.createColumnSelector(this.env);
6170
- this.relationManager = deps.createRelationManager(this.env);
6667
+ const relationManager = deps.createRelationManager(this.env);
6668
+ this.fromFacet = new SelectFromFacet(this.env, createAstService);
6669
+ this.joinFacet = new SelectJoinFacet(this.env, createAstService);
6670
+ this.projectionFacet = new SelectProjectionFacet(this.columnSelector);
6671
+ this.predicateFacet = new SelectPredicateFacet(this.env, createAstService);
6672
+ this.cteFacet = new SelectCTEFacet(this.env, createAstService);
6673
+ this.setOpFacet = new SelectSetOpFacet(this.env, createAstService);
6674
+ this.relationFacet = new SelectRelationFacet(relationManager);
6171
6675
  }
6172
6676
  /**
6173
6677
  * Creates a new SelectQueryBuilder instance with updated context and lazy relations
@@ -6190,12 +6694,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6190
6694
  * @param alias - Alias to apply
6191
6695
  */
6192
6696
  as(alias) {
6193
- const from = this.context.state.ast.from;
6194
- if (from.type !== "Table") {
6195
- throw new Error("Cannot alias non-table FROM sources");
6196
- }
6197
- const nextFrom = { ...from, alias };
6198
- const nextContext = this.applyAst(this.context, (service) => service.withFrom(nextFrom));
6697
+ const nextContext = this.fromFacet.as(this.context, alias);
6199
6698
  return this.clone(nextContext);
6200
6699
  }
6201
6700
  /**
@@ -6220,29 +6719,6 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6220
6719
  createChildBuilder(table) {
6221
6720
  return new _SelectQueryBuilder(table, void 0, void 0, this.env.deps);
6222
6721
  }
6223
- /**
6224
- * Applies an AST mutation using the query AST service
6225
- * @param context - Current query context
6226
- * @param mutator - Function that mutates the AST
6227
- * @returns Updated query context
6228
- */
6229
- applyAst(context, mutator) {
6230
- const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
6231
- const nextState = mutator(astService);
6232
- return { state: nextState, hydration: context.hydration };
6233
- }
6234
- /**
6235
- * Applies a join to the query context
6236
- * @param context - Current query context
6237
- * @param table - Table to join
6238
- * @param condition - Join condition
6239
- * @param kind - Join kind
6240
- * @returns Updated query context with join applied
6241
- */
6242
- applyJoin(context, table, condition, kind) {
6243
- const joinNode = createJoinNode(kind, { type: "Table", name: table.name, schema: table.schema }, condition);
6244
- return this.applyAst(context, (service) => service.withJoin(joinNode));
6245
- }
6246
6722
  /**
6247
6723
  * Applies a set operation to the query
6248
6724
  * @param operator - Set operation kind
@@ -6251,12 +6727,12 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6251
6727
  */
6252
6728
  applySetOperation(operator, query) {
6253
6729
  const subAst = resolveSelectQuery(query);
6254
- return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
6730
+ return this.setOpFacet.applySetOperation(this.context, operator, subAst);
6255
6731
  }
6256
6732
  select(...args) {
6257
6733
  if (args.length === 1 && typeof args[0] === "object" && args[0] !== null && typeof args[0] !== "string") {
6258
6734
  const columns = args[0];
6259
- return this.clone(this.columnSelector.select(this.context, columns));
6735
+ return this.clone(this.projectionFacet.select(this.context, columns));
6260
6736
  }
6261
6737
  const cols = args;
6262
6738
  const selection = {};
@@ -6267,7 +6743,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6267
6743
  }
6268
6744
  selection[key] = col2;
6269
6745
  }
6270
- return this.clone(this.columnSelector.select(this.context, selection));
6746
+ return this.clone(this.projectionFacet.select(this.context, selection));
6271
6747
  }
6272
6748
  /**
6273
6749
  * Selects raw column expressions
@@ -6275,7 +6751,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6275
6751
  * @returns New query builder instance with raw column selections
6276
6752
  */
6277
6753
  selectRaw(...cols) {
6278
- return this.clone(this.columnSelector.selectRaw(this.context, cols));
6754
+ return this.clone(this.projectionFacet.selectRaw(this.context, cols));
6279
6755
  }
6280
6756
  /**
6281
6757
  * Adds a Common Table Expression (CTE) to the query
@@ -6286,7 +6762,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6286
6762
  */
6287
6763
  with(name, query, columns) {
6288
6764
  const subAst = resolveSelectQuery(query);
6289
- const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, false));
6765
+ const nextContext = this.cteFacet.withCTE(this.context, name, subAst, columns, false);
6290
6766
  return this.clone(nextContext);
6291
6767
  }
6292
6768
  /**
@@ -6298,7 +6774,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6298
6774
  */
6299
6775
  withRecursive(name, query, columns) {
6300
6776
  const subAst = resolveSelectQuery(query);
6301
- const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
6777
+ const nextContext = this.cteFacet.withCTE(this.context, name, subAst, columns, true);
6302
6778
  return this.clone(nextContext);
6303
6779
  }
6304
6780
  /**
@@ -6310,8 +6786,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6310
6786
  */
6311
6787
  fromSubquery(subquery, alias, columnAliases) {
6312
6788
  const subAst = resolveSelectQuery(subquery);
6313
- const fromNode = derivedTable(subAst, alias, columnAliases);
6314
- const nextContext = this.applyAst(this.context, (service) => service.withFrom(fromNode));
6789
+ const nextContext = this.fromFacet.fromSubquery(this.context, subAst, alias, columnAliases);
6315
6790
  return this.clone(nextContext);
6316
6791
  }
6317
6792
  /**
@@ -6322,8 +6797,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6322
6797
  * @param options - Optional function-table metadata (lateral, ordinality, column aliases, schema)
6323
6798
  */
6324
6799
  fromFunctionTable(name, args = [], alias, options) {
6325
- const functionTable = fnTable(name, args, alias, options);
6326
- const nextContext = this.applyAst(this.context, (service) => service.withFrom(functionTable));
6800
+ const nextContext = this.fromFacet.fromFunctionTable(this.context, name, args, alias, options);
6327
6801
  return this.clone(nextContext);
6328
6802
  }
6329
6803
  /**
@@ -6334,7 +6808,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6334
6808
  */
6335
6809
  selectSubquery(alias, sub2) {
6336
6810
  const query = resolveSelectQuery(sub2);
6337
- return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
6811
+ return this.clone(this.projectionFacet.selectSubquery(this.context, alias, query));
6338
6812
  }
6339
6813
  /**
6340
6814
  * Adds a JOIN against a derived table (subquery with alias)
@@ -6347,8 +6821,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6347
6821
  */
6348
6822
  joinSubquery(subquery, alias, condition, joinKind = JOIN_KINDS.INNER, columnAliases) {
6349
6823
  const subAst = resolveSelectQuery(subquery);
6350
- const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
6351
- const nextContext = this.applyAst(this.context, (service) => service.withJoin(joinNode));
6824
+ const nextContext = this.joinFacet.joinSubquery(this.context, subAst, alias, condition, joinKind, columnAliases);
6352
6825
  return this.clone(nextContext);
6353
6826
  }
6354
6827
  /**
@@ -6361,9 +6834,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6361
6834
  * @param options - Optional metadata (lateral, ordinality, column aliases, schema)
6362
6835
  */
6363
6836
  joinFunctionTable(name, args = [], alias, condition, joinKind = JOIN_KINDS.INNER, options) {
6364
- const functionTable = fnTable(name, args, alias, options);
6365
- const joinNode = createJoinNode(joinKind, functionTable, condition);
6366
- const nextContext = this.applyAst(this.context, (service) => service.withJoin(joinNode));
6837
+ const nextContext = this.joinFacet.joinFunctionTable(this.context, name, args, alias, condition, joinKind, options);
6367
6838
  return this.clone(nextContext);
6368
6839
  }
6369
6840
  /**
@@ -6373,7 +6844,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6373
6844
  * @returns New query builder instance with the INNER JOIN
6374
6845
  */
6375
6846
  innerJoin(table, condition) {
6376
- const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
6847
+ const nextContext = this.joinFacet.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
6377
6848
  return this.clone(nextContext);
6378
6849
  }
6379
6850
  /**
@@ -6383,7 +6854,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6383
6854
  * @returns New query builder instance with the LEFT JOIN
6384
6855
  */
6385
6856
  leftJoin(table, condition) {
6386
- const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
6857
+ const nextContext = this.joinFacet.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
6387
6858
  return this.clone(nextContext);
6388
6859
  }
6389
6860
  /**
@@ -6393,7 +6864,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6393
6864
  * @returns New query builder instance with the RIGHT JOIN
6394
6865
  */
6395
6866
  rightJoin(table, condition) {
6396
- const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
6867
+ const nextContext = this.joinFacet.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
6397
6868
  return this.clone(nextContext);
6398
6869
  }
6399
6870
  /**
@@ -6403,7 +6874,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6403
6874
  * @returns New query builder instance with the relationship match
6404
6875
  */
6405
6876
  match(relationName, predicate) {
6406
- const nextContext = this.relationManager.match(this.context, relationName, predicate);
6877
+ const nextContext = this.relationFacet.match(this.context, relationName, predicate);
6407
6878
  return this.clone(nextContext);
6408
6879
  }
6409
6880
  /**
@@ -6414,7 +6885,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6414
6885
  * @returns New query builder instance with the relationship join
6415
6886
  */
6416
6887
  joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
6417
- const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
6888
+ const nextContext = this.relationFacet.joinRelation(this.context, relationName, joinKind, extraCondition);
6418
6889
  return this.clone(nextContext);
6419
6890
  }
6420
6891
  /**
@@ -6424,7 +6895,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6424
6895
  * @returns New query builder instance with the relationship inclusion
6425
6896
  */
6426
6897
  include(relationName, options) {
6427
- const nextContext = this.relationManager.include(this.context, relationName, options);
6898
+ const nextContext = this.relationFacet.include(this.context, relationName, options);
6428
6899
  return this.clone(nextContext);
6429
6900
  }
6430
6901
  /**
@@ -6463,7 +6934,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6463
6934
  * Convenience alias for including only specific columns from a relation.
6464
6935
  */
6465
6936
  includePick(relationName, cols) {
6466
- return this.include(relationName, { columns: cols });
6937
+ const options = { columns: cols };
6938
+ return this.include(relationName, options);
6467
6939
  }
6468
6940
  /**
6469
6941
  * Selects columns for the root table and relations from an array of entries
@@ -6476,7 +6948,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6476
6948
  if (entry.type === "root") {
6477
6949
  currBuilder = currBuilder.select(...entry.columns);
6478
6950
  } else {
6479
- currBuilder = currBuilder.include(entry.relationName, { columns: entry.columns });
6951
+ const options = { columns: entry.columns };
6952
+ currBuilder = currBuilder.include(entry.relationName, options);
6480
6953
  }
6481
6954
  }
6482
6955
  return currBuilder;
@@ -6510,51 +6983,23 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6510
6983
  async execute(ctx) {
6511
6984
  return executeHydrated(ctx, this);
6512
6985
  }
6513
- withAst(ast) {
6514
- const nextState = new SelectQueryState(this.env.table, ast);
6515
- const nextContext = {
6516
- ...this.context,
6517
- state: nextState
6518
- };
6519
- return this.clone(nextContext);
6520
- }
6986
+ /**
6987
+ * Executes a count query for the current builder without LIMIT/OFFSET clauses.
6988
+ *
6989
+ * @example
6990
+ * const total = await qb.count(session);
6991
+ */
6521
6992
  async count(session) {
6522
- const unpagedAst = {
6523
- ...this.context.state.ast,
6524
- orderBy: void 0,
6525
- limit: void 0,
6526
- offset: void 0
6527
- };
6528
- const subAst = this.withAst(unpagedAst).getAST();
6529
- const countQuery = {
6530
- type: "SelectQuery",
6531
- from: derivedTable(subAst, "__metal_count"),
6532
- columns: [{ type: "Function", name: "COUNT", args: [], alias: "total" }],
6533
- joins: []
6534
- };
6535
- const execCtx = session.getExecutionContext();
6536
- const compiled = execCtx.dialect.compileSelect(countQuery);
6537
- const results = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
6538
- const value = results[0]?.values?.[0]?.[0];
6539
- if (typeof value === "number") return value;
6540
- if (typeof value === "bigint") return Number(value);
6541
- if (typeof value === "string") return Number(value);
6542
- return value === null || value === void 0 ? 0 : Number(value);
6993
+ return executeCount(this.context, this.env, session);
6543
6994
  }
6995
+ /**
6996
+ * Executes the query and returns both the paged items and the total.
6997
+ *
6998
+ * @example
6999
+ * const { items, totalItems } = await qb.executePaged(session, { page: 1, pageSize: 20 });
7000
+ */
6544
7001
  async executePaged(session, options) {
6545
- const { page, pageSize } = options;
6546
- if (!Number.isInteger(page) || page < 1) {
6547
- throw new Error("executePaged: page must be an integer >= 1");
6548
- }
6549
- if (!Number.isInteger(pageSize) || pageSize < 1) {
6550
- throw new Error("executePaged: pageSize must be an integer >= 1");
6551
- }
6552
- const offset = (page - 1) * pageSize;
6553
- const [items, totalItems] = await Promise.all([
6554
- this.limit(pageSize).offset(offset).execute(session),
6555
- this.count(session)
6556
- ]);
6557
- return { items, totalItems };
7002
+ return executePagedQuery(this, session, options, (sess) => this.count(sess));
6558
7003
  }
6559
7004
  /**
6560
7005
  * Executes the query with provided execution and hydration contexts
@@ -6571,7 +7016,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6571
7016
  * @returns New query builder instance with the WHERE condition
6572
7017
  */
6573
7018
  where(expr) {
6574
- const nextContext = this.applyAst(this.context, (service) => service.withWhere(expr));
7019
+ const nextContext = this.predicateFacet.where(this.context, expr);
6575
7020
  return this.clone(nextContext);
6576
7021
  }
6577
7022
  /**
@@ -6580,7 +7025,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6580
7025
  * @returns New query builder instance with the GROUP BY clause
6581
7026
  */
6582
7027
  groupBy(term) {
6583
- const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(term));
7028
+ const nextContext = this.predicateFacet.groupBy(this.context, term);
6584
7029
  return this.clone(nextContext);
6585
7030
  }
6586
7031
  /**
@@ -6589,7 +7034,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6589
7034
  * @returns New query builder instance with the HAVING condition
6590
7035
  */
6591
7036
  having(expr) {
6592
- const nextContext = this.applyAst(this.context, (service) => service.withHaving(expr));
7037
+ const nextContext = this.predicateFacet.having(this.context, expr);
6593
7038
  return this.clone(nextContext);
6594
7039
  }
6595
7040
  /**
@@ -6597,14 +7042,12 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6597
7042
  * @param term - Column definition or ordering term to order by
6598
7043
  * @param directionOrOptions - Order direction or options (defaults to ASC)
6599
7044
  * @returns New query builder instance with the ORDER BY clause
7045
+ *
7046
+ * @example
7047
+ * qb.orderBy(userTable.columns.createdAt, 'DESC');
6600
7048
  */
6601
7049
  orderBy(term, directionOrOptions = ORDER_DIRECTIONS.ASC) {
6602
- const options = typeof directionOrOptions === "string" ? { direction: directionOrOptions } : directionOrOptions;
6603
- const dir = options.direction ?? ORDER_DIRECTIONS.ASC;
6604
- const nextContext = this.applyAst(
6605
- this.context,
6606
- (service) => service.withOrderBy(term, dir, options.nulls, options.collation)
6607
- );
7050
+ const nextContext = applyOrderBy(this.context, this.predicateFacet, term, directionOrOptions);
6608
7051
  return this.clone(nextContext);
6609
7052
  }
6610
7053
  /**
@@ -6613,7 +7056,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6613
7056
  * @returns New query builder instance with the DISTINCT clause
6614
7057
  */
6615
7058
  distinct(...cols) {
6616
- return this.clone(this.columnSelector.distinct(this.context, cols));
7059
+ return this.clone(this.projectionFacet.distinct(this.context, cols));
6617
7060
  }
6618
7061
  /**
6619
7062
  * Adds a LIMIT clause to the query
@@ -6621,7 +7064,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6621
7064
  * @returns New query builder instance with the LIMIT clause
6622
7065
  */
6623
7066
  limit(n) {
6624
- const nextContext = this.applyAst(this.context, (service) => service.withLimit(n));
7067
+ const nextContext = this.predicateFacet.limit(this.context, n);
6625
7068
  return this.clone(nextContext);
6626
7069
  }
6627
7070
  /**
@@ -6630,7 +7073,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6630
7073
  * @returns New query builder instance with the OFFSET clause
6631
7074
  */
6632
7075
  offset(n) {
6633
- const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
7076
+ const nextContext = this.predicateFacet.offset(this.context, n);
6634
7077
  return this.clone(nextContext);
6635
7078
  }
6636
7079
  /**
@@ -6690,42 +7133,44 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
6690
7133
  * @param relationName - Name of the relationship to check
6691
7134
  * @param callback - Optional callback to modify the relationship query
6692
7135
  * @returns New query builder instance with the relationship existence check
7136
+ *
7137
+ * @example
7138
+ * qb.whereHas('posts', postQb => postQb.where(eq(postTable.columns.published, true)));
6693
7139
  */
6694
7140
  whereHas(relationName, callbackOrOptions, maybeOptions) {
6695
- const relation = this.env.table.relations[relationName];
6696
- if (!relation) {
6697
- throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
6698
- }
6699
- const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
6700
- const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
6701
- let subQb = this.createChildBuilder(relation.target);
6702
- if (callback) {
6703
- subQb = callback(subQb);
6704
- }
6705
- const subAst = subQb.getAST();
6706
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
6707
- return this.where(exists(finalSubAst));
7141
+ const predicate = buildWhereHasPredicate(
7142
+ this.env,
7143
+ this.context,
7144
+ this.relationFacet,
7145
+ (table) => this.createChildBuilder(table),
7146
+ relationName,
7147
+ callbackOrOptions,
7148
+ maybeOptions,
7149
+ false
7150
+ );
7151
+ return this.where(predicate);
6708
7152
  }
6709
7153
  /**
6710
7154
  * Adds a WHERE NOT EXISTS condition based on a relationship
6711
7155
  * @param relationName - Name of the relationship to check
6712
7156
  * @param callback - Optional callback to modify the relationship query
6713
7157
  * @returns New query builder instance with the relationship non-existence check
7158
+ *
7159
+ * @example
7160
+ * qb.whereHasNot('posts', postQb => postQb.where(eq(postTable.columns.published, true)));
6714
7161
  */
6715
7162
  whereHasNot(relationName, callbackOrOptions, maybeOptions) {
6716
- const relation = this.env.table.relations[relationName];
6717
- if (!relation) {
6718
- throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
6719
- }
6720
- const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
6721
- const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
6722
- let subQb = this.createChildBuilder(relation.target);
6723
- if (callback) {
6724
- subQb = callback(subQb);
6725
- }
6726
- const subAst = subQb.getAST();
6727
- const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
6728
- return this.where(notExists(finalSubAst));
7163
+ const predicate = buildWhereHasPredicate(
7164
+ this.env,
7165
+ this.context,
7166
+ this.relationFacet,
7167
+ (table) => this.createChildBuilder(table),
7168
+ relationName,
7169
+ callbackOrOptions,
7170
+ maybeOptions,
7171
+ true
7172
+ );
7173
+ return this.where(predicate);
6729
7174
  }
6730
7175
  /**
6731
7176
  * Compiles the query to SQL for a specific dialect
@@ -6857,23 +7302,44 @@ var resolveTableTarget = (target, tableMap) => {
6857
7302
  }
6858
7303
  return table;
6859
7304
  };
7305
+ var toSnakeCase = (value) => {
7306
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9_]+/gi, "_").replace(/__+/g, "_").replace(/^_|_$/g, "").toLowerCase();
7307
+ };
7308
+ var normalizeEntityName = (value) => {
7309
+ const stripped = value.replace(/Entity$/i, "");
7310
+ const normalized = toSnakeCase(stripped || value);
7311
+ return normalized || "unknown";
7312
+ };
7313
+ var getPivotKeyBaseFromTarget = (target) => {
7314
+ const resolved = unwrapTarget(target);
7315
+ if (isTableDef(resolved)) {
7316
+ return toSnakeCase(resolved.name || "unknown");
7317
+ }
7318
+ const ctor = resolved;
7319
+ return normalizeEntityName(ctor.name || "unknown");
7320
+ };
7321
+ var getPivotKeyBaseFromRoot = (meta) => {
7322
+ return normalizeEntityName(meta.target.name || meta.tableName || "unknown");
7323
+ };
6860
7324
  var buildRelationDefinitions = (meta, tableMap) => {
6861
7325
  const relations = {};
6862
7326
  for (const [name, relation] of Object.entries(meta.relations)) {
6863
7327
  switch (relation.kind) {
6864
7328
  case RelationKinds.HasOne: {
7329
+ const foreignKey = relation.foreignKey ?? `${getPivotKeyBaseFromRoot(meta)}_id`;
6865
7330
  relations[name] = hasOne(
6866
7331
  resolveTableTarget(relation.target, tableMap),
6867
- relation.foreignKey,
7332
+ foreignKey,
6868
7333
  relation.localKey,
6869
7334
  relation.cascade
6870
7335
  );
6871
7336
  break;
6872
7337
  }
6873
7338
  case RelationKinds.HasMany: {
7339
+ const foreignKey = relation.foreignKey ?? `${getPivotKeyBaseFromRoot(meta)}_id`;
6874
7340
  relations[name] = hasMany(
6875
7341
  resolveTableTarget(relation.target, tableMap),
6876
- relation.foreignKey,
7342
+ foreignKey,
6877
7343
  relation.localKey,
6878
7344
  relation.cascade
6879
7345
  );
@@ -6889,12 +7355,14 @@ var buildRelationDefinitions = (meta, tableMap) => {
6889
7355
  break;
6890
7356
  }
6891
7357
  case RelationKinds.BelongsToMany: {
7358
+ const pivotForeignKeyToRoot = relation.pivotForeignKeyToRoot ?? `${getPivotKeyBaseFromRoot(meta)}_id`;
7359
+ const pivotForeignKeyToTarget = relation.pivotForeignKeyToTarget ?? `${getPivotKeyBaseFromTarget(relation.target)}_id`;
6892
7360
  relations[name] = belongsToMany(
6893
7361
  resolveTableTarget(relation.target, tableMap),
6894
7362
  resolveTableTarget(relation.pivotTable, tableMap),
6895
7363
  {
6896
- pivotForeignKeyToRoot: relation.pivotForeignKeyToRoot,
6897
- pivotForeignKeyToTarget: relation.pivotForeignKeyToTarget,
7364
+ pivotForeignKeyToRoot,
7365
+ pivotForeignKeyToTarget,
6898
7366
  localKey: relation.localKey,
6899
7367
  targetKey: relation.targetKey,
6900
7368
  pivotPrimaryKey: relation.pivotPrimaryKey,
@@ -6975,6 +7443,8 @@ function esel(entity, ...props) {
6975
7443
 
6976
7444
  // src/query-builder/insert-query-state.ts
6977
7445
  var InsertQueryState = class _InsertQueryState {
7446
+ table;
7447
+ ast;
6978
7448
  /**
6979
7449
  * Creates a new InsertQueryState instance
6980
7450
  * @param table - The table definition for the INSERT query
@@ -7095,6 +7565,8 @@ var InsertQueryState = class _InsertQueryState {
7095
7565
 
7096
7566
  // src/query-builder/insert.ts
7097
7567
  var InsertQueryBuilder = class _InsertQueryBuilder {
7568
+ table;
7569
+ state;
7098
7570
  /**
7099
7571
  * Creates a new InsertQueryBuilder instance
7100
7572
  * @param table - The table definition for the INSERT query
@@ -7194,6 +7666,8 @@ var isUpdateValue = (value) => {
7194
7666
  }
7195
7667
  };
7196
7668
  var UpdateQueryState = class _UpdateQueryState {
7669
+ table;
7670
+ ast;
7197
7671
  /**
7198
7672
  * Creates a new UpdateQueryState instance
7199
7673
  * @param table - Table definition for the update
@@ -7304,6 +7778,8 @@ var UpdateQueryState = class _UpdateQueryState {
7304
7778
 
7305
7779
  // src/query-builder/update.ts
7306
7780
  var UpdateQueryBuilder = class _UpdateQueryBuilder {
7781
+ table;
7782
+ state;
7307
7783
  /**
7308
7784
  * Creates a new UpdateQueryBuilder instance
7309
7785
  * @param table - The table definition for the UPDATE query
@@ -7421,6 +7897,8 @@ var isTableSourceNode = (source) => typeof source.type === "string";
7421
7897
 
7422
7898
  // src/query-builder/delete-query-state.ts
7423
7899
  var DeleteQueryState = class _DeleteQueryState {
7900
+ table;
7901
+ ast;
7424
7902
  /**
7425
7903
  * Creates a new DeleteQueryState instance
7426
7904
  * @param table - The table definition for the DELETE query
@@ -7499,6 +7977,8 @@ var DeleteQueryState = class _DeleteQueryState {
7499
7977
 
7500
7978
  // src/query-builder/delete.ts
7501
7979
  var DeleteQueryBuilder = class _DeleteQueryBuilder {
7980
+ table;
7981
+ state;
7502
7982
  /**
7503
7983
  * Creates a new DeleteQueryBuilder instance
7504
7984
  * @param table - The table definition for the DELETE query
@@ -7626,7 +8106,8 @@ var renderColumnDefinition = (table, col2, dialect, options = {}) => {
7626
8106
  if (col2.default !== void 0) {
7627
8107
  parts.push(`DEFAULT ${dialect.renderDefault(col2.default, col2)}`);
7628
8108
  }
7629
- if (options.includePrimary && col2.primary) {
8109
+ const autoIncIncludesPrimary = typeof autoInc === "string" && /\bPRIMARY\s+KEY\b/i.test(autoInc);
8110
+ if (options.includePrimary && col2.primary && !autoIncIncludesPrimary) {
7630
8111
  parts.push("PRIMARY KEY");
7631
8112
  }
7632
8113
  if (col2.check) {
@@ -7684,6 +8165,16 @@ var generateSchemaSql = (tables, dialect) => {
7684
8165
  });
7685
8166
  return statements;
7686
8167
  };
8168
+ var generateSchemaSqlFor = (dialect, ...tables) => generateSchemaSql(tables, dialect);
8169
+ var executeSchemaSql = async (executor, tables, dialect) => {
8170
+ const statements = generateSchemaSql(tables, dialect);
8171
+ for (const sql of statements) {
8172
+ await executor.executeSql(sql);
8173
+ }
8174
+ };
8175
+ var executeSchemaSqlFor = async (executor, dialect, ...tables) => {
8176
+ await executeSchemaSql(executor, tables, dialect);
8177
+ };
7687
8178
  var orderTablesByDependencies = (tables) => {
7688
8179
  const map = /* @__PURE__ */ new Map();
7689
8180
  tables.forEach((t) => map.set(t.name, t));
@@ -9590,6 +10081,7 @@ var arrayAppend = (array, value) => fn7("ARRAY_APPEND", [array, value]);
9590
10081
 
9591
10082
  // src/orm/als.ts
9592
10083
  var AsyncLocalStorage = class {
10084
+ store;
9593
10085
  /**
9594
10086
  * Executes a callback function within a context containing the specified store value.
9595
10087
  * The store value is only available during the callback's execution and is automatically
@@ -10086,9 +10578,7 @@ var TypeScriptGenerator = class {
10086
10578
 
10087
10579
  // src/orm/identity-map.ts
10088
10580
  var IdentityMap = class {
10089
- constructor() {
10090
- this.buckets = /* @__PURE__ */ new Map();
10091
- }
10581
+ buckets = /* @__PURE__ */ new Map();
10092
10582
  get bucketsMap() {
10093
10583
  return this.buckets;
10094
10584
  }
@@ -10158,8 +10648,8 @@ var UnitOfWork = class {
10158
10648
  this.executor = executor;
10159
10649
  this.identityMap = identityMap;
10160
10650
  this.hookContext = hookContext;
10161
- this.trackedEntities = /* @__PURE__ */ new Map();
10162
10651
  }
10652
+ trackedEntities = /* @__PURE__ */ new Map();
10163
10653
  /**
10164
10654
  * Gets the identity buckets map.
10165
10655
  */
@@ -10489,12 +10979,12 @@ var UnitOfWork = class {
10489
10979
 
10490
10980
  // src/orm/domain-event-bus.ts
10491
10981
  var DomainEventBus = class {
10982
+ handlers = /* @__PURE__ */ new Map();
10492
10983
  /**
10493
10984
  * Creates a new DomainEventBus instance.
10494
10985
  * @param initialHandlers - Optional initial event handlers
10495
10986
  */
10496
10987
  constructor(initialHandlers) {
10497
- this.handlers = /* @__PURE__ */ new Map();
10498
10988
  if (initialHandlers) {
10499
10989
  for (const key in initialHandlers) {
10500
10990
  const type = key;
@@ -10563,8 +11053,8 @@ var RelationChangeProcessor = class {
10563
11053
  this.unitOfWork = unitOfWork;
10564
11054
  this.dialect = dialect;
10565
11055
  this.executor = executor;
10566
- this.relationChanges = [];
10567
11056
  }
11057
+ relationChanges = [];
10568
11058
  /**
10569
11059
  * Registers a relation change for processing.
10570
11060
  * @param entry - The relation change entry
@@ -10998,25 +11488,24 @@ var saveGraphInternal = async (session, entityClass, payload, options = {}) => {
10998
11488
 
10999
11489
  // src/orm/orm-session.ts
11000
11490
  var OrmSession = class {
11491
+ /** The ORM instance */
11492
+ orm;
11493
+ /** The database executor */
11494
+ executor;
11495
+ /** The identity map for tracking entity instances */
11496
+ identityMap;
11497
+ /** The unit of work for tracking entity changes */
11498
+ unitOfWork;
11499
+ /** The domain event bus */
11500
+ domainEvents;
11501
+ /** The relation change processor */
11502
+ relationChanges;
11503
+ interceptors;
11001
11504
  /**
11002
11505
  * Creates a new OrmSession instance.
11003
11506
  * @param opts - Session options
11004
11507
  */
11005
11508
  constructor(opts) {
11006
- /**
11007
- * Registers a relation change.
11008
- * @param root - The root entity
11009
- * @param relationKey - The relation key
11010
- * @param rootTable - The root table definition
11011
- * @param relationName - The relation name
11012
- * @param relation - The relation definition
11013
- * @param change - The relation change
11014
- */
11015
- this.registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
11016
- this.relationChanges.registerChange(
11017
- buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
11018
- );
11019
- };
11020
11509
  this.orm = opts.orm;
11021
11510
  this.executor = createQueryLoggingExecutor(opts.executor, opts.queryLogger);
11022
11511
  this.interceptors = [...opts.interceptors ?? []];
@@ -11105,6 +11594,20 @@ var OrmSession = class {
11105
11594
  markRemoved(entity) {
11106
11595
  this.unitOfWork.markRemoved(entity);
11107
11596
  }
11597
+ /**
11598
+ * Registers a relation change.
11599
+ * @param root - The root entity
11600
+ * @param relationKey - The relation key
11601
+ * @param rootTable - The root table definition
11602
+ * @param relationName - The relation name
11603
+ * @param relation - The relation definition
11604
+ * @param change - The relation change
11605
+ */
11606
+ registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
11607
+ this.relationChanges.registerChange(
11608
+ buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
11609
+ );
11610
+ };
11108
11611
  /**
11109
11612
  * Gets all tracked entities for a specific table.
11110
11613
  * @param table - The table definition
@@ -11310,9 +11813,7 @@ var buildRelationChangeEntry = (root, relationKey, rootTable, relationName, rela
11310
11813
 
11311
11814
  // src/orm/interceptor-pipeline.ts
11312
11815
  var InterceptorPipeline = class {
11313
- constructor() {
11314
- this.interceptors = [];
11315
- }
11816
+ interceptors = [];
11316
11817
  use(interceptor) {
11317
11818
  this.interceptors.push(interceptor);
11318
11819
  }
@@ -11331,6 +11832,13 @@ var InterceptorPipeline = class {
11331
11832
 
11332
11833
  // src/orm/orm.ts
11333
11834
  var Orm = class {
11835
+ /** The database dialect */
11836
+ dialect;
11837
+ /** The interceptors pipeline */
11838
+ interceptors;
11839
+ /** The naming strategy */
11840
+ namingStrategy;
11841
+ executorFactory;
11334
11842
  /**
11335
11843
  * Creates a new ORM instance.
11336
11844
  * @param opts - ORM options
@@ -11387,17 +11895,13 @@ var jsonify = (value) => {
11387
11895
 
11388
11896
  // src/decorators/decorator-metadata.ts
11389
11897
  var METADATA_KEY = "metal-orm:decorators";
11390
- var isStandardDecoratorContext = (value) => {
11391
- return typeof value === "object" && value !== null && "kind" in value;
11392
- };
11393
11898
  var getOrCreateMetadataBag = (context) => {
11394
11899
  const metadata = context.metadata || (context.metadata = {});
11395
- const existing = metadata[METADATA_KEY];
11396
- if (existing) {
11397
- return existing;
11900
+ let bag = metadata[METADATA_KEY];
11901
+ if (!bag) {
11902
+ bag = { columns: [], relations: [] };
11903
+ metadata[METADATA_KEY] = bag;
11398
11904
  }
11399
- const bag = { columns: [], relations: [] };
11400
- metadata[METADATA_KEY] = bag;
11401
11905
  return bag;
11402
11906
  };
11403
11907
  var readMetadataBag = (context) => {
@@ -11412,57 +11916,50 @@ var readMetadataBagFromConstructor = (ctor) => {
11412
11916
  var getDecoratorMetadata = (ctor) => readMetadataBagFromConstructor(ctor);
11413
11917
 
11414
11918
  // src/decorators/entity.ts
11415
- var toSnakeCase = (value) => {
11919
+ var toSnakeCase2 = (value) => {
11416
11920
  return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9_]+/gi, "_").replace(/__+/g, "_").replace(/^_|_$/g, "").toLowerCase();
11417
11921
  };
11418
11922
  var deriveTableNameFromConstructor = (ctor) => {
11419
11923
  const fallback = "unknown";
11420
11924
  const rawName = ctor.name || fallback;
11421
11925
  const strippedName = rawName.replace(/Entity$/i, "");
11422
- const normalized = toSnakeCase(strippedName || rawName);
11926
+ const normalized = toSnakeCase2(strippedName || rawName);
11423
11927
  if (!normalized) {
11424
11928
  return fallback;
11425
11929
  }
11426
11930
  return normalized.endsWith("s") ? normalized : `${normalized}s`;
11427
11931
  };
11428
11932
  function Entity(options = {}) {
11429
- const decorator = (value) => {
11430
- const tableName = options.tableName ?? deriveTableNameFromConstructor(value);
11431
- setEntityTableName(value, tableName, options.hooks);
11432
- return value;
11433
- };
11434
- const decoratorWithContext = (value, context) => {
11933
+ return function(value, context) {
11435
11934
  const ctor = value;
11436
- decorator(ctor);
11437
- if (context && isStandardDecoratorContext(context)) {
11438
- const bag = readMetadataBag(context);
11439
- if (bag) {
11440
- const meta = ensureEntityMetadata(ctor);
11441
- for (const entry of bag.columns) {
11442
- if (meta.columns[entry.propertyName]) {
11443
- throw new Error(
11444
- `Column '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
11445
- );
11446
- }
11447
- addColumnMetadata(ctor, entry.propertyName, { ...entry.column });
11935
+ const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
11936
+ setEntityTableName(ctor, tableName, options.hooks);
11937
+ const bag = readMetadataBag(context);
11938
+ if (bag) {
11939
+ const meta = ensureEntityMetadata(ctor);
11940
+ for (const entry of bag.columns) {
11941
+ if (meta.columns[entry.propertyName]) {
11942
+ throw new Error(
11943
+ `Column '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
11944
+ );
11448
11945
  }
11449
- for (const entry of bag.relations) {
11450
- if (meta.relations[entry.propertyName]) {
11451
- throw new Error(
11452
- `Relation '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
11453
- );
11454
- }
11455
- const relationCopy = entry.relation.kind === RelationKinds.BelongsToMany ? {
11456
- ...entry.relation,
11457
- defaultPivotColumns: entry.relation.defaultPivotColumns ? [...entry.relation.defaultPivotColumns] : void 0
11458
- } : { ...entry.relation };
11459
- addRelationMetadata(ctor, entry.propertyName, relationCopy);
11946
+ addColumnMetadata(ctor, entry.propertyName, { ...entry.column });
11947
+ }
11948
+ for (const entry of bag.relations) {
11949
+ if (meta.relations[entry.propertyName]) {
11950
+ throw new Error(
11951
+ `Relation '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
11952
+ );
11460
11953
  }
11954
+ const relationCopy = entry.relation.kind === RelationKinds.BelongsToMany ? {
11955
+ ...entry.relation,
11956
+ defaultPivotColumns: entry.relation.defaultPivotColumns ? [...entry.relation.defaultPivotColumns] : void 0
11957
+ } : { ...entry.relation };
11958
+ addRelationMetadata(ctor, entry.propertyName, relationCopy);
11461
11959
  }
11462
11960
  }
11463
11961
  return ctor;
11464
11962
  };
11465
- return decoratorWithContext;
11466
11963
  }
11467
11964
 
11468
11965
  // src/decorators/column-decorator.ts
@@ -11495,26 +11992,13 @@ var normalizePropertyName = (name) => {
11495
11992
  }
11496
11993
  return name;
11497
11994
  };
11498
- var resolveConstructor = (target) => {
11499
- if (typeof target === "function") {
11500
- return target;
11501
- }
11502
- if (target && typeof target.constructor === "function") {
11503
- return target.constructor;
11504
- }
11505
- return void 0;
11506
- };
11507
- var registerColumn = (ctor, propertyName, column) => {
11508
- const meta = ensureEntityMetadata(ctor);
11509
- if (meta.columns[propertyName]) {
11510
- return;
11511
- }
11512
- addColumnMetadata(ctor, propertyName, column);
11513
- };
11514
11995
  var registerColumnFromContext = (context, column) => {
11515
11996
  if (!context.name) {
11516
11997
  throw new Error("Column decorator requires a property name");
11517
11998
  }
11999
+ if (context.private) {
12000
+ throw new Error("Column decorator does not support private fields");
12001
+ }
11518
12002
  const propertyName = normalizePropertyName(context.name);
11519
12003
  const bag = getOrCreateMetadataBag(context);
11520
12004
  if (!bag.columns.some((entry) => entry.propertyName === propertyName)) {
@@ -11523,19 +12007,9 @@ var registerColumnFromContext = (context, column) => {
11523
12007
  };
11524
12008
  function Column(definition) {
11525
12009
  const normalized = normalizeColumnInput(definition);
11526
- const decorator = (targetOrValue, propertyKeyOrContext) => {
11527
- if (isStandardDecoratorContext(propertyKeyOrContext)) {
11528
- registerColumnFromContext(propertyKeyOrContext, normalized);
11529
- return;
11530
- }
11531
- const propertyName = normalizePropertyName(propertyKeyOrContext);
11532
- const ctor = resolveConstructor(targetOrValue);
11533
- if (!ctor) {
11534
- throw new Error("Unable to resolve constructor when registering column metadata");
11535
- }
11536
- registerColumn(ctor, propertyName, { ...normalized });
12010
+ return function(_value, context) {
12011
+ registerColumnFromContext(context, normalized);
11537
12012
  };
11538
- return decorator;
11539
12013
  }
11540
12014
  function PrimaryKey(definition) {
11541
12015
  const normalized = normalizeColumnInput(definition);
@@ -11550,41 +12024,21 @@ var normalizePropertyName2 = (name) => {
11550
12024
  }
11551
12025
  return name;
11552
12026
  };
11553
- var resolveConstructor2 = (instanceOrCtor) => {
11554
- if (typeof instanceOrCtor === "function") {
11555
- return instanceOrCtor;
11556
- }
11557
- if (instanceOrCtor && typeof instanceOrCtor.constructor === "function") {
11558
- return instanceOrCtor.constructor;
11559
- }
11560
- return void 0;
11561
- };
11562
- var registerRelation = (ctor, propertyName, metadata) => {
11563
- addRelationMetadata(ctor, propertyName, metadata);
11564
- };
11565
12027
  var createFieldDecorator = (metadataFactory) => {
11566
- const decorator = (targetOrValue, propertyKeyOrContext) => {
11567
- if (isStandardDecoratorContext(propertyKeyOrContext)) {
11568
- const ctx = propertyKeyOrContext;
11569
- if (!ctx.name) {
11570
- throw new Error("Relation decorator requires a property name");
11571
- }
11572
- const propertyName2 = normalizePropertyName2(ctx.name);
11573
- const bag = getOrCreateMetadataBag(ctx);
11574
- const relationMetadata = metadataFactory(propertyName2);
11575
- if (!bag.relations.some((entry) => entry.propertyName === propertyName2)) {
11576
- bag.relations.push({ propertyName: propertyName2, relation: relationMetadata });
11577
- }
11578
- return;
12028
+ return function(_value, context) {
12029
+ if (!context.name) {
12030
+ throw new Error("Relation decorator requires a property name");
11579
12031
  }
11580
- const propertyName = normalizePropertyName2(propertyKeyOrContext);
11581
- const ctor = resolveConstructor2(targetOrValue);
11582
- if (!ctor) {
11583
- throw new Error("Unable to resolve constructor when registering relation metadata");
12032
+ if (context.private) {
12033
+ throw new Error("Relation decorator does not support private fields");
12034
+ }
12035
+ const propertyName = normalizePropertyName2(context.name);
12036
+ const bag = getOrCreateMetadataBag(context);
12037
+ const relationMetadata = metadataFactory(propertyName);
12038
+ if (!bag.relations.some((entry) => entry.propertyName === propertyName)) {
12039
+ bag.relations.push({ propertyName, relation: relationMetadata });
11584
12040
  }
11585
- registerRelation(ctor, propertyName, metadataFactory(propertyName));
11586
12041
  };
11587
- return decorator;
11588
12042
  };
11589
12043
  function HasMany(options) {
11590
12044
  return createFieldDecorator((propertyName) => ({
@@ -11611,7 +12065,7 @@ function BelongsTo(options) {
11611
12065
  kind: RelationKinds.BelongsTo,
11612
12066
  propertyKey: propertyName,
11613
12067
  target: options.target,
11614
- foreignKey: options.foreignKey,
12068
+ foreignKey: options.foreignKey ?? `${propertyName}_id`,
11615
12069
  localKey: options.localKey,
11616
12070
  cascade: options.cascade
11617
12071
  }));
@@ -11687,13 +12141,15 @@ var deferred = () => {
11687
12141
  return { promise, resolve, reject };
11688
12142
  };
11689
12143
  var Pool = class {
12144
+ adapter;
12145
+ options;
12146
+ destroyed = false;
12147
+ creating = 0;
12148
+ leased = 0;
12149
+ idle = [];
12150
+ waiters = [];
12151
+ reapTimer = null;
11690
12152
  constructor(adapter, options) {
11691
- this.destroyed = false;
11692
- this.creating = 0;
11693
- this.leased = 0;
11694
- this.idle = [];
11695
- this.waiters = [];
11696
- this.reapTimer = null;
11697
12153
  if (!Number.isFinite(options.max) || options.max <= 0) {
11698
12154
  throw new Error("Pool options.max must be a positive number");
11699
12155
  }
@@ -12256,6 +12712,8 @@ function createPooledExecutorFactory(opts) {
12256
12712
  esel,
12257
12713
  executeHydrated,
12258
12714
  executeHydratedWithContexts,
12715
+ executeSchemaSql,
12716
+ executeSchemaSqlFor,
12259
12717
  exists,
12260
12718
  exp,
12261
12719
  extract,
@@ -12264,6 +12722,7 @@ function createPooledExecutorFactory(opts) {
12264
12722
  fromUnixTime,
12265
12723
  generateCreateTableSql,
12266
12724
  generateSchemaSql,
12725
+ generateSchemaSqlFor,
12267
12726
  getColumn,
12268
12727
  getDecoratorMetadata,
12269
12728
  getSchemaIntrospector,
@@ -12369,6 +12828,7 @@ function createPooledExecutorFactory(opts) {
12369
12828
  second,
12370
12829
  sel,
12371
12830
  selectFromEntity,
12831
+ setRelations,
12372
12832
  sha1,
12373
12833
  sha2,
12374
12834
  shiftLeft,