metal-orm 1.0.57 → 1.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -241,6 +241,7 @@ __export(index_exports, {
241
241
  registerExpressionDispatcher: () => registerExpressionDispatcher,
242
242
  registerOperandDispatcher: () => registerOperandDispatcher,
243
243
  registerSchemaIntrospector: () => registerSchemaIntrospector,
244
+ relationLoaderCache: () => relationLoaderCache,
244
245
  renderColumnDefinition: () => renderColumnDefinition,
245
246
  renderTypeWithArgs: () => renderTypeWithArgs,
246
247
  repeat: () => repeat,
@@ -4111,20 +4112,21 @@ var RelationProjectionHelper = class {
4111
4112
  var assertNever = (value) => {
4112
4113
  throw new Error(`Unhandled relation type: ${JSON.stringify(value)}`);
4113
4114
  };
4114
- var baseRelationCondition = (root, relation, rootAlias) => {
4115
+ var baseRelationCondition = (root, relation, rootAlias, targetTableName) => {
4115
4116
  const rootTable = rootAlias || root.name;
4117
+ const targetTable = targetTableName ?? relation.target.name;
4116
4118
  const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
4117
4119
  const localKey = relation.localKey || defaultLocalKey;
4118
4120
  switch (relation.type) {
4119
4121
  case RelationKinds.HasMany:
4120
4122
  case RelationKinds.HasOne:
4121
4123
  return eq(
4122
- { type: "Column", table: relation.target.name, name: relation.foreignKey },
4124
+ { type: "Column", table: targetTable, name: relation.foreignKey },
4123
4125
  { type: "Column", table: rootTable, name: localKey }
4124
4126
  );
4125
4127
  case RelationKinds.BelongsTo:
4126
4128
  return eq(
4127
- { type: "Column", table: relation.target.name, name: localKey },
4129
+ { type: "Column", table: targetTable, name: localKey },
4128
4130
  { type: "Column", table: rootTable, name: relation.foreignKey }
4129
4131
  );
4130
4132
  case RelationKinds.BelongsToMany:
@@ -4133,7 +4135,7 @@ var baseRelationCondition = (root, relation, rootAlias) => {
4133
4135
  return assertNever(relation);
4134
4136
  }
4135
4137
  };
4136
- var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias) => {
4138
+ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, rootAlias, targetTable, targetTableName) => {
4137
4139
  const rootKey = relation.localKey || findPrimaryKey(root);
4138
4140
  const targetKey = relation.targetKey || findPrimaryKey(relation.target);
4139
4141
  const rootTable = rootAlias || root.name;
@@ -4146,8 +4148,14 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, ro
4146
4148
  { type: "Table", name: relation.pivotTable.name, schema: relation.pivotTable.schema },
4147
4149
  pivotCondition
4148
4150
  );
4151
+ const targetSource = targetTable ?? {
4152
+ type: "Table",
4153
+ name: relation.target.name,
4154
+ schema: relation.target.schema
4155
+ };
4156
+ const effectiveTargetName = targetTableName ?? relation.target.name;
4149
4157
  let targetCondition = eq(
4150
- { type: "Column", table: relation.target.name, name: targetKey },
4158
+ { type: "Column", table: effectiveTargetName, name: targetKey },
4151
4159
  { type: "Column", table: relation.pivotTable.name, name: relation.pivotForeignKeyToTarget }
4152
4160
  );
4153
4161
  if (extra) {
@@ -4155,24 +4163,25 @@ var buildBelongsToManyJoins = (root, relationName, relation, joinKind, extra, ro
4155
4163
  }
4156
4164
  const targetJoin = createJoinNode(
4157
4165
  joinKind,
4158
- { type: "Table", name: relation.target.name, schema: relation.target.schema },
4166
+ targetSource,
4159
4167
  targetCondition,
4160
4168
  relationName
4161
4169
  );
4162
4170
  return [pivotJoin, targetJoin];
4163
4171
  };
4164
- var buildRelationJoinCondition = (root, relation, extra, rootAlias) => {
4165
- const base = baseRelationCondition(root, relation, rootAlias);
4172
+ var buildRelationJoinCondition = (root, relation, extra, rootAlias, targetTableName) => {
4173
+ const base = baseRelationCondition(root, relation, rootAlias, targetTableName);
4166
4174
  return extra ? and(base, extra) : base;
4167
4175
  };
4168
- var buildRelationCorrelation = (root, relation, rootAlias) => {
4169
- return baseRelationCondition(root, relation, rootAlias);
4176
+ var buildRelationCorrelation = (root, relation, rootAlias, targetTableName) => {
4177
+ return baseRelationCondition(root, relation, rootAlias, targetTableName);
4170
4178
  };
4171
4179
 
4172
4180
  // src/core/ast/join-metadata.ts
4173
4181
  var getJoinRelationName = (join) => join.meta?.relationName;
4174
4182
 
4175
4183
  // src/query-builder/relation-service.ts
4184
+ var hasRelationForeignKey = (relation) => relation.type !== RelationKinds.BelongsToMany;
4176
4185
  var RelationService = class {
4177
4186
  /**
4178
4187
  * Creates a new RelationService instance
@@ -4197,8 +4206,8 @@ var RelationService = class {
4197
4206
  * @param extraCondition - Additional join condition
4198
4207
  * @returns Relation result with updated state and hydration
4199
4208
  */
4200
- joinRelation(relationName, joinKind, extraCondition) {
4201
- const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition);
4209
+ joinRelation(relationName, joinKind, extraCondition, tableSource) {
4210
+ const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition, tableSource);
4202
4211
  return { state: nextState, hydration: this.hydration };
4203
4212
  }
4204
4213
  /**
@@ -4227,14 +4236,58 @@ var RelationService = class {
4227
4236
  const relation = this.getRelation(relationName);
4228
4237
  const aliasPrefix = options?.aliasPrefix ?? relationName;
4229
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
+ }
4230
4255
  if (!alreadyJoined) {
4231
- const joined = this.joinRelation(relationName, options?.joinKind ?? JOIN_KINDS.LEFT, options?.filter);
4232
- state = joined.state;
4256
+ state = this.withJoin(
4257
+ state,
4258
+ relationName,
4259
+ options?.joinKind ?? JOIN_KINDS.LEFT,
4260
+ joinCondition,
4261
+ tableSourceOverride
4262
+ );
4233
4263
  }
4234
4264
  const projectionResult = this.projectionHelper.ensureBaseProjection(state, hydration);
4235
4265
  state = projectionResult.state;
4236
4266
  hydration = projectionResult.hydration;
4237
- const targetColumns = options?.columns?.length ? options.columns : Object.keys(relation.target.columns);
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
+ }
4283
+ }
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;
4238
4291
  const buildTypedSelection = (columns, prefix, keys, missingMsg) => {
4239
4292
  return keys.reduce((acc, key) => {
4240
4293
  const def = columns[key];
@@ -4318,27 +4371,42 @@ var RelationService = class {
4318
4371
  * @param extraCondition - Additional join condition
4319
4372
  * @returns Updated query state with join
4320
4373
  */
4321
- withJoin(state, relationName, joinKind, extraCondition) {
4374
+ withJoin(state, relationName, joinKind, extraCondition, tableSource) {
4322
4375
  const relation = this.getRelation(relationName);
4323
4376
  const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
4324
4377
  if (relation.type === RelationKinds.BelongsToMany) {
4378
+ const targetTableSource = tableSource ?? {
4379
+ type: "Table",
4380
+ name: relation.target.name,
4381
+ schema: relation.target.schema
4382
+ };
4383
+ const targetName2 = this.resolveTargetTableName(targetTableSource, relation);
4325
4384
  const joins = buildBelongsToManyJoins(
4326
4385
  this.table,
4327
4386
  relationName,
4328
4387
  relation,
4329
4388
  joinKind,
4330
4389
  extraCondition,
4331
- rootAlias
4390
+ rootAlias,
4391
+ targetTableSource,
4392
+ targetName2
4332
4393
  );
4333
4394
  return joins.reduce((current, join) => this.astService(current).withJoin(join), state);
4334
4395
  }
4335
- const condition = buildRelationJoinCondition(this.table, relation, extraCondition, rootAlias);
4336
- const joinNode = createJoinNode(
4337
- joinKind,
4338
- { type: "Table", name: relation.target.name, schema: relation.target.schema },
4339
- condition,
4340
- relationName
4396
+ const targetTable = tableSource ?? {
4397
+ type: "Table",
4398
+ name: relation.target.name,
4399
+ schema: relation.target.schema
4400
+ };
4401
+ const targetName = this.resolveTargetTableName(targetTable, relation);
4402
+ const condition = buildRelationJoinCondition(
4403
+ this.table,
4404
+ relation,
4405
+ extraCondition,
4406
+ rootAlias,
4407
+ targetName
4341
4408
  );
4409
+ const joinNode = createJoinNode(joinKind, targetTable, condition, relationName);
4342
4410
  return this.astService(state).withJoin(joinNode);
4343
4411
  }
4344
4412
  /**
@@ -4355,6 +4423,198 @@ var RelationService = class {
4355
4423
  hydration: hydration.onColumnsSelected(nextState, addedColumns)
4356
4424
  };
4357
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];
4454
+ }
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
+ }
4463
+ }
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;
4509
+ }
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;
4561
+ }
4562
+ }
4563
+ collectFromOrderingTerm(term, collector) {
4564
+ if (isOperandNode(term)) {
4565
+ this.collectFromOperand(term, collector);
4566
+ return;
4567
+ }
4568
+ this.collectFromExpression(term, collector);
4569
+ }
4570
+ createFilteredRelationCte(state, relationName, relation, filters) {
4571
+ const cteName = this.generateUniqueCteName(state, relationName);
4572
+ const predicate = this.combineWithAnd(filters);
4573
+ if (!predicate) {
4574
+ throw new Error("Unable to build filter CTE without predicates.");
4575
+ }
4576
+ const columns = Object.keys(relation.target.columns).map((name) => ({
4577
+ type: "Column",
4578
+ table: relation.target.name,
4579
+ name
4580
+ }));
4581
+ const cteQuery = {
4582
+ type: "SelectQuery",
4583
+ from: { type: "Table", name: relation.target.name, schema: relation.target.schema },
4584
+ columns,
4585
+ joins: [],
4586
+ where: predicate
4587
+ };
4588
+ const nextState = this.astService(state).withCte(cteName, cteQuery);
4589
+ const tableNode3 = {
4590
+ type: "Table",
4591
+ name: cteName,
4592
+ alias: relation.target.name
4593
+ };
4594
+ return { state: nextState, table: tableNode3 };
4595
+ }
4596
+ generateUniqueCteName(state, relationName) {
4597
+ const existing = new Set((state.ast.ctes ?? []).map((cte) => cte.name));
4598
+ let candidate = `${relationName}__filtered`;
4599
+ let suffix = 1;
4600
+ while (existing.has(candidate)) {
4601
+ candidate = `${relationName}__filtered_${suffix}`;
4602
+ suffix += 1;
4603
+ }
4604
+ return candidate;
4605
+ }
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;
4617
+ }
4358
4618
  /**
4359
4619
  * Gets a relation definition by name
4360
4620
  * @param relationName - Name of the relation
@@ -4705,6 +4965,18 @@ var DefaultHasManyCollection = class {
4705
4965
  getItems() {
4706
4966
  return this.items;
4707
4967
  }
4968
+ /**
4969
+ * Array-compatible length for testing frameworks.
4970
+ */
4971
+ get length() {
4972
+ return this.items.length;
4973
+ }
4974
+ /**
4975
+ * Enables iteration over the collection like an array.
4976
+ */
4977
+ [Symbol.iterator]() {
4978
+ return this.items[Symbol.iterator]();
4979
+ }
4708
4980
  /**
4709
4981
  * Adds a new child entity to the collection.
4710
4982
  * @param data - Partial data for the new entity
@@ -5086,6 +5358,18 @@ var DefaultManyToManyCollection = class {
5086
5358
  getItems() {
5087
5359
  return this.items;
5088
5360
  }
5361
+ /**
5362
+ * Array-compatible length for testing frameworks.
5363
+ */
5364
+ get length() {
5365
+ return this.items.length;
5366
+ }
5367
+ /**
5368
+ * Enables iteration over the collection like an array.
5369
+ */
5370
+ [Symbol.iterator]() {
5371
+ return this.items[Symbol.iterator]();
5372
+ }
5089
5373
  /**
5090
5374
  * Attaches an entity to the collection.
5091
5375
  * Registers an 'attach' change in the entity context.
@@ -5193,10 +5477,27 @@ var DefaultManyToManyCollection = class {
5193
5477
  };
5194
5478
 
5195
5479
  // src/orm/lazy-batch.ts
5196
- var selectAllColumns = (table) => Object.entries(table.columns).reduce((acc, [name, def]) => {
5197
- acc[name] = def;
5198
- return acc;
5199
- }, {});
5480
+ var hasColumns = (columns) => Boolean(columns && columns.length > 0);
5481
+ var buildColumnSelection = (table, columns, missingMsg) => {
5482
+ return columns.reduce((acc, column) => {
5483
+ const def = table.columns[column];
5484
+ if (!def) {
5485
+ throw new Error(missingMsg(column));
5486
+ }
5487
+ acc[column] = def;
5488
+ return acc;
5489
+ }, {});
5490
+ };
5491
+ var filterRow = (row, columns) => {
5492
+ const filtered = {};
5493
+ for (const column of columns) {
5494
+ if (column in row) {
5495
+ filtered[column] = row[column];
5496
+ }
5497
+ }
5498
+ return filtered;
5499
+ };
5500
+ var filterRows = (rows, columns) => rows.map((row) => filterRow(row, columns));
5200
5501
  var rowsFromResults = (results) => {
5201
5502
  const rows = [];
5202
5503
  for (const result of results) {
@@ -5228,9 +5529,12 @@ var collectKeysFromRoots = (roots, key) => {
5228
5529
  return collected;
5229
5530
  };
5230
5531
  var buildInListValues = (keys) => Array.from(keys);
5231
- var fetchRowsForKeys = async (ctx, table, column, keys) => {
5232
- const qb = new SelectQueryBuilder(table).select(selectAllColumns(table));
5233
- qb.where(inList(column, buildInListValues(keys)));
5532
+ var fetchRowsForKeys = async (ctx, table, column, keys, selection, filter) => {
5533
+ let qb = new SelectQueryBuilder(table).select(selection);
5534
+ qb = qb.where(inList(column, buildInListValues(keys)));
5535
+ if (filter) {
5536
+ qb = qb.where(filter);
5537
+ }
5234
5538
  return executeQuery(ctx, qb);
5235
5539
  };
5236
5540
  var groupRowsByMany = (rows, keyColumn) => {
@@ -5257,7 +5561,7 @@ var groupRowsByUnique = (rows, keyColumn) => {
5257
5561
  }
5258
5562
  return lookup;
5259
5563
  };
5260
- var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
5564
+ var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options) => {
5261
5565
  const localKey = relation.localKey || findPrimaryKey(rootTable);
5262
5566
  const roots = ctx.getEntitiesForTable(rootTable);
5263
5567
  const keys = collectKeysFromRoots(roots, localKey);
@@ -5266,10 +5570,30 @@ var loadHasManyRelation = async (ctx, rootTable, _relationName, relation) => {
5266
5570
  }
5267
5571
  const fkColumn = relation.target.columns[relation.foreignKey];
5268
5572
  if (!fkColumn) return /* @__PURE__ */ new Map();
5269
- const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys);
5270
- return groupRowsByMany(rows, relation.foreignKey);
5573
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5574
+ const targetPrimaryKey = findPrimaryKey(relation.target);
5575
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
5576
+ if (!selectedColumns.includes(targetPrimaryKey)) {
5577
+ selectedColumns.push(targetPrimaryKey);
5578
+ }
5579
+ const queryColumns = new Set(selectedColumns);
5580
+ queryColumns.add(relation.foreignKey);
5581
+ const selection = buildColumnSelection(
5582
+ relation.target,
5583
+ Array.from(queryColumns),
5584
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5585
+ );
5586
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, options?.filter);
5587
+ const grouped = groupRowsByMany(rows, relation.foreignKey);
5588
+ if (!requestedColumns) return grouped;
5589
+ const visibleColumns = new Set(selectedColumns);
5590
+ const filtered = /* @__PURE__ */ new Map();
5591
+ for (const [key, bucket] of grouped.entries()) {
5592
+ filtered.set(key, filterRows(bucket, visibleColumns));
5593
+ }
5594
+ return filtered;
5271
5595
  };
5272
- var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
5596
+ var loadHasOneRelation = async (ctx, rootTable, relationName, relation, options) => {
5273
5597
  const localKey = relation.localKey || findPrimaryKey(rootTable);
5274
5598
  const roots = ctx.getEntitiesForTable(rootTable);
5275
5599
  const keys = collectKeysFromRoots(roots, localKey);
@@ -5278,22 +5602,98 @@ var loadHasOneRelation = async (ctx, rootTable, _relationName, relation) => {
5278
5602
  }
5279
5603
  const fkColumn = relation.target.columns[relation.foreignKey];
5280
5604
  if (!fkColumn) return /* @__PURE__ */ new Map();
5281
- const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys);
5282
- return groupRowsByUnique(rows, relation.foreignKey);
5605
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5606
+ const targetPrimaryKey = findPrimaryKey(relation.target);
5607
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
5608
+ if (!selectedColumns.includes(targetPrimaryKey)) {
5609
+ selectedColumns.push(targetPrimaryKey);
5610
+ }
5611
+ const queryColumns = new Set(selectedColumns);
5612
+ queryColumns.add(relation.foreignKey);
5613
+ const selection = buildColumnSelection(
5614
+ relation.target,
5615
+ Array.from(queryColumns),
5616
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5617
+ );
5618
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, options?.filter);
5619
+ const grouped = groupRowsByUnique(rows, relation.foreignKey);
5620
+ if (!requestedColumns) return grouped;
5621
+ const visibleColumns = new Set(selectedColumns);
5622
+ const filtered = /* @__PURE__ */ new Map();
5623
+ for (const [key, row] of grouped.entries()) {
5624
+ filtered.set(key, filterRow(row, visibleColumns));
5625
+ }
5626
+ return filtered;
5283
5627
  };
5284
- var loadBelongsToRelation = async (ctx, rootTable, _relationName, relation) => {
5628
+ var loadBelongsToRelation = async (ctx, rootTable, relationName, relation, options) => {
5285
5629
  const roots = ctx.getEntitiesForTable(rootTable);
5286
- const foreignKeys = collectKeysFromRoots(roots, relation.foreignKey);
5630
+ const getForeignKeys = () => collectKeysFromRoots(roots, relation.foreignKey);
5631
+ let foreignKeys = getForeignKeys();
5632
+ if (!foreignKeys.size) {
5633
+ const pkName = findPrimaryKey(rootTable);
5634
+ const pkColumn2 = rootTable.columns[pkName];
5635
+ const fkColumn = rootTable.columns[relation.foreignKey];
5636
+ if (pkColumn2 && fkColumn) {
5637
+ const missingKeys = /* @__PURE__ */ new Set();
5638
+ const entityByPk = /* @__PURE__ */ new Map();
5639
+ for (const tracked of roots) {
5640
+ const entity = tracked.entity;
5641
+ const pkValue = entity[pkName];
5642
+ if (pkValue === void 0 || pkValue === null) continue;
5643
+ const fkValue = entity[relation.foreignKey];
5644
+ if (fkValue === void 0 || fkValue === null) {
5645
+ missingKeys.add(pkValue);
5646
+ entityByPk.set(pkValue, entity);
5647
+ }
5648
+ }
5649
+ if (missingKeys.size) {
5650
+ const selection2 = buildColumnSelection(
5651
+ rootTable,
5652
+ [pkName, relation.foreignKey],
5653
+ (column) => `Column '${column}' not found on table '${rootTable.name}'`
5654
+ );
5655
+ const keyRows = await fetchRowsForKeys(ctx, rootTable, pkColumn2, missingKeys, selection2);
5656
+ for (const row of keyRows) {
5657
+ const pkValue = row[pkName];
5658
+ if (pkValue === void 0 || pkValue === null) continue;
5659
+ const entity = entityByPk.get(pkValue);
5660
+ if (!entity) continue;
5661
+ const fkValue = row[relation.foreignKey];
5662
+ if (fkValue !== void 0 && fkValue !== null) {
5663
+ entity[relation.foreignKey] = fkValue;
5664
+ }
5665
+ }
5666
+ foreignKeys = getForeignKeys();
5667
+ }
5668
+ }
5669
+ }
5287
5670
  if (!foreignKeys.size) {
5288
5671
  return /* @__PURE__ */ new Map();
5289
5672
  }
5290
5673
  const targetKey = relation.localKey || findPrimaryKey(relation.target);
5291
5674
  const pkColumn = relation.target.columns[targetKey];
5292
5675
  if (!pkColumn) return /* @__PURE__ */ new Map();
5293
- const rows = await fetchRowsForKeys(ctx, relation.target, pkColumn, foreignKeys);
5294
- return groupRowsByUnique(rows, targetKey);
5676
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5677
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
5678
+ if (!selectedColumns.includes(targetKey)) {
5679
+ selectedColumns.push(targetKey);
5680
+ }
5681
+ const selection = buildColumnSelection(
5682
+ relation.target,
5683
+ selectedColumns,
5684
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5685
+ );
5686
+ const rows = await fetchRowsForKeys(ctx, relation.target, pkColumn, foreignKeys, selection, options?.filter);
5687
+ const grouped = groupRowsByUnique(rows, targetKey);
5688
+ if (!requestedColumns) return grouped;
5689
+ const visibleColumns = new Set(selectedColumns);
5690
+ const filtered = /* @__PURE__ */ new Map();
5691
+ for (const [key, row] of grouped.entries()) {
5692
+ filtered.set(key, filterRow(row, visibleColumns));
5693
+ }
5694
+ return filtered;
5295
5695
  };
5296
- var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation) => {
5696
+ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, options) => {
5297
5697
  const rootKey = relation.localKey || findPrimaryKey(rootTable);
5298
5698
  const roots = ctx.getEntitiesForTable(rootTable);
5299
5699
  const rootIds = collectKeysFromRoots(roots, rootKey);
@@ -5302,9 +5702,29 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
5302
5702
  }
5303
5703
  const pivotColumn = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
5304
5704
  if (!pivotColumn) return /* @__PURE__ */ new Map();
5305
- const pivotRows = await fetchRowsForKeys(ctx, relation.pivotTable, pivotColumn, rootIds);
5705
+ const pivotColumnsRequested = hasColumns(options?.pivot?.columns) ? [...options.pivot.columns] : void 0;
5706
+ const useIncludeDefaults = options !== void 0;
5707
+ let pivotSelectedColumns;
5708
+ if (pivotColumnsRequested) {
5709
+ pivotSelectedColumns = [...pivotColumnsRequested];
5710
+ } else if (useIncludeDefaults) {
5711
+ const pivotPk = relation.pivotPrimaryKey || findPrimaryKey(relation.pivotTable);
5712
+ pivotSelectedColumns = relation.defaultPivotColumns ?? buildDefaultPivotColumns(relation, pivotPk);
5713
+ } else {
5714
+ pivotSelectedColumns = Object.keys(relation.pivotTable.columns);
5715
+ }
5716
+ const pivotQueryColumns = new Set(pivotSelectedColumns);
5717
+ pivotQueryColumns.add(relation.pivotForeignKeyToRoot);
5718
+ pivotQueryColumns.add(relation.pivotForeignKeyToTarget);
5719
+ const pivotSelection = buildColumnSelection(
5720
+ relation.pivotTable,
5721
+ Array.from(pivotQueryColumns),
5722
+ (column) => `Column '${column}' not found on pivot table '${relation.pivotTable.name}'`
5723
+ );
5724
+ const pivotRows = await fetchRowsForKeys(ctx, relation.pivotTable, pivotColumn, rootIds, pivotSelection);
5306
5725
  const rootLookup = /* @__PURE__ */ new Map();
5307
5726
  const targetIds = /* @__PURE__ */ new Set();
5727
+ const pivotVisibleColumns = new Set(pivotSelectedColumns);
5308
5728
  for (const pivot of pivotRows) {
5309
5729
  const rootValue = pivot[relation.pivotForeignKeyToRoot];
5310
5730
  const targetValue = pivot[relation.pivotForeignKeyToTarget];
@@ -5314,7 +5734,7 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
5314
5734
  const bucket = rootLookup.get(toKey6(rootValue)) ?? [];
5315
5735
  bucket.push({
5316
5736
  targetId: targetValue,
5317
- pivot: { ...pivot }
5737
+ pivot: pivotVisibleColumns.size ? filterRow(pivot, pivotVisibleColumns) : {}
5318
5738
  });
5319
5739
  rootLookup.set(toKey6(rootValue), bucket);
5320
5740
  targetIds.add(targetValue);
@@ -5325,8 +5745,19 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
5325
5745
  const targetKey = relation.targetKey || findPrimaryKey(relation.target);
5326
5746
  const targetPkColumn = relation.target.columns[targetKey];
5327
5747
  if (!targetPkColumn) return /* @__PURE__ */ new Map();
5328
- const targetRows = await fetchRowsForKeys(ctx, relation.target, targetPkColumn, targetIds);
5748
+ const targetRequestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
5749
+ const targetSelectedColumns = targetRequestedColumns ? [...targetRequestedColumns] : Object.keys(relation.target.columns);
5750
+ if (!targetSelectedColumns.includes(targetKey)) {
5751
+ targetSelectedColumns.push(targetKey);
5752
+ }
5753
+ const targetSelection = buildColumnSelection(
5754
+ relation.target,
5755
+ targetSelectedColumns,
5756
+ (column) => `Column '${column}' not found on relation '${relationName}'`
5757
+ );
5758
+ const targetRows = await fetchRowsForKeys(ctx, relation.target, targetPkColumn, targetIds, targetSelection, options?.filter);
5329
5759
  const targetMap = groupRowsByUnique(targetRows, targetKey);
5760
+ const targetVisibleColumns = new Set(targetSelectedColumns);
5330
5761
  const result = /* @__PURE__ */ new Map();
5331
5762
  for (const [rootId, entries] of rootLookup.entries()) {
5332
5763
  const bucket = [];
@@ -5334,7 +5765,7 @@ var loadBelongsToManyRelation = async (ctx, rootTable, _relationName, relation)
5334
5765
  const targetRow = targetMap.get(toKey6(entry.targetId));
5335
5766
  if (!targetRow) continue;
5336
5767
  bucket.push({
5337
- ...targetRow,
5768
+ ...targetRequestedColumns ? filterRow(targetRow, targetVisibleColumns) : targetRow,
5338
5769
  _pivot: entry.pivot
5339
5770
  });
5340
5771
  }
@@ -5364,12 +5795,13 @@ var relationLoaderCache = (meta, relationName, factory) => {
5364
5795
  }
5365
5796
  return promise;
5366
5797
  };
5367
- var createEntityProxy = (ctx, table, row, lazyRelations = []) => {
5798
+ var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
5368
5799
  const target = { ...row };
5369
5800
  const meta = {
5370
5801
  ctx,
5371
5802
  table,
5372
5803
  lazyRelations: [...lazyRelations],
5804
+ lazyRelationOptions: new Map(lazyRelationOptions),
5373
5805
  relationCache: /* @__PURE__ */ new Map(),
5374
5806
  relationHydration: /* @__PURE__ */ new Map(),
5375
5807
  relationWrappers: /* @__PURE__ */ new Map()
@@ -5410,14 +5842,14 @@ var createEntityProxy = (ctx, table, row, lazyRelations = []) => {
5410
5842
  populateHydrationCache(proxy, row, meta);
5411
5843
  return proxy;
5412
5844
  };
5413
- var createEntityFromRow = (ctx, table, row, lazyRelations = []) => {
5845
+ var createEntityFromRow = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
5414
5846
  const pkName = findPrimaryKey(table);
5415
5847
  const pkValue = row[pkName];
5416
5848
  if (pkValue !== void 0 && pkValue !== null) {
5417
5849
  const tracked = ctx.getEntity(table, pkValue);
5418
5850
  if (tracked) return tracked;
5419
5851
  }
5420
- const entity = createEntityProxy(ctx, table, row, lazyRelations);
5852
+ const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
5421
5853
  if (pkValue !== void 0 && pkValue !== null) {
5422
5854
  ctx.trackManaged(table, pkValue, entity);
5423
5855
  } else {
@@ -5467,6 +5899,58 @@ var populateHydrationCache = (entity, row, meta) => {
5467
5899
  }
5468
5900
  }
5469
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);
5910
+ }
5911
+ const getItems = target.getItems;
5912
+ if (typeof getItems === "function") {
5913
+ const items = getItems.call(target);
5914
+ if (items && prop in items) {
5915
+ const propName = prop;
5916
+ const value = items[propName];
5917
+ return typeof value === "function" ? value.bind(items) : value;
5918
+ }
5919
+ }
5920
+ const getRef = target.get;
5921
+ if (typeof getRef === "function") {
5922
+ const current = getRef.call(target);
5923
+ if (current && prop in current) {
5924
+ const propName = prop;
5925
+ const value = current[propName];
5926
+ return typeof value === "function" ? value.bind(current) : value;
5927
+ }
5928
+ }
5929
+ return void 0;
5930
+ },
5931
+ set(target, prop, value, receiver) {
5932
+ if (typeof prop === "symbol") {
5933
+ return Reflect.set(target, prop, value, receiver);
5934
+ }
5935
+ if (prop in target) {
5936
+ return Reflect.set(target, prop, value, receiver);
5937
+ }
5938
+ const getRef = target.get;
5939
+ if (typeof getRef === "function") {
5940
+ const current = getRef.call(target);
5941
+ if (current && typeof current === "object") {
5942
+ return Reflect.set(current, prop, value);
5943
+ }
5944
+ }
5945
+ const getItems = target.getItems;
5946
+ if (typeof getItems === "function") {
5947
+ const items = getItems.call(target);
5948
+ return Reflect.set(items, prop, value);
5949
+ }
5950
+ return Reflect.set(target, prop, value, receiver);
5951
+ }
5952
+ });
5953
+ };
5470
5954
  var getRelationWrapper = (meta, relationName, owner) => {
5471
5955
  if (meta.relationWrappers.has(relationName)) {
5472
5956
  return meta.relationWrappers.get(relationName);
@@ -5474,12 +5958,13 @@ var getRelationWrapper = (meta, relationName, owner) => {
5474
5958
  const relation = meta.table.relations[relationName];
5475
5959
  if (!relation) return void 0;
5476
5960
  const wrapper = instantiateWrapper(meta, relationName, relation, owner);
5477
- if (wrapper) {
5478
- meta.relationWrappers.set(relationName, wrapper);
5479
- }
5480
- return wrapper;
5961
+ if (!wrapper) return void 0;
5962
+ const proxied = proxifyRelationWrapper(wrapper);
5963
+ meta.relationWrappers.set(relationName, proxied);
5964
+ return proxied;
5481
5965
  };
5482
5966
  var instantiateWrapper = (meta, relationName, relation, owner) => {
5967
+ const lazyOptions = meta.lazyRelationOptions.get(relationName);
5483
5968
  switch (relation.type) {
5484
5969
  case RelationKinds.HasOne: {
5485
5970
  const hasOne2 = relation;
@@ -5487,7 +5972,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
5487
5972
  const loader = () => relationLoaderCache(
5488
5973
  meta,
5489
5974
  relationName,
5490
- () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2)
5975
+ () => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2, lazyOptions)
5491
5976
  );
5492
5977
  return new DefaultHasOneReference(
5493
5978
  meta.ctx,
@@ -5507,7 +5992,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
5507
5992
  const loader = () => relationLoaderCache(
5508
5993
  meta,
5509
5994
  relationName,
5510
- () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2)
5995
+ () => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2, lazyOptions)
5511
5996
  );
5512
5997
  return new DefaultHasManyCollection(
5513
5998
  meta.ctx,
@@ -5527,7 +6012,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
5527
6012
  const loader = () => relationLoaderCache(
5528
6013
  meta,
5529
6014
  relationName,
5530
- () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2)
6015
+ () => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2, lazyOptions)
5531
6016
  );
5532
6017
  return new DefaultBelongsToReference(
5533
6018
  meta.ctx,
@@ -5547,7 +6032,7 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
5547
6032
  const loader = () => relationLoaderCache(
5548
6033
  meta,
5549
6034
  relationName,
5550
- () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many)
6035
+ () => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many, lazyOptions)
5551
6036
  );
5552
6037
  return new DefaultManyToManyCollection(
5553
6038
  meta.ctx,
@@ -5586,11 +6071,17 @@ var executeWithContexts = async (execCtx, entityCtx, qb) => {
5586
6071
  const compiled = execCtx.dialect.compileSelect(ast);
5587
6072
  const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
5588
6073
  const rows = flattenResults(executed);
6074
+ const lazyRelations = qb.getLazyRelations();
6075
+ const lazyRelationOptions = qb.getLazyRelationOptions();
5589
6076
  if (ast.setOps && ast.setOps.length > 0) {
5590
- return rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
6077
+ const proxies = rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
6078
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
6079
+ return proxies;
5591
6080
  }
5592
6081
  const hydrated = hydrateRows(rows, qb.getHydrationPlan());
5593
- return hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
6082
+ const entities = hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, lazyRelations, lazyRelationOptions));
6083
+ await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
6084
+ return entities;
5594
6085
  };
5595
6086
  async function executeHydrated(session, qb) {
5596
6087
  return executeWithContexts(session.getExecutionContext(), session, qb);
@@ -5602,6 +6093,52 @@ async function executeHydratedWithContexts(execCtx, hydCtx, qb) {
5602
6093
  }
5603
6094
  return executeWithContexts(execCtx, entityCtx, qb);
5604
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
+ }
6140
+ }
6141
+ };
5605
6142
 
5606
6143
  // src/query-builder/query-resolution.ts
5607
6144
  function resolveSelectQuery(query) {
@@ -5618,7 +6155,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5618
6155
  * @param hydration - Optional hydration manager
5619
6156
  * @param dependencies - Optional query builder dependencies
5620
6157
  */
5621
- constructor(table, state, hydration, dependencies, lazyRelations) {
6158
+ constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions) {
5622
6159
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
5623
6160
  this.env = { table, deps };
5624
6161
  const initialState = state ?? deps.createState(table);
@@ -5628,6 +6165,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5628
6165
  hydration: initialHydration
5629
6166
  };
5630
6167
  this.lazyRelations = new Set(lazyRelations ?? []);
6168
+ this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
5631
6169
  this.columnSelector = deps.createColumnSelector(this.env);
5632
6170
  this.relationManager = deps.createRelationManager(this.env);
5633
6171
  }
@@ -5637,8 +6175,15 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5637
6175
  * @param lazyRelations - Updated lazy relations set
5638
6176
  * @returns New SelectQueryBuilder instance
5639
6177
  */
5640
- clone(context = this.context, lazyRelations = new Set(this.lazyRelations)) {
5641
- return new _SelectQueryBuilder(this.env.table, context.state, context.hydration, this.env.deps, lazyRelations);
6178
+ clone(context = this.context, lazyRelations = new Set(this.lazyRelations), lazyRelationOptions = new Map(this.lazyRelationOptions)) {
6179
+ return new _SelectQueryBuilder(
6180
+ this.env.table,
6181
+ context.state,
6182
+ context.hydration,
6183
+ this.env.deps,
6184
+ lazyRelations,
6185
+ lazyRelationOptions
6186
+ );
5642
6187
  }
5643
6188
  /**
5644
6189
  * Applies an alias to the root FROM table.
@@ -5885,36 +6430,40 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5885
6430
  /**
5886
6431
  * Includes a relation lazily in the query results
5887
6432
  * @param relationName - Name of the relation to include lazily
6433
+ * @param options - Optional include options for lazy loading
5888
6434
  * @returns New query builder instance with lazy relation inclusion
5889
6435
  */
5890
- includeLazy(relationName) {
5891
- const nextLazy = new Set(this.lazyRelations);
5892
- nextLazy.add(relationName);
5893
- return this.clone(this.context, nextLazy);
5894
- }
5895
- /**
5896
- * Selects columns for a related table in a single hop.
5897
- */
5898
- selectRelationColumns(relationName, ...cols) {
6436
+ includeLazy(relationName, options) {
6437
+ let nextContext = this.context;
5899
6438
  const relation = this.env.table.relations[relationName];
5900
- if (!relation) {
5901
- throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
5902
- }
5903
- const target = relation.target;
5904
- for (const col2 of cols) {
5905
- if (!target.columns[col2]) {
5906
- throw new Error(
5907
- `Column '${col2}' not found on related table '${target.name}' for relation '${relationName}'`
5908
- );
6439
+ if (relation?.type === RelationKinds.BelongsTo) {
6440
+ const foreignKey = relation.foreignKey;
6441
+ const fkColumn = this.env.table.columns[foreignKey];
6442
+ if (fkColumn) {
6443
+ const hasAlias2 = nextContext.state.ast.columns.some((col2) => {
6444
+ const node = col2;
6445
+ return (node.alias ?? node.name) === foreignKey;
6446
+ });
6447
+ if (!hasAlias2) {
6448
+ nextContext = this.columnSelector.select(nextContext, { [foreignKey]: fkColumn });
6449
+ }
5909
6450
  }
5910
6451
  }
5911
- return this.include(relationName, { columns: cols });
6452
+ const nextLazy = new Set(this.lazyRelations);
6453
+ nextLazy.add(relationName);
6454
+ const nextOptions = new Map(this.lazyRelationOptions);
6455
+ if (options) {
6456
+ nextOptions.set(relationName, options);
6457
+ } else {
6458
+ nextOptions.delete(relationName);
6459
+ }
6460
+ return this.clone(nextContext, nextLazy, nextOptions);
5912
6461
  }
5913
6462
  /**
5914
- * Convenience alias for selecting specific columns from a relation.
6463
+ * Convenience alias for including only specific columns from a relation.
5915
6464
  */
5916
6465
  includePick(relationName, cols) {
5917
- return this.selectRelationColumns(relationName, ...cols);
6466
+ return this.include(relationName, { columns: cols });
5918
6467
  }
5919
6468
  /**
5920
6469
  * Selects columns for the root table and relations from an array of entries
@@ -5927,7 +6476,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5927
6476
  if (entry.type === "root") {
5928
6477
  currBuilder = currBuilder.select(...entry.columns);
5929
6478
  } else {
5930
- currBuilder = currBuilder.selectRelationColumns(entry.relationName, ...entry.columns);
6479
+ currBuilder = currBuilder.include(entry.relationName, { columns: entry.columns });
5931
6480
  }
5932
6481
  }
5933
6482
  return currBuilder;
@@ -5939,6 +6488,13 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
5939
6488
  getLazyRelations() {
5940
6489
  return Array.from(this.lazyRelations);
5941
6490
  }
6491
+ /**
6492
+ * Gets lazy relation include options
6493
+ * @returns Map of relation names to include options
6494
+ */
6495
+ getLazyRelationOptions() {
6496
+ return new Map(this.lazyRelationOptions);
6497
+ }
5942
6498
  /**
5943
6499
  * Gets the table definition for this query builder
5944
6500
  * @returns Table definition
@@ -11798,6 +12354,7 @@ function createPooledExecutorFactory(opts) {
11798
12354
  registerExpressionDispatcher,
11799
12355
  registerOperandDispatcher,
11800
12356
  registerSchemaIntrospector,
12357
+ relationLoaderCache,
11801
12358
  renderColumnDefinition,
11802
12359
  renderTypeWithArgs,
11803
12360
  repeat,