metal-orm 1.1.9 → 1.1.10

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 (73) hide show
  1. package/README.md +769 -764
  2. package/dist/index.cjs +2147 -239
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +559 -39
  5. package/dist/index.d.ts +559 -39
  6. package/dist/index.js +2119 -239
  7. package/dist/index.js.map +1 -1
  8. package/package.json +17 -12
  9. package/src/bulk/bulk-context.ts +83 -0
  10. package/src/bulk/bulk-delete-executor.ts +89 -0
  11. package/src/bulk/bulk-executor.base.ts +73 -0
  12. package/src/bulk/bulk-insert-executor.ts +74 -0
  13. package/src/bulk/bulk-types.ts +70 -0
  14. package/src/bulk/bulk-update-executor.ts +192 -0
  15. package/src/bulk/bulk-upsert-executor.ts +95 -0
  16. package/src/bulk/bulk-utils.ts +91 -0
  17. package/src/bulk/index.ts +18 -0
  18. package/src/codegen/typescript.ts +30 -21
  19. package/src/core/ast/expression-builders.ts +107 -10
  20. package/src/core/ast/expression-nodes.ts +52 -22
  21. package/src/core/ast/expression-visitor.ts +23 -13
  22. package/src/core/dialect/abstract.ts +30 -17
  23. package/src/core/dialect/mysql/index.ts +20 -5
  24. package/src/core/execution/db-executor.ts +96 -64
  25. package/src/core/execution/executors/better-sqlite3-executor.ts +94 -0
  26. package/src/core/execution/executors/mssql-executor.ts +66 -34
  27. package/src/core/execution/executors/mysql-executor.ts +98 -66
  28. package/src/core/execution/executors/postgres-executor.ts +33 -11
  29. package/src/core/execution/executors/sqlite-executor.ts +86 -30
  30. package/src/decorators/bootstrap.ts +482 -398
  31. package/src/decorators/column-decorator.ts +87 -96
  32. package/src/decorators/decorator-metadata.ts +100 -24
  33. package/src/decorators/entity.ts +27 -24
  34. package/src/decorators/relations.ts +231 -149
  35. package/src/decorators/transformers/transformer-decorators.ts +26 -29
  36. package/src/decorators/validators/country-validators-decorators.ts +9 -15
  37. package/src/dto/apply-filter.ts +568 -551
  38. package/src/index.ts +16 -9
  39. package/src/orm/entity-hydration.ts +116 -72
  40. package/src/orm/entity-metadata.ts +347 -301
  41. package/src/orm/entity-relations.ts +264 -207
  42. package/src/orm/entity.ts +199 -199
  43. package/src/orm/execute.ts +13 -13
  44. package/src/orm/lazy-batch/morph-many.ts +70 -0
  45. package/src/orm/lazy-batch/morph-one.ts +69 -0
  46. package/src/orm/lazy-batch/morph-to.ts +59 -0
  47. package/src/orm/lazy-batch.ts +4 -1
  48. package/src/orm/orm-session.ts +170 -104
  49. package/src/orm/pooled-executor-factory.ts +99 -58
  50. package/src/orm/query-logger.ts +49 -40
  51. package/src/orm/relation-change-processor.ts +198 -96
  52. package/src/orm/relations/belongs-to.ts +143 -143
  53. package/src/orm/relations/has-many.ts +204 -204
  54. package/src/orm/relations/has-one.ts +174 -174
  55. package/src/orm/relations/many-to-many.ts +288 -288
  56. package/src/orm/relations/morph-many.ts +156 -0
  57. package/src/orm/relations/morph-one.ts +151 -0
  58. package/src/orm/relations/morph-to.ts +162 -0
  59. package/src/orm/save-graph.ts +116 -1
  60. package/src/query-builder/expression-table-mapper.ts +5 -0
  61. package/src/query-builder/hydration-manager.ts +345 -345
  62. package/src/query-builder/hydration-planner.ts +178 -148
  63. package/src/query-builder/relation-conditions.ts +171 -151
  64. package/src/query-builder/relation-cte-builder.ts +5 -1
  65. package/src/query-builder/relation-filter-utils.ts +9 -6
  66. package/src/query-builder/relation-include-strategies.ts +44 -2
  67. package/src/query-builder/relation-join-strategies.ts +8 -1
  68. package/src/query-builder/relation-service.ts +250 -241
  69. package/src/query-builder/select/select-operations.ts +110 -105
  70. package/src/query-builder/update-include.ts +4 -0
  71. package/src/schema/relation.ts +296 -188
  72. package/src/schema/types.ts +138 -123
  73. package/src/tree/tree-decorator.ts +127 -137
package/dist/index.js CHANGED
@@ -328,7 +328,13 @@ var RelationKinds = {
328
328
  /** Many-to-one relationship */
329
329
  BelongsTo: "BELONGS_TO",
330
330
  /** Many-to-many relationship with pivot metadata */
331
- BelongsToMany: "BELONGS_TO_MANY"
331
+ BelongsToMany: "BELONGS_TO_MANY",
332
+ /** Polymorphic inverse (child side) */
333
+ MorphTo: "MORPH_TO",
334
+ /** Polymorphic one-to-one (parent side) */
335
+ MorphOne: "MORPH_ONE",
336
+ /** Polymorphic one-to-many (parent side) */
337
+ MorphMany: "MORPH_MANY"
332
338
  };
333
339
  var hasMany = (target, foreignKey, localKey, cascade) => ({
334
340
  type: RelationKinds.HasMany,
@@ -363,6 +369,32 @@ var belongsToMany = (target, pivotTable, options) => ({
363
369
  defaultPivotColumns: options.defaultPivotColumns,
364
370
  cascade: options.cascade
365
371
  });
372
+ var isSingleTargetRelation = (rel) => rel.type !== RelationKinds.MorphTo;
373
+ var isMorphRelation = (rel) => rel.type === RelationKinds.MorphTo || rel.type === RelationKinds.MorphOne || rel.type === RelationKinds.MorphMany;
374
+ var morphTo = (opts) => ({
375
+ type: RelationKinds.MorphTo,
376
+ ...opts
377
+ });
378
+ var morphOne = (target, opts) => ({
379
+ type: RelationKinds.MorphOne,
380
+ target,
381
+ morphName: opts.as,
382
+ typeField: opts.typeField ?? `${opts.as}Type`,
383
+ idField: opts.idField ?? `${opts.as}Id`,
384
+ typeValue: opts.typeValue,
385
+ localKey: opts.localKey,
386
+ cascade: opts.cascade
387
+ });
388
+ var morphMany = (target, opts) => ({
389
+ type: RelationKinds.MorphMany,
390
+ target,
391
+ morphName: opts.as,
392
+ typeField: opts.typeField ?? `${opts.as}Type`,
393
+ idField: opts.idField ?? `${opts.as}Id`,
394
+ typeValue: opts.typeValue,
395
+ localKey: opts.localKey,
396
+ cascade: opts.cascade
397
+ });
366
398
 
367
399
  // src/core/ast/expression-nodes.ts
368
400
  var operandTypes = /* @__PURE__ */ new Set([
@@ -490,6 +522,10 @@ var or = (...operands) => ({
490
522
  operator: "OR",
491
523
  operands
492
524
  });
525
+ var not = (operand) => ({
526
+ type: "NotExpression",
527
+ operand
528
+ });
493
529
  var isNull = (left2) => ({
494
530
  type: "NullExpression",
495
531
  left: toOperandNode(left2),
@@ -573,6 +609,18 @@ var collate = (expression, collation) => ({
573
609
  expression: toOperand(expression),
574
610
  collation
575
611
  });
612
+ var isDistinctFrom = (left2, right2) => ({
613
+ type: "IsDistinctExpression",
614
+ left: toOperandNode(left2),
615
+ operator: "IS DISTINCT FROM",
616
+ right: toOperand(right2)
617
+ });
618
+ var isNotDistinctFrom = (left2, right2) => ({
619
+ type: "IsDistinctExpression",
620
+ left: toOperandNode(left2),
621
+ operator: "IS NOT DISTINCT FROM",
622
+ right: toOperand(right2)
623
+ });
576
624
 
577
625
  // src/core/ast/window-functions.ts
578
626
  var buildWindowFunction = (name, args = [], partitionBy, orderBy) => {
@@ -779,6 +827,9 @@ var visitExpression = (node, visitor) => {
779
827
  case "LogicalExpression":
780
828
  if (visitor.visitLogicalExpression) return visitor.visitLogicalExpression(node);
781
829
  break;
830
+ case "NotExpression":
831
+ if (visitor.visitNotExpression) return visitor.visitNotExpression(node);
832
+ break;
782
833
  case "NullExpression":
783
834
  if (visitor.visitNullExpression) return visitor.visitNullExpression(node);
784
835
  break;
@@ -797,6 +848,9 @@ var visitExpression = (node, visitor) => {
797
848
  case "BitwiseExpression":
798
849
  if (visitor.visitBitwiseExpression) return visitor.visitBitwiseExpression(node);
799
850
  break;
851
+ case "IsDistinctExpression":
852
+ if (visitor.visitIsDistinctExpression) return visitor.visitIsDistinctExpression(node);
853
+ break;
800
854
  default:
801
855
  break;
802
856
  }
@@ -1520,6 +1574,10 @@ var Dialect = class _Dialect {
1520
1574
  });
1521
1575
  return parts.join(` ${logical.operator} `);
1522
1576
  });
1577
+ this.registerExpressionCompiler("NotExpression", (notExpr, ctx) => {
1578
+ const operand = this.compileExpression(notExpr.operand, ctx);
1579
+ return `NOT (${operand})`;
1580
+ });
1523
1581
  this.registerExpressionCompiler("NullExpression", (nullExpr, ctx) => {
1524
1582
  const left2 = this.compileOperand(nullExpr.left, ctx);
1525
1583
  return `${left2} ${nullExpr.operator}`;
@@ -1553,6 +1611,11 @@ var Dialect = class _Dialect {
1553
1611
  const right2 = this.compileOperand(bitwise.right, ctx);
1554
1612
  return `${left2} ${bitwise.operator} ${right2}`;
1555
1613
  });
1614
+ this.registerExpressionCompiler("IsDistinctExpression", (node, ctx) => {
1615
+ const left2 = this.compileOperand(node.left, ctx);
1616
+ const right2 = this.compileOperand(node.right, ctx);
1617
+ return `${left2} ${node.operator} ${right2}`;
1618
+ });
1556
1619
  }
1557
1620
  registerDefaultOperandCompilers() {
1558
1621
  this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
@@ -2474,6 +2537,18 @@ var MySqlDialect = class extends SqlDialectBase {
2474
2537
  */
2475
2538
  constructor() {
2476
2539
  super(new MysqlFunctionStrategy());
2540
+ this.registerExpressionCompiler(
2541
+ "IsDistinctExpression",
2542
+ (node, ctx) => {
2543
+ const left2 = this.compileOperand(node.left, ctx);
2544
+ const right2 = this.compileOperand(node.right, ctx);
2545
+ const spaceship = `${left2} <=> ${right2}`;
2546
+ if (node.operator === "IS NOT DISTINCT FROM") {
2547
+ return spaceship;
2548
+ }
2549
+ return `NOT (${spaceship})`;
2550
+ }
2551
+ );
2477
2552
  }
2478
2553
  /**
2479
2554
  * Quotes an identifier using MySQL backtick syntax
@@ -3573,7 +3648,7 @@ var HydrationManager = class _HydrationManager {
3573
3648
  */
3574
3649
  hasMultiplyingRelations(plan) {
3575
3650
  return plan.relations.some(
3576
- (rel) => rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany
3651
+ (rel) => rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany || rel.type === RelationKinds.MorphMany
3577
3652
  );
3578
3653
  }
3579
3654
  /**
@@ -3907,6 +3982,36 @@ var HydrationPlanner = class _HydrationPlanner {
3907
3982
  }
3908
3983
  };
3909
3984
  }
3985
+ case RelationKinds.MorphOne: {
3986
+ const morphRel = rel;
3987
+ const localKey = morphRel.localKey || findPrimaryKey(this.table);
3988
+ return {
3989
+ name: relationName,
3990
+ aliasPrefix,
3991
+ type: rel.type,
3992
+ targetTable: morphRel.target.name,
3993
+ targetPrimaryKey: findPrimaryKey(morphRel.target),
3994
+ foreignKey: morphRel.idField,
3995
+ localKey,
3996
+ columns
3997
+ };
3998
+ }
3999
+ case RelationKinds.MorphMany: {
4000
+ const morphRel = rel;
4001
+ const localKey = morphRel.localKey || findPrimaryKey(this.table);
4002
+ return {
4003
+ name: relationName,
4004
+ aliasPrefix,
4005
+ type: rel.type,
4006
+ targetTable: morphRel.target.name,
4007
+ targetPrimaryKey: findPrimaryKey(morphRel.target),
4008
+ foreignKey: morphRel.idField,
4009
+ localKey,
4010
+ columns
4011
+ };
4012
+ }
4013
+ case RelationKinds.MorphTo:
4014
+ throw new Error("MorphTo relations do not support hydration planning via JOIN.");
3910
4015
  }
3911
4016
  }
3912
4017
  };
@@ -4211,23 +4316,44 @@ var assertNever = (value) => {
4211
4316
  };
4212
4317
  var baseRelationCondition = (root, relation, rootAlias, targetTableName) => {
4213
4318
  const rootTable = rootAlias || root.name;
4319
+ if (relation.type === RelationKinds.MorphTo) {
4320
+ throw new Error("MorphTo relations do not support the standard join condition builder");
4321
+ }
4214
4322
  const targetTable = targetTableName ?? relation.target.name;
4215
- const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
4216
- const localKey = relation.localKey || defaultLocalKey;
4217
4323
  switch (relation.type) {
4218
4324
  case RelationKinds.HasMany:
4219
- case RelationKinds.HasOne:
4325
+ case RelationKinds.HasOne: {
4326
+ const defaultLocalKey = findPrimaryKey(root);
4327
+ const localKey = relation.localKey || defaultLocalKey;
4220
4328
  return eq(
4221
4329
  { type: "Column", table: targetTable, name: relation.foreignKey },
4222
4330
  { type: "Column", table: rootTable, name: localKey }
4223
4331
  );
4224
- case RelationKinds.BelongsTo:
4332
+ }
4333
+ case RelationKinds.BelongsTo: {
4334
+ const defaultLocalKey = findPrimaryKey(relation.target);
4335
+ const localKey = relation.localKey || defaultLocalKey;
4225
4336
  return eq(
4226
4337
  { type: "Column", table: targetTable, name: localKey },
4227
4338
  { type: "Column", table: rootTable, name: relation.foreignKey }
4228
4339
  );
4340
+ }
4229
4341
  case RelationKinds.BelongsToMany:
4230
4342
  throw new Error("BelongsToMany relations do not support the standard join condition builder");
4343
+ case RelationKinds.MorphOne:
4344
+ case RelationKinds.MorphMany: {
4345
+ const morphRel = relation;
4346
+ const morphLocalKey = morphRel.localKey || findPrimaryKey(root);
4347
+ const baseCondition = eq(
4348
+ { type: "Column", table: targetTable, name: morphRel.idField },
4349
+ { type: "Column", table: rootTable, name: morphLocalKey }
4350
+ );
4351
+ const discriminatorCondition = eq(
4352
+ { type: "Column", table: targetTable, name: morphRel.typeField },
4353
+ { type: "Literal", value: morphRel.typeValue }
4354
+ );
4355
+ return and(baseCondition, discriminatorCondition);
4356
+ }
4231
4357
  default:
4232
4358
  return assertNever(relation);
4233
4359
  }
@@ -4323,6 +4449,9 @@ var collectFromExpression = (expr, collector) => {
4323
4449
  case "LogicalExpression":
4324
4450
  expr.operands.forEach((operand) => collectFromExpression(operand, collector));
4325
4451
  break;
4452
+ case "NotExpression":
4453
+ collectFromExpression(expr.operand, collector);
4454
+ break;
4326
4455
  case "NullExpression":
4327
4456
  collectFromOperand(expr.left, collector);
4328
4457
  break;
@@ -4429,6 +4558,11 @@ var mapExpression = (expr, fromTable, toTable) => {
4429
4558
  if (nextOperands.every((op, i) => op === expr.operands[i])) return expr;
4430
4559
  return { ...expr, operands: nextOperands };
4431
4560
  }
4561
+ case "NotExpression": {
4562
+ const operand = mapExpression(expr.operand, fromTable, toTable);
4563
+ if (operand === expr.operand) return expr;
4564
+ return { ...expr, operand };
4565
+ }
4432
4566
  case "NullExpression": {
4433
4567
  const left2 = mapOperand(expr.left, fromTable, toTable);
4434
4568
  if (left2 === expr.left) return expr;
@@ -4729,6 +4863,9 @@ var addRelationJoin = (params) => {
4729
4863
  );
4730
4864
  return joins.reduce((curr, join) => curr.withJoin(join), state);
4731
4865
  }
4866
+ if (!isSingleTargetRelation(relation)) {
4867
+ throw new Error("Polymorphic MorphTo relations do not support join-based strategies");
4868
+ }
4732
4869
  let targetSource = tableSource ?? {
4733
4870
  type: "Table",
4734
4871
  name: relation.target.name,
@@ -4744,6 +4881,9 @@ var addRelationJoin = (params) => {
4744
4881
  var updateRelationJoin = (params) => {
4745
4882
  const { joins, joinIndex, relation, currentTable, currentAlias, options } = params;
4746
4883
  const join = joins[joinIndex];
4884
+ if (!isSingleTargetRelation(relation)) {
4885
+ throw new Error("Polymorphic MorphTo relations do not support join updates");
4886
+ }
4747
4887
  const targetName = resolveTargetTableName(join.table, relation.target.name);
4748
4888
  const extra = remapExpressionTable(options.filter, relation.target.name, targetName);
4749
4889
  if (relation.type === RelationKinds.BelongsToMany) {
@@ -4803,6 +4943,9 @@ var RelationCteBuilder = class {
4803
4943
  if (!predicate) {
4804
4944
  throw new Error("Unable to build filter CTE without predicates.");
4805
4945
  }
4946
+ if (!isSingleTargetRelation(relation)) {
4947
+ throw new Error("Polymorphic MorphTo relations do not support filter CTEs");
4948
+ }
4806
4949
  const columns = Object.keys(relation.target.columns).map((name) => ({
4807
4950
  type: "Column",
4808
4951
  table: relation.target.name,
@@ -4850,6 +4993,9 @@ var buildTypedSelection = (columns, prefix, keys, missingMsg, tableOverride) =>
4850
4993
  }, {});
4851
4994
  };
4852
4995
  var resolveTargetColumns = (relation, options) => {
4996
+ if (!isSingleTargetRelation(relation)) {
4997
+ return [];
4998
+ }
4853
4999
  const requestedColumns = options?.columns?.length ? [...options.columns] : Object.keys(relation.target.columns);
4854
5000
  const targetPrimaryKey = findPrimaryKey(relation.target);
4855
5001
  if (!requestedColumns.includes(targetPrimaryKey)) {
@@ -4941,11 +5087,41 @@ var belongsToManyStrategy = (context) => {
4941
5087
  );
4942
5088
  return { state, hydration };
4943
5089
  };
5090
+ var morphIncludeStrategy = (context) => {
5091
+ let { state, hydration } = context;
5092
+ const relation = context.relation;
5093
+ const targetColumns = resolveTargetColumns(relation, context.options);
5094
+ const tableOverride = getJoinCorrelationName(state, context.relationName, relation.target.name);
5095
+ const targetSelection = buildTypedSelection(
5096
+ relation.target.columns,
5097
+ context.aliasPrefix,
5098
+ targetColumns,
5099
+ (key) => `Column '${key}' not found on relation '${context.relationName}'`,
5100
+ tableOverride
5101
+ );
5102
+ const relationSelectionResult = context.selectColumns(state, hydration, targetSelection);
5103
+ state = relationSelectionResult.state;
5104
+ hydration = relationSelectionResult.hydration;
5105
+ hydration = hydration.onRelationIncluded(
5106
+ state,
5107
+ relation,
5108
+ context.relationName,
5109
+ context.aliasPrefix,
5110
+ targetColumns
5111
+ );
5112
+ return { state, hydration };
5113
+ };
5114
+ var morphToIncludeStrategy = () => {
5115
+ throw new Error("MorphTo relations do not support JOIN-based include. Use lazy loading instead.");
5116
+ };
4944
5117
  var relationIncludeStrategies = {
4945
5118
  [RelationKinds.HasMany]: standardIncludeStrategy,
4946
5119
  [RelationKinds.HasOne]: standardIncludeStrategy,
4947
5120
  [RelationKinds.BelongsTo]: standardIncludeStrategy,
4948
- [RelationKinds.BelongsToMany]: belongsToManyStrategy
5121
+ [RelationKinds.BelongsToMany]: belongsToManyStrategy,
5122
+ [RelationKinds.MorphOne]: morphIncludeStrategy,
5123
+ [RelationKinds.MorphMany]: morphIncludeStrategy,
5124
+ [RelationKinds.MorphTo]: morphToIncludeStrategy
4949
5125
  };
4950
5126
 
4951
5127
  // src/query-builder/relation-service.ts
@@ -5014,6 +5190,12 @@ var RelationService = class {
5014
5190
  let state = this.state;
5015
5191
  let hydration = this.hydration;
5016
5192
  const relation = this.getRelation(relationName);
5193
+ if (relation.type === RelationKinds.MorphTo) {
5194
+ throw new Error(`MorphTo relation '${relationName}' does not support include() via JOIN. Use lazy loading ($load) instead.`);
5195
+ }
5196
+ if (!isSingleTargetRelation(relation)) {
5197
+ return { state, hydration };
5198
+ }
5017
5199
  const aliasPrefix = options?.aliasPrefix ?? relationName;
5018
5200
  const alreadyJoined = hasJoinForRelationKey(state.ast.joins, relationName);
5019
5201
  const { selfFilters, crossFilters } = splitFilterExpressions(
@@ -5072,6 +5254,9 @@ var RelationService = class {
5072
5254
  */
5073
5255
  applyRelationCorrelation(relationName, ast, additionalCorrelation) {
5074
5256
  const relation = this.getRelation(relationName);
5257
+ if (relation.type === RelationKinds.MorphTo) {
5258
+ throw new Error(`MorphTo relation '${relationName}' does not support correlation-based operations.`);
5259
+ }
5075
5260
  const rootAlias = this.state.ast.from.type === "Table" ? this.state.ast.from.alias : void 0;
5076
5261
  let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
5077
5262
  if (additionalCorrelation) {
@@ -5531,6 +5716,45 @@ var populateHydrationCache = (entity, row, meta) => {
5531
5716
  }
5532
5717
  }
5533
5718
  }
5719
+ for (const relationName of Object.keys(meta.table.relations)) {
5720
+ const relation = meta.table.relations[relationName];
5721
+ const data = row[relationName];
5722
+ if (relation.type === RelationKinds.MorphOne) {
5723
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
5724
+ const rootValue = entity[localKey];
5725
+ if (rootValue === void 0 || rootValue === null) continue;
5726
+ if (!data || typeof data !== "object") continue;
5727
+ const cache = /* @__PURE__ */ new Map();
5728
+ cache.set(toKey2(rootValue), data);
5729
+ meta.relationHydration.set(relationName, cache);
5730
+ meta.relationCache.set(relationName, Promise.resolve(cache));
5731
+ continue;
5732
+ }
5733
+ if (relation.type === RelationKinds.MorphMany) {
5734
+ if (!Array.isArray(data)) continue;
5735
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
5736
+ const rootValue = entity[localKey];
5737
+ if (rootValue === void 0 || rootValue === null) continue;
5738
+ const cache = /* @__PURE__ */ new Map();
5739
+ cache.set(toKey2(rootValue), data);
5740
+ meta.relationHydration.set(relationName, cache);
5741
+ meta.relationCache.set(relationName, Promise.resolve(cache));
5742
+ continue;
5743
+ }
5744
+ if (relation.type === RelationKinds.MorphTo) {
5745
+ if (!data || typeof data !== "object") continue;
5746
+ const morphTo2 = relation;
5747
+ const typeValue = entity[morphTo2.typeField];
5748
+ const idValue = entity[morphTo2.idField];
5749
+ if (!typeValue || idValue === void 0 || idValue === null) continue;
5750
+ const compositeKey = `${toKey2(typeValue)}:${toKey2(idValue)}`;
5751
+ const cache = /* @__PURE__ */ new Map();
5752
+ cache.set(compositeKey, data);
5753
+ meta.relationHydration.set(relationName, cache);
5754
+ meta.relationCache.set(relationName, Promise.resolve(cache));
5755
+ continue;
5756
+ }
5757
+ }
5534
5758
  };
5535
5759
 
5536
5760
  // src/orm/relations/has-many.ts
@@ -6203,113 +6427,519 @@ var DefaultManyToManyCollection = class {
6203
6427
  }
6204
6428
  };
6205
6429
 
6206
- // src/orm/lazy-batch/shared.ts
6207
- var hasColumns = (columns) => Boolean(columns && columns.length > 0);
6208
- var buildColumnSelection = (table, columns, missingMsg) => {
6209
- return columns.reduce((acc, column) => {
6210
- const def = table.columns[column];
6211
- if (!def) {
6212
- throw new Error(missingMsg(column));
6213
- }
6214
- acc[column] = def;
6215
- return acc;
6216
- }, {});
6217
- };
6218
- var filterRow = (row, columns) => {
6219
- const filtered = {};
6220
- for (const column of columns) {
6221
- if (column in row) {
6222
- filtered[column] = row[column];
6223
- }
6224
- }
6225
- return filtered;
6226
- };
6227
- var filterRows = (rows, columns) => rows.map((row) => filterRow(row, columns));
6228
- var rowsFromResults = (results) => {
6229
- const rows = [];
6230
- for (const result of results) {
6231
- const { columns, values } = result;
6232
- for (const valueRow of values) {
6233
- const row = {};
6234
- columns.forEach((column, idx) => {
6235
- row[column] = valueRow[idx];
6236
- });
6237
- rows.push(row);
6238
- }
6239
- }
6240
- return rows;
6241
- };
6242
- var executeQuery = async (ctx, qb) => {
6243
- const compiled = ctx.dialect.compileSelect(qb.getAST());
6244
- const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
6245
- return rowsFromResults(results);
6246
- };
6430
+ // src/orm/relations/morph-one.ts
6247
6431
  var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
6248
- var collectKeysFromRoots = (roots, key) => {
6249
- const collected = /* @__PURE__ */ new Set();
6250
- for (const tracked of roots) {
6251
- const value = tracked.entity[key];
6252
- if (value !== null && value !== void 0) {
6253
- collected.add(value);
6254
- }
6432
+ var hideInternal5 = (obj, keys) => {
6433
+ for (const key of keys) {
6434
+ Object.defineProperty(obj, key, {
6435
+ value: obj[key],
6436
+ writable: false,
6437
+ configurable: false,
6438
+ enumerable: false
6439
+ });
6255
6440
  }
6256
- return collected;
6257
6441
  };
6258
- var buildInListValues = (keys) => Array.from(keys);
6259
- var fetchRowsForKeys = async (ctx, table, column, keys, selection, filter) => {
6260
- let qb = new SelectQueryBuilder(table).select(selection);
6261
- qb = qb.where(inList(column, buildInListValues(keys)));
6262
- if (filter) {
6263
- qb = qb.where(filter);
6442
+ var hideWritable5 = (obj, keys) => {
6443
+ for (const key of keys) {
6444
+ const value = obj[key];
6445
+ Object.defineProperty(obj, key, {
6446
+ value,
6447
+ writable: true,
6448
+ configurable: true,
6449
+ enumerable: false
6450
+ });
6264
6451
  }
6265
- return executeQuery(ctx, qb);
6266
6452
  };
6267
- var groupRowsByMany = (rows, keyColumn) => {
6268
- const grouped = /* @__PURE__ */ new Map();
6269
- for (const row of rows) {
6270
- const value = row[keyColumn];
6271
- if (value === null || value === void 0) continue;
6272
- const key = toKey7(value);
6273
- const bucket = grouped.get(key) ?? [];
6274
- bucket.push(row);
6275
- grouped.set(key, bucket);
6453
+ var DefaultMorphOneReference = class {
6454
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
6455
+ this.ctx = ctx;
6456
+ this.meta = meta;
6457
+ this.root = root;
6458
+ this.relationName = relationName;
6459
+ this.relation = relation;
6460
+ this.rootTable = rootTable;
6461
+ this.loader = loader;
6462
+ this.createEntity = createEntity;
6463
+ this.localKey = localKey;
6464
+ hideInternal5(this, [
6465
+ "ctx",
6466
+ "meta",
6467
+ "root",
6468
+ "relationName",
6469
+ "relation",
6470
+ "rootTable",
6471
+ "loader",
6472
+ "createEntity",
6473
+ "localKey"
6474
+ ]);
6475
+ hideWritable5(this, ["loaded", "current"]);
6476
+ this.populateFromHydrationCache();
6276
6477
  }
6277
- return grouped;
6278
- };
6279
- var groupRowsByUnique = (rows, keyColumn) => {
6280
- const lookup = /* @__PURE__ */ new Map();
6281
- for (const row of rows) {
6282
- const value = row[keyColumn];
6283
- if (value === null || value === void 0) continue;
6284
- const key = toKey7(value);
6285
- if (!lookup.has(key)) {
6286
- lookup.set(key, row);
6478
+ loaded = false;
6479
+ current = null;
6480
+ async load() {
6481
+ if (this.loaded) return this.current;
6482
+ const map = await this.loader();
6483
+ const keyValue = this.root[this.localKey];
6484
+ if (keyValue === void 0 || keyValue === null) {
6485
+ this.loaded = true;
6486
+ return this.current;
6287
6487
  }
6488
+ const row = map.get(toKey7(keyValue));
6489
+ this.current = row ? this.createEntity(row) : null;
6490
+ this.loaded = true;
6491
+ return this.current;
6288
6492
  }
6289
- return lookup;
6290
- };
6291
-
6292
- // src/orm/lazy-batch/has-many.ts
6293
- var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options) => {
6294
- const localKey = relation.localKey || findPrimaryKey(rootTable);
6295
- const roots = ctx.getEntitiesForTable(rootTable);
6296
- const keys = collectKeysFromRoots(roots, localKey);
6297
- if (!keys.size) {
6298
- return /* @__PURE__ */ new Map();
6299
- }
6300
- const fkColumn = relation.target.columns[relation.foreignKey];
6301
- if (!fkColumn) return /* @__PURE__ */ new Map();
6302
- const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
6303
- const targetPrimaryKey = findPrimaryKey(relation.target);
6304
- const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
6305
- if (!selectedColumns.includes(targetPrimaryKey)) {
6306
- selectedColumns.push(targetPrimaryKey);
6493
+ get() {
6494
+ return this.current;
6307
6495
  }
6308
- const queryColumns = new Set(selectedColumns);
6309
- queryColumns.add(relation.foreignKey);
6310
- const selection = buildColumnSelection(
6311
- relation.target,
6312
- Array.from(queryColumns),
6496
+ set(data) {
6497
+ if (data === null) {
6498
+ return this.detachCurrent();
6499
+ }
6500
+ const entity = hasEntityMeta(data) ? data : this.createEntity(data);
6501
+ if (this.current && this.current !== entity) {
6502
+ this.ctx.registerRelationChange(
6503
+ this.root,
6504
+ this.relationKey,
6505
+ this.rootTable,
6506
+ this.relationName,
6507
+ this.relation,
6508
+ { kind: "remove", entity: this.current }
6509
+ );
6510
+ }
6511
+ this.assignMorphKeys(entity);
6512
+ this.current = entity;
6513
+ this.loaded = true;
6514
+ this.ctx.registerRelationChange(
6515
+ this.root,
6516
+ this.relationKey,
6517
+ this.rootTable,
6518
+ this.relationName,
6519
+ this.relation,
6520
+ { kind: "attach", entity }
6521
+ );
6522
+ return entity;
6523
+ }
6524
+ toJSON() {
6525
+ if (!this.current) return null;
6526
+ const entityWithToJSON = this.current;
6527
+ return typeof entityWithToJSON.toJSON === "function" ? entityWithToJSON.toJSON() : this.current;
6528
+ }
6529
+ detachCurrent() {
6530
+ const previous = this.current;
6531
+ if (!previous) return null;
6532
+ this.current = null;
6533
+ this.loaded = true;
6534
+ this.ctx.registerRelationChange(
6535
+ this.root,
6536
+ this.relationKey,
6537
+ this.rootTable,
6538
+ this.relationName,
6539
+ this.relation,
6540
+ { kind: "remove", entity: previous }
6541
+ );
6542
+ return null;
6543
+ }
6544
+ assignMorphKeys(entity) {
6545
+ const keyValue = this.root[this.localKey];
6546
+ entity[this.relation.idField] = keyValue;
6547
+ entity[this.relation.typeField] = this.relation.typeValue;
6548
+ }
6549
+ get relationKey() {
6550
+ return `${this.rootTable.name}.${this.relationName}`;
6551
+ }
6552
+ populateFromHydrationCache() {
6553
+ const keyValue = this.root[this.localKey];
6554
+ if (keyValue === void 0 || keyValue === null) return;
6555
+ const row = getHydrationRecord(this.meta, this.relationName, keyValue);
6556
+ if (!row) return;
6557
+ this.current = this.createEntity(row);
6558
+ this.loaded = true;
6559
+ }
6560
+ };
6561
+
6562
+ // src/orm/relations/morph-many.ts
6563
+ var toKey8 = (value) => value === null || value === void 0 ? "" : String(value);
6564
+ var hideInternal6 = (obj, keys) => {
6565
+ for (const key of keys) {
6566
+ Object.defineProperty(obj, key, {
6567
+ value: obj[key],
6568
+ writable: false,
6569
+ configurable: false,
6570
+ enumerable: false
6571
+ });
6572
+ }
6573
+ };
6574
+ var hideWritable6 = (obj, keys) => {
6575
+ for (const key of keys) {
6576
+ const value = obj[key];
6577
+ Object.defineProperty(obj, key, {
6578
+ value,
6579
+ writable: true,
6580
+ configurable: true,
6581
+ enumerable: false
6582
+ });
6583
+ }
6584
+ };
6585
+ var DefaultMorphManyCollection = class {
6586
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
6587
+ this.ctx = ctx;
6588
+ this.meta = meta;
6589
+ this.root = root;
6590
+ this.relationName = relationName;
6591
+ this.relation = relation;
6592
+ this.rootTable = rootTable;
6593
+ this.loader = loader;
6594
+ this.createEntity = createEntity;
6595
+ this.localKey = localKey;
6596
+ hideInternal6(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
6597
+ hideWritable6(this, ["loaded", "items", "added", "removed"]);
6598
+ this.hydrateFromCache();
6599
+ }
6600
+ loaded = false;
6601
+ items = [];
6602
+ added = /* @__PURE__ */ new Set();
6603
+ removed = /* @__PURE__ */ new Set();
6604
+ async load() {
6605
+ if (this.loaded) return this.items;
6606
+ const map = await this.loader();
6607
+ const key = toKey8(this.root[this.localKey]);
6608
+ const rows = map.get(key) ?? [];
6609
+ this.items = rows.map((row) => this.createEntity(row));
6610
+ this.loaded = true;
6611
+ return this.items;
6612
+ }
6613
+ getItems() {
6614
+ return this.items;
6615
+ }
6616
+ get length() {
6617
+ return this.items.length;
6618
+ }
6619
+ [Symbol.iterator]() {
6620
+ return this.items[Symbol.iterator]();
6621
+ }
6622
+ add(data) {
6623
+ const keyValue = this.root[this.localKey];
6624
+ const childRow = {
6625
+ ...data,
6626
+ [this.relation.idField]: keyValue,
6627
+ [this.relation.typeField]: this.relation.typeValue
6628
+ };
6629
+ const entity = this.createEntity(childRow);
6630
+ this.added.add(entity);
6631
+ this.items.push(entity);
6632
+ this.ctx.registerRelationChange(
6633
+ this.root,
6634
+ this.relationKey,
6635
+ this.rootTable,
6636
+ this.relationName,
6637
+ this.relation,
6638
+ { kind: "add", entity }
6639
+ );
6640
+ return entity;
6641
+ }
6642
+ attach(entity) {
6643
+ const keyValue = this.root[this.localKey];
6644
+ entity[this.relation.idField] = keyValue;
6645
+ entity[this.relation.typeField] = this.relation.typeValue;
6646
+ this.ctx.markDirty(entity);
6647
+ this.items.push(entity);
6648
+ this.ctx.registerRelationChange(
6649
+ this.root,
6650
+ this.relationKey,
6651
+ this.rootTable,
6652
+ this.relationName,
6653
+ this.relation,
6654
+ { kind: "attach", entity }
6655
+ );
6656
+ }
6657
+ remove(entity) {
6658
+ this.items = this.items.filter((item) => item !== entity);
6659
+ this.removed.add(entity);
6660
+ this.ctx.registerRelationChange(
6661
+ this.root,
6662
+ this.relationKey,
6663
+ this.rootTable,
6664
+ this.relationName,
6665
+ this.relation,
6666
+ { kind: "remove", entity }
6667
+ );
6668
+ }
6669
+ clear() {
6670
+ for (const entity of [...this.items]) {
6671
+ this.remove(entity);
6672
+ }
6673
+ }
6674
+ get relationKey() {
6675
+ return `${this.rootTable.name}.${this.relationName}`;
6676
+ }
6677
+ hydrateFromCache() {
6678
+ const keyValue = this.root[this.localKey];
6679
+ if (keyValue === void 0 || keyValue === null) return;
6680
+ const rows = getHydrationRows(this.meta, this.relationName, keyValue);
6681
+ if (!rows?.length) return;
6682
+ this.items = rows.map((row) => this.createEntity(row));
6683
+ this.loaded = true;
6684
+ }
6685
+ toJSON() {
6686
+ return this.items.map((item) => {
6687
+ const entityWithToJSON = item;
6688
+ return typeof entityWithToJSON.toJSON === "function" ? entityWithToJSON.toJSON() : item;
6689
+ });
6690
+ }
6691
+ };
6692
+
6693
+ // src/orm/relations/morph-to.ts
6694
+ var toKey9 = (value) => value === null || value === void 0 ? "" : String(value);
6695
+ var hideInternal7 = (obj, keys) => {
6696
+ for (const key of keys) {
6697
+ Object.defineProperty(obj, key, {
6698
+ value: obj[key],
6699
+ writable: false,
6700
+ configurable: false,
6701
+ enumerable: false
6702
+ });
6703
+ }
6704
+ };
6705
+ var hideWritable7 = (obj, keys) => {
6706
+ for (const key of keys) {
6707
+ const value = obj[key];
6708
+ Object.defineProperty(obj, key, {
6709
+ value,
6710
+ writable: true,
6711
+ configurable: true,
6712
+ enumerable: false
6713
+ });
6714
+ }
6715
+ };
6716
+ var DefaultMorphToReference = class {
6717
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, resolveTargetTable) {
6718
+ this.ctx = ctx;
6719
+ this.meta = meta;
6720
+ this.root = root;
6721
+ this.relationName = relationName;
6722
+ this.relation = relation;
6723
+ this.rootTable = rootTable;
6724
+ this.loader = loader;
6725
+ this.createEntity = createEntity;
6726
+ this.resolveTargetTable = resolveTargetTable;
6727
+ hideInternal7(this, [
6728
+ "ctx",
6729
+ "meta",
6730
+ "root",
6731
+ "relationName",
6732
+ "relation",
6733
+ "rootTable",
6734
+ "loader",
6735
+ "createEntity",
6736
+ "resolveTargetTable"
6737
+ ]);
6738
+ hideWritable7(this, ["loaded", "current"]);
6739
+ this.populateFromHydrationCache();
6740
+ }
6741
+ loaded = false;
6742
+ current = null;
6743
+ async load() {
6744
+ if (this.loaded) return this.current;
6745
+ const rootObj = this.root;
6746
+ const typeValue = rootObj[this.relation.typeField];
6747
+ const idValue = rootObj[this.relation.idField];
6748
+ if (!typeValue || idValue === void 0 || idValue === null) {
6749
+ this.loaded = true;
6750
+ return this.current;
6751
+ }
6752
+ const map = await this.loader();
6753
+ const compositeKey = `${toKey9(typeValue)}:${toKey9(idValue)}`;
6754
+ const row = map.get(compositeKey);
6755
+ if (row) {
6756
+ const targetTable = this.resolveTargetTable(toKey9(typeValue));
6757
+ if (targetTable) {
6758
+ this.current = this.createEntity(targetTable, row);
6759
+ }
6760
+ }
6761
+ this.loaded = true;
6762
+ return this.current;
6763
+ }
6764
+ get() {
6765
+ return this.current;
6766
+ }
6767
+ set(data) {
6768
+ if (data === null) {
6769
+ return this.detachCurrent();
6770
+ }
6771
+ const entity = hasEntityMeta(data) ? data : data;
6772
+ if (this.current && this.current !== entity) {
6773
+ this.ctx.registerRelationChange(
6774
+ this.root,
6775
+ this.relationKey,
6776
+ this.rootTable,
6777
+ this.relationName,
6778
+ this.relation,
6779
+ { kind: "remove", entity: this.current }
6780
+ );
6781
+ }
6782
+ this.current = entity;
6783
+ this.loaded = true;
6784
+ this.ctx.registerRelationChange(
6785
+ this.root,
6786
+ this.relationKey,
6787
+ this.rootTable,
6788
+ this.relationName,
6789
+ this.relation,
6790
+ { kind: "attach", entity }
6791
+ );
6792
+ return entity;
6793
+ }
6794
+ toJSON() {
6795
+ if (!this.current) return null;
6796
+ const entityWithToJSON = this.current;
6797
+ return typeof entityWithToJSON.toJSON === "function" ? entityWithToJSON.toJSON() : this.current;
6798
+ }
6799
+ detachCurrent() {
6800
+ const previous = this.current;
6801
+ if (!previous) return null;
6802
+ this.current = null;
6803
+ this.loaded = true;
6804
+ const rootObj = this.root;
6805
+ rootObj[this.relation.typeField] = null;
6806
+ rootObj[this.relation.idField] = null;
6807
+ this.ctx.registerRelationChange(
6808
+ this.root,
6809
+ this.relationKey,
6810
+ this.rootTable,
6811
+ this.relationName,
6812
+ this.relation,
6813
+ { kind: "remove", entity: previous }
6814
+ );
6815
+ return null;
6816
+ }
6817
+ get relationKey() {
6818
+ return `${this.rootTable.name}.${this.relationName}`;
6819
+ }
6820
+ populateFromHydrationCache() {
6821
+ const rootObj = this.root;
6822
+ const typeValue = rootObj[this.relation.typeField];
6823
+ const idValue = rootObj[this.relation.idField];
6824
+ if (!typeValue || idValue === void 0 || idValue === null) return;
6825
+ const compositeKey = `${toKey9(typeValue)}:${toKey9(idValue)}`;
6826
+ const row = getHydrationRecord(this.meta, this.relationName, compositeKey);
6827
+ if (!row) return;
6828
+ const targetTable = this.resolveTargetTable(toKey9(typeValue));
6829
+ if (targetTable) {
6830
+ this.current = this.createEntity(targetTable, row);
6831
+ this.loaded = true;
6832
+ }
6833
+ }
6834
+ };
6835
+
6836
+ // src/orm/lazy-batch/shared.ts
6837
+ var hasColumns = (columns) => Boolean(columns && columns.length > 0);
6838
+ var buildColumnSelection = (table, columns, missingMsg) => {
6839
+ return columns.reduce((acc, column) => {
6840
+ const def = table.columns[column];
6841
+ if (!def) {
6842
+ throw new Error(missingMsg(column));
6843
+ }
6844
+ acc[column] = def;
6845
+ return acc;
6846
+ }, {});
6847
+ };
6848
+ var filterRow = (row, columns) => {
6849
+ const filtered = {};
6850
+ for (const column of columns) {
6851
+ if (column in row) {
6852
+ filtered[column] = row[column];
6853
+ }
6854
+ }
6855
+ return filtered;
6856
+ };
6857
+ var filterRows = (rows, columns) => rows.map((row) => filterRow(row, columns));
6858
+ var rowsFromResults = (results) => {
6859
+ const rows = [];
6860
+ for (const result of results) {
6861
+ const { columns, values } = result;
6862
+ for (const valueRow of values) {
6863
+ const row = {};
6864
+ columns.forEach((column, idx) => {
6865
+ row[column] = valueRow[idx];
6866
+ });
6867
+ rows.push(row);
6868
+ }
6869
+ }
6870
+ return rows;
6871
+ };
6872
+ var executeQuery = async (ctx, qb) => {
6873
+ const compiled = ctx.dialect.compileSelect(qb.getAST());
6874
+ const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
6875
+ return rowsFromResults(results);
6876
+ };
6877
+ var toKey10 = (value) => value === null || value === void 0 ? "" : String(value);
6878
+ var collectKeysFromRoots = (roots, key) => {
6879
+ const collected = /* @__PURE__ */ new Set();
6880
+ for (const tracked of roots) {
6881
+ const value = tracked.entity[key];
6882
+ if (value !== null && value !== void 0) {
6883
+ collected.add(value);
6884
+ }
6885
+ }
6886
+ return collected;
6887
+ };
6888
+ var buildInListValues = (keys) => Array.from(keys);
6889
+ var fetchRowsForKeys = async (ctx, table, column, keys, selection, filter) => {
6890
+ let qb = new SelectQueryBuilder(table).select(selection);
6891
+ qb = qb.where(inList(column, buildInListValues(keys)));
6892
+ if (filter) {
6893
+ qb = qb.where(filter);
6894
+ }
6895
+ return executeQuery(ctx, qb);
6896
+ };
6897
+ var groupRowsByMany = (rows, keyColumn) => {
6898
+ const grouped = /* @__PURE__ */ new Map();
6899
+ for (const row of rows) {
6900
+ const value = row[keyColumn];
6901
+ if (value === null || value === void 0) continue;
6902
+ const key = toKey10(value);
6903
+ const bucket = grouped.get(key) ?? [];
6904
+ bucket.push(row);
6905
+ grouped.set(key, bucket);
6906
+ }
6907
+ return grouped;
6908
+ };
6909
+ var groupRowsByUnique = (rows, keyColumn) => {
6910
+ const lookup = /* @__PURE__ */ new Map();
6911
+ for (const row of rows) {
6912
+ const value = row[keyColumn];
6913
+ if (value === null || value === void 0) continue;
6914
+ const key = toKey10(value);
6915
+ if (!lookup.has(key)) {
6916
+ lookup.set(key, row);
6917
+ }
6918
+ }
6919
+ return lookup;
6920
+ };
6921
+
6922
+ // src/orm/lazy-batch/has-many.ts
6923
+ var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options) => {
6924
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
6925
+ const roots = ctx.getEntitiesForTable(rootTable);
6926
+ const keys = collectKeysFromRoots(roots, localKey);
6927
+ if (!keys.size) {
6928
+ return /* @__PURE__ */ new Map();
6929
+ }
6930
+ const fkColumn = relation.target.columns[relation.foreignKey];
6931
+ if (!fkColumn) return /* @__PURE__ */ new Map();
6932
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
6933
+ const targetPrimaryKey = findPrimaryKey(relation.target);
6934
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
6935
+ if (!selectedColumns.includes(targetPrimaryKey)) {
6936
+ selectedColumns.push(targetPrimaryKey);
6937
+ }
6938
+ const queryColumns = new Set(selectedColumns);
6939
+ queryColumns.add(relation.foreignKey);
6940
+ const selection = buildColumnSelection(
6941
+ relation.target,
6942
+ Array.from(queryColumns),
6313
6943
  (column) => `Column '${column}' not found on relation '${relationName}'`
6314
6944
  );
6315
6945
  const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, options?.filter);
@@ -6466,12 +7096,12 @@ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, o
6466
7096
  if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
6467
7097
  continue;
6468
7098
  }
6469
- const bucket = rootLookup.get(toKey7(rootValue)) ?? [];
7099
+ const bucket = rootLookup.get(toKey10(rootValue)) ?? [];
6470
7100
  bucket.push({
6471
7101
  targetId: targetValue,
6472
7102
  pivot: pivotVisibleColumns.size ? filterRow(pivot, pivotVisibleColumns) : {}
6473
7103
  });
6474
- rootLookup.set(toKey7(rootValue), bucket);
7104
+ rootLookup.set(toKey10(rootValue), bucket);
6475
7105
  targetIds.add(targetValue);
6476
7106
  }
6477
7107
  if (!targetIds.size) {
@@ -6501,27 +7131,144 @@ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, o
6501
7131
  const targetMap = groupRowsByUnique(targetRows, targetKey);
6502
7132
  const targetVisibleColumns = new Set(targetSelectedColumns);
6503
7133
  const result = /* @__PURE__ */ new Map();
6504
- for (const [rootId, entries] of rootLookup.entries()) {
6505
- const bucket = [];
6506
- for (const entry of entries) {
6507
- const targetRow = targetMap.get(toKey7(entry.targetId));
6508
- if (!targetRow) continue;
6509
- const row = targetRequestedColumns ? filterRow(targetRow, targetVisibleColumns) : { ...targetRow };
6510
- if (options?.pivot?.merge) {
6511
- mergePivotIntoRow(row, entry.pivot);
6512
- }
6513
- bucket.push({ ...row, _pivot: entry.pivot });
7134
+ for (const [rootId, entries] of rootLookup.entries()) {
7135
+ const bucket = [];
7136
+ for (const entry of entries) {
7137
+ const targetRow = targetMap.get(toKey10(entry.targetId));
7138
+ if (!targetRow) continue;
7139
+ const row = targetRequestedColumns ? filterRow(targetRow, targetVisibleColumns) : { ...targetRow };
7140
+ if (options?.pivot?.merge) {
7141
+ mergePivotIntoRow(row, entry.pivot);
7142
+ }
7143
+ bucket.push({ ...row, _pivot: entry.pivot });
7144
+ }
7145
+ result.set(rootId, bucket);
7146
+ }
7147
+ return result;
7148
+ };
7149
+ var mergePivotIntoRow = (row, pivot) => {
7150
+ for (const [key, value] of Object.entries(pivot)) {
7151
+ if (key in row) continue;
7152
+ row[key] = value;
7153
+ }
7154
+ };
7155
+
7156
+ // src/orm/lazy-batch/morph-one.ts
7157
+ var loadMorphOneRelation = async (ctx, rootTable, relationName, relation, options) => {
7158
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
7159
+ const roots = ctx.getEntitiesForTable(rootTable);
7160
+ const keys = collectKeysFromRoots(roots, localKey);
7161
+ if (!keys.size) {
7162
+ return /* @__PURE__ */ new Map();
7163
+ }
7164
+ const fkColumn = relation.target.columns[relation.idField];
7165
+ if (!fkColumn) return /* @__PURE__ */ new Map();
7166
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
7167
+ const targetPrimaryKey = findPrimaryKey(relation.target);
7168
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
7169
+ if (!selectedColumns.includes(targetPrimaryKey)) {
7170
+ selectedColumns.push(targetPrimaryKey);
7171
+ }
7172
+ const queryColumns = new Set(selectedColumns);
7173
+ queryColumns.add(relation.idField);
7174
+ const selection = buildColumnSelection(
7175
+ relation.target,
7176
+ Array.from(queryColumns),
7177
+ (column) => `Column '${column}' not found on relation '${relationName}'`
7178
+ );
7179
+ const typeColumn = relation.target.columns[relation.typeField];
7180
+ const discriminatorFilter = eq(
7181
+ typeColumn ?? { type: "Column", table: relation.target.name, name: relation.typeField },
7182
+ { type: "Literal", value: relation.typeValue }
7183
+ );
7184
+ const combinedFilter = options?.filter ? and(options.filter, discriminatorFilter) : discriminatorFilter;
7185
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, combinedFilter);
7186
+ const grouped = groupRowsByUnique(rows, relation.idField);
7187
+ if (!requestedColumns) return grouped;
7188
+ const visibleColumns = new Set(selectedColumns);
7189
+ const filtered = /* @__PURE__ */ new Map();
7190
+ for (const [key, row] of grouped.entries()) {
7191
+ filtered.set(key, filterRow(row, visibleColumns));
7192
+ }
7193
+ return filtered;
7194
+ };
7195
+
7196
+ // src/orm/lazy-batch/morph-many.ts
7197
+ var loadMorphManyRelation = async (ctx, rootTable, relationName, relation, options) => {
7198
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
7199
+ const roots = ctx.getEntitiesForTable(rootTable);
7200
+ const keys = collectKeysFromRoots(roots, localKey);
7201
+ if (!keys.size) {
7202
+ return /* @__PURE__ */ new Map();
7203
+ }
7204
+ const fkColumn = relation.target.columns[relation.idField];
7205
+ if (!fkColumn) return /* @__PURE__ */ new Map();
7206
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
7207
+ const targetPrimaryKey = findPrimaryKey(relation.target);
7208
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
7209
+ if (!selectedColumns.includes(targetPrimaryKey)) {
7210
+ selectedColumns.push(targetPrimaryKey);
7211
+ }
7212
+ const queryColumns = new Set(selectedColumns);
7213
+ queryColumns.add(relation.idField);
7214
+ const selection = buildColumnSelection(
7215
+ relation.target,
7216
+ Array.from(queryColumns),
7217
+ (column) => `Column '${column}' not found on relation '${relationName}'`
7218
+ );
7219
+ const typeColumn = relation.target.columns[relation.typeField];
7220
+ const discriminatorFilter = eq(
7221
+ typeColumn ?? { type: "Column", table: relation.target.name, name: relation.typeField },
7222
+ { type: "Literal", value: relation.typeValue }
7223
+ );
7224
+ const combinedFilter = options?.filter ? and(options.filter, discriminatorFilter) : discriminatorFilter;
7225
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, combinedFilter);
7226
+ const grouped = groupRowsByMany(rows, relation.idField);
7227
+ if (!requestedColumns) return grouped;
7228
+ const visibleColumns = new Set(selectedColumns);
7229
+ const filtered = /* @__PURE__ */ new Map();
7230
+ for (const [key, bucket] of grouped.entries()) {
7231
+ filtered.set(key, filterRows(bucket, visibleColumns));
7232
+ }
7233
+ return filtered;
7234
+ };
7235
+
7236
+ // src/orm/lazy-batch/morph-to.ts
7237
+ var loadMorphToRelation = async (ctx, rootTable, _relationName, relation) => {
7238
+ const roots = ctx.getEntitiesForTable(rootTable);
7239
+ const result = /* @__PURE__ */ new Map();
7240
+ const grouped = /* @__PURE__ */ new Map();
7241
+ for (const tracked of roots) {
7242
+ const entity = tracked.entity;
7243
+ const typeValue = entity[relation.typeField];
7244
+ const idValue = entity[relation.idField];
7245
+ if (!typeValue || idValue === void 0 || idValue === null) continue;
7246
+ const typeKey = toKey10(typeValue);
7247
+ const ids = grouped.get(typeKey) ?? /* @__PURE__ */ new Set();
7248
+ ids.add(idValue);
7249
+ grouped.set(typeKey, ids);
7250
+ }
7251
+ for (const [typeKey, ids] of grouped.entries()) {
7252
+ const targetTable = relation.targets[typeKey];
7253
+ if (!targetTable) continue;
7254
+ const targetPk = relation.targetKey || findPrimaryKey(targetTable);
7255
+ const pkColumn = targetTable.columns[targetPk];
7256
+ if (!pkColumn) continue;
7257
+ const selection = buildColumnSelection(
7258
+ targetTable,
7259
+ Object.keys(targetTable.columns),
7260
+ (column) => `Column '${column}' not found on target '${targetTable.name}'`
7261
+ );
7262
+ const rows = await fetchRowsForKeys(ctx, targetTable, pkColumn, ids, selection);
7263
+ for (const row of rows) {
7264
+ const pkValue = row[targetPk];
7265
+ if (pkValue === void 0 || pkValue === null) continue;
7266
+ const compositeKey = `${typeKey}:${toKey10(pkValue)}`;
7267
+ result.set(compositeKey, row);
6514
7268
  }
6515
- result.set(rootId, bucket);
6516
7269
  }
6517
7270
  return result;
6518
7271
  };
6519
- var mergePivotIntoRow = (row, pivot) => {
6520
- for (const [key, value] of Object.entries(pivot)) {
6521
- if (key in row) continue;
6522
- row[key] = value;
6523
- }
6524
- };
6525
7272
 
6526
7273
  // src/orm/entity-relation-cache.ts
6527
7274
  var relationLoaderCache = (meta, relationName, factory) => {
@@ -6688,6 +7435,60 @@ var instantiateWrapper = (meta, relationName, relation, owner, createEntity) =>
6688
7435
  localKey
6689
7436
  );
6690
7437
  }
7438
+ case RelationKinds.MorphOne: {
7439
+ const morphOne2 = relation;
7440
+ const localKey = morphOne2.localKey || findPrimaryKey(meta.table);
7441
+ const loader = () => loadCached(
7442
+ () => loadMorphOneRelation(meta.ctx, meta.table, relationName, morphOne2, resolveOptions())
7443
+ );
7444
+ return new DefaultMorphOneReference(
7445
+ meta.ctx,
7446
+ metaBase,
7447
+ owner,
7448
+ relationName,
7449
+ morphOne2,
7450
+ meta.table,
7451
+ loader,
7452
+ (row) => createEntity(morphOne2.target, row),
7453
+ localKey
7454
+ );
7455
+ }
7456
+ case RelationKinds.MorphMany: {
7457
+ const morphMany2 = relation;
7458
+ const localKey = morphMany2.localKey || findPrimaryKey(meta.table);
7459
+ const loader = () => loadCached(
7460
+ () => loadMorphManyRelation(meta.ctx, meta.table, relationName, morphMany2, resolveOptions())
7461
+ );
7462
+ return new DefaultMorphManyCollection(
7463
+ meta.ctx,
7464
+ metaBase,
7465
+ owner,
7466
+ relationName,
7467
+ morphMany2,
7468
+ meta.table,
7469
+ loader,
7470
+ (row) => createEntity(morphMany2.target, row),
7471
+ localKey
7472
+ );
7473
+ }
7474
+ case RelationKinds.MorphTo: {
7475
+ const morphTo2 = relation;
7476
+ const loader = () => loadCached(
7477
+ () => loadMorphToRelation(meta.ctx, meta.table, relationName, morphTo2)
7478
+ );
7479
+ const resolveTargetTable = (typeValue) => morphTo2.targets[typeValue];
7480
+ return new DefaultMorphToReference(
7481
+ meta.ctx,
7482
+ metaBase,
7483
+ owner,
7484
+ relationName,
7485
+ morphTo2,
7486
+ meta.table,
7487
+ loader,
7488
+ (table, row) => createEntity(table, row),
7489
+ resolveTargetTable
7490
+ );
7491
+ }
6691
7492
  default:
6692
7493
  return void 0;
6693
7494
  }
@@ -6713,7 +7514,7 @@ var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOption
6713
7514
  const isCollectionRelation = (relationName) => {
6714
7515
  const rel = table.relations[relationName];
6715
7516
  if (!rel) return false;
6716
- return rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany;
7517
+ return rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany || rel.type === RelationKinds.MorphMany;
6717
7518
  };
6718
7519
  const buildJson = (self, options) => {
6719
7520
  const json = {};
@@ -6895,9 +7696,11 @@ function rowsToQueryResult(rows) {
6895
7696
  }
6896
7697
  function createExecutorFromQueryRunner(runner) {
6897
7698
  const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
7699
+ const supportsSavepoints = supportsTransactions && typeof runner.savepoint === "function" && typeof runner.releaseSavepoint === "function" && typeof runner.rollbackToSavepoint === "function";
6898
7700
  return {
6899
7701
  capabilities: {
6900
- transactions: supportsTransactions
7702
+ transactions: supportsTransactions,
7703
+ ...supportsSavepoints ? { savepoints: true } : {}
6901
7704
  },
6902
7705
  async executeSql(sql, params) {
6903
7706
  const rows = await runner.query(sql, params);
@@ -6922,6 +7725,24 @@ function createExecutorFromQueryRunner(runner) {
6922
7725
  }
6923
7726
  await runner.rollbackTransaction.call(runner);
6924
7727
  },
7728
+ async savepoint(name) {
7729
+ if (!supportsSavepoints) {
7730
+ throw new Error("Savepoints are not supported by this executor");
7731
+ }
7732
+ await runner.savepoint.call(runner, name);
7733
+ },
7734
+ async releaseSavepoint(name) {
7735
+ if (!supportsSavepoints) {
7736
+ throw new Error("Savepoints are not supported by this executor");
7737
+ }
7738
+ await runner.releaseSavepoint.call(runner, name);
7739
+ },
7740
+ async rollbackToSavepoint(name) {
7741
+ if (!supportsSavepoints) {
7742
+ throw new Error("Savepoints are not supported by this executor");
7743
+ }
7744
+ await runner.rollbackToSavepoint.call(runner, name);
7745
+ },
6925
7746
  async dispose() {
6926
7747
  await runner.dispose?.call(runner);
6927
7748
  }
@@ -7296,6 +8117,9 @@ function buildWhereHasPredicate(env, context, relationFacet, createChildBuilder,
7296
8117
  }
7297
8118
  const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
7298
8119
  const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
8120
+ if (!isSingleTargetRelation(relation)) {
8121
+ throw new Error(`Polymorphic relation '${relationName}' does not support whereHas/whereHasNot`);
8122
+ }
7299
8123
  let subQb = createChildBuilder(relation.target);
7300
8124
  if (callback) {
7301
8125
  subQb = callback(subQb);
@@ -8882,6 +9706,7 @@ var isTableDef = (value) => {
8882
9706
 
8883
9707
  // src/decorators/decorator-metadata.ts
8884
9708
  var METADATA_KEY = "metal-orm:decorators";
9709
+ var LEGACY_METADATA_KEY = /* @__PURE__ */ Symbol.for("metal-orm:decorators:legacy");
8885
9710
  var getOrCreateMetadataBag = (context) => {
8886
9711
  const metadata = context.metadata || (context.metadata = {});
8887
9712
  let bag = metadata[METADATA_KEY];
@@ -8891,16 +9716,65 @@ var getOrCreateMetadataBag = (context) => {
8891
9716
  }
8892
9717
  return bag;
8893
9718
  };
9719
+ var getOrCreateMetadataBagOnConstructor = (ctor) => {
9720
+ const carrier = ctor;
9721
+ let bag = carrier[LEGACY_METADATA_KEY];
9722
+ if (!bag) {
9723
+ bag = { columns: [], relations: [], transformers: [] };
9724
+ carrier[LEGACY_METADATA_KEY] = bag;
9725
+ }
9726
+ return bag;
9727
+ };
8894
9728
  var readMetadataBag = (context) => {
8895
9729
  return context.metadata?.[METADATA_KEY];
8896
9730
  };
8897
9731
  var readMetadataBagFromConstructor = (ctor) => {
8898
9732
  const metadataSymbol = Symbol.metadata;
8899
- if (!metadataSymbol) return void 0;
8900
- const metadata = Reflect.get(ctor, metadataSymbol);
8901
- return metadata?.[METADATA_KEY];
9733
+ if (metadataSymbol) {
9734
+ const metadata = Reflect.get(ctor, metadataSymbol);
9735
+ const stage3Bag = metadata?.[METADATA_KEY];
9736
+ if (stage3Bag) {
9737
+ return stage3Bag;
9738
+ }
9739
+ }
9740
+ return ctor[LEGACY_METADATA_KEY];
8902
9741
  };
8903
9742
  var getDecoratorMetadata = (ctor) => readMetadataBagFromConstructor(ctor);
9743
+ var normalizePropertyName = (name) => {
9744
+ if (typeof name === "symbol") {
9745
+ return name.description ?? name.toString();
9746
+ }
9747
+ return name;
9748
+ };
9749
+ var isStage3FieldContext = (value) => {
9750
+ return typeof value === "object" && value !== null && "name" in value && "private" in value && "metadata" in value;
9751
+ };
9752
+ var resolveFieldDecoratorInfo = (targetOrValue, contextOrProperty, decoratorName) => {
9753
+ if (isStage3FieldContext(contextOrProperty)) {
9754
+ if (!contextOrProperty.name) {
9755
+ throw new Error(`${decoratorName} decorator requires a property name`);
9756
+ }
9757
+ if (contextOrProperty.private) {
9758
+ throw new Error(`${decoratorName} decorator does not support private fields`);
9759
+ }
9760
+ return {
9761
+ propertyName: normalizePropertyName(contextOrProperty.name),
9762
+ bag: getOrCreateMetadataBag(contextOrProperty)
9763
+ };
9764
+ }
9765
+ if (typeof contextOrProperty === "string" || typeof contextOrProperty === "symbol") {
9766
+ const legacyTarget = targetOrValue;
9767
+ const ctor = typeof legacyTarget === "function" ? legacyTarget : legacyTarget?.constructor;
9768
+ if (!ctor || typeof ctor !== "function" && typeof ctor !== "object") {
9769
+ throw new Error(`${decoratorName} decorator requires a class field target`);
9770
+ }
9771
+ return {
9772
+ propertyName: normalizePropertyName(contextOrProperty),
9773
+ bag: getOrCreateMetadataBagOnConstructor(ctor)
9774
+ };
9775
+ }
9776
+ throw new Error(`${decoratorName} decorator received an unsupported decorator context`);
9777
+ };
8904
9778
 
8905
9779
  // src/decorators/bootstrap.ts
8906
9780
  var unwrapTarget = (target) => {
@@ -8990,6 +9864,48 @@ var buildRelationDefinitions = (meta, tableMap) => {
8990
9864
  );
8991
9865
  break;
8992
9866
  }
9867
+ case RelationKinds.MorphOne: {
9868
+ relations[name] = morphOne(
9869
+ resolveTableTarget(relation.target, tableMap),
9870
+ {
9871
+ as: relation.morphName,
9872
+ typeValue: relation.typeValue,
9873
+ typeField: relation.typeField,
9874
+ idField: relation.idField,
9875
+ localKey: relation.localKey,
9876
+ cascade: relation.cascade
9877
+ }
9878
+ );
9879
+ break;
9880
+ }
9881
+ case RelationKinds.MorphMany: {
9882
+ relations[name] = morphMany(
9883
+ resolveTableTarget(relation.target, tableMap),
9884
+ {
9885
+ as: relation.morphName,
9886
+ typeValue: relation.typeValue,
9887
+ typeField: relation.typeField,
9888
+ idField: relation.idField,
9889
+ localKey: relation.localKey,
9890
+ cascade: relation.cascade
9891
+ }
9892
+ );
9893
+ break;
9894
+ }
9895
+ case RelationKinds.MorphTo: {
9896
+ const resolvedTargets = {};
9897
+ for (const [typeValue, targetResolver] of Object.entries(relation.targets)) {
9898
+ resolvedTargets[typeValue] = resolveTableTarget(targetResolver, tableMap);
9899
+ }
9900
+ relations[name] = morphTo({
9901
+ typeField: relation.typeField,
9902
+ idField: relation.idField,
9903
+ targets: resolvedTargets,
9904
+ targetKey: relation.targetKey,
9905
+ cascade: relation.cascade
9906
+ });
9907
+ break;
9908
+ }
8993
9909
  }
8994
9910
  }
8995
9911
  return relations;
@@ -9063,6 +9979,45 @@ var resolveSingleRelation = (relationName, relation, rootMeta) => {
9063
9979
  }
9064
9980
  );
9065
9981
  }
9982
+ case RelationKinds.MorphOne: {
9983
+ return morphOne(
9984
+ resolveTableTarget(relation.target, tableMap),
9985
+ {
9986
+ as: relation.morphName,
9987
+ typeValue: relation.typeValue,
9988
+ typeField: relation.typeField,
9989
+ idField: relation.idField,
9990
+ localKey: relation.localKey,
9991
+ cascade: relation.cascade
9992
+ }
9993
+ );
9994
+ }
9995
+ case RelationKinds.MorphMany: {
9996
+ return morphMany(
9997
+ resolveTableTarget(relation.target, tableMap),
9998
+ {
9999
+ as: relation.morphName,
10000
+ typeValue: relation.typeValue,
10001
+ typeField: relation.typeField,
10002
+ idField: relation.idField,
10003
+ localKey: relation.localKey,
10004
+ cascade: relation.cascade
10005
+ }
10006
+ );
10007
+ }
10008
+ case RelationKinds.MorphTo: {
10009
+ const resolvedTargets = {};
10010
+ for (const [typeValue, targetResolver] of Object.entries(relation.targets)) {
10011
+ resolvedTargets[typeValue] = resolveTableTarget(targetResolver, tableMap);
10012
+ }
10013
+ return morphTo({
10014
+ typeField: relation.typeField,
10015
+ idField: relation.idField,
10016
+ targets: resolvedTargets,
10017
+ targetKey: relation.targetKey,
10018
+ cascade: relation.cascade
10019
+ });
10020
+ }
9066
10021
  default:
9067
10022
  throw new Error(`Unknown relation kind for relation '${relationName}'`);
9068
10023
  }
@@ -12652,6 +13607,9 @@ var TypeScriptGenerator = class {
12652
13607
  visitLogicalExpression(logical) {
12653
13608
  return this.printLogicalExpression(logical);
12654
13609
  }
13610
+ visitNotExpression(notExpr) {
13611
+ return this.printNotExpression(notExpr);
13612
+ }
12655
13613
  visitNullExpression(nullExpr) {
12656
13614
  return this.printNullExpression(nullExpr);
12657
13615
  }
@@ -12727,6 +13685,9 @@ var TypeScriptGenerator = class {
12727
13685
  ${parts.join(",\n ")}
12728
13686
  )`;
12729
13687
  }
13688
+ printNotExpression(notExpr) {
13689
+ return `not(${this.printExpression(notExpr.operand)})`;
13690
+ }
12730
13691
  printArithmeticExpression(expr) {
12731
13692
  const left2 = this.printOperand(expr.left);
12732
13693
  const right2 = this.printOperand(expr.right);
@@ -13420,6 +14381,15 @@ var RelationChangeProcessor = class {
13420
14381
  case RelationKinds.BelongsTo:
13421
14382
  await this.handleBelongsToChange(entry);
13422
14383
  break;
14384
+ case RelationKinds.MorphOne:
14385
+ await this.handleMorphOneChange(entry);
14386
+ break;
14387
+ case RelationKinds.MorphMany:
14388
+ await this.handleMorphManyChange(entry);
14389
+ break;
14390
+ case RelationKinds.MorphTo:
14391
+ await this.handleMorphToChange(entry);
14392
+ break;
13423
14393
  }
13424
14394
  }
13425
14395
  }
@@ -13622,6 +14592,86 @@ var RelationChangeProcessor = class {
13622
14592
  }
13623
14593
  return Object.keys(payload).length ? payload : void 0;
13624
14594
  }
14595
+ async handleMorphOneChange(entry) {
14596
+ const relation = entry.relation;
14597
+ const target = entry.change.entity;
14598
+ if (!target) return;
14599
+ const tracked = this.unitOfWork.findTracked(target);
14600
+ if (!tracked) return;
14601
+ const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
14602
+ const rootValue = entry.root[localKey];
14603
+ if (rootValue === void 0 || rootValue === null) return;
14604
+ if (entry.change.kind === "add" || entry.change.kind === "attach") {
14605
+ const child = tracked.entity;
14606
+ child[relation.idField] = rootValue;
14607
+ child[relation.typeField] = relation.typeValue;
14608
+ this.unitOfWork.markDirty(tracked.entity);
14609
+ return;
14610
+ }
14611
+ if (entry.change.kind === "remove") {
14612
+ const child = tracked.entity;
14613
+ if (relation.cascade === "all" || relation.cascade === "remove") {
14614
+ this.unitOfWork.markRemoved(child);
14615
+ return;
14616
+ }
14617
+ child[relation.idField] = null;
14618
+ child[relation.typeField] = null;
14619
+ this.unitOfWork.markDirty(child);
14620
+ }
14621
+ }
14622
+ async handleMorphManyChange(entry) {
14623
+ const relation = entry.relation;
14624
+ const target = entry.change.entity;
14625
+ if (!target) return;
14626
+ const tracked = this.unitOfWork.findTracked(target);
14627
+ if (!tracked) return;
14628
+ const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
14629
+ const rootValue = entry.root[localKey];
14630
+ if (rootValue === void 0 || rootValue === null) return;
14631
+ if (entry.change.kind === "add" || entry.change.kind === "attach") {
14632
+ const child = tracked.entity;
14633
+ child[relation.idField] = rootValue;
14634
+ child[relation.typeField] = relation.typeValue;
14635
+ this.unitOfWork.markDirty(tracked.entity);
14636
+ return;
14637
+ }
14638
+ if (entry.change.kind === "remove") {
14639
+ const child = tracked.entity;
14640
+ if (relation.cascade === "all" || relation.cascade === "remove") {
14641
+ this.unitOfWork.markRemoved(child);
14642
+ return;
14643
+ }
14644
+ child[relation.idField] = null;
14645
+ child[relation.typeField] = null;
14646
+ this.unitOfWork.markDirty(child);
14647
+ }
14648
+ }
14649
+ async handleMorphToChange(entry) {
14650
+ const relation = entry.relation;
14651
+ const target = entry.change.entity;
14652
+ if (!target) return;
14653
+ if (entry.change.kind === "attach" || entry.change.kind === "add") {
14654
+ const targetEntity = target;
14655
+ for (const [typeValue, targetTable] of Object.entries(relation.targets)) {
14656
+ const targetPk = relation.targetKey || findPrimaryKey(targetTable);
14657
+ const pkValue = targetEntity[targetPk];
14658
+ if (pkValue !== void 0 && pkValue !== null) {
14659
+ const rootEntity = entry.root;
14660
+ rootEntity[relation.typeField] = typeValue;
14661
+ rootEntity[relation.idField] = pkValue;
14662
+ this.unitOfWork.markDirty(entry.root);
14663
+ break;
14664
+ }
14665
+ }
14666
+ return;
14667
+ }
14668
+ if (entry.change.kind === "remove") {
14669
+ const rootEntity = entry.root;
14670
+ rootEntity[relation.typeField] = null;
14671
+ rootEntity[relation.idField] = null;
14672
+ this.unitOfWork.markDirty(entry.root);
14673
+ }
14674
+ }
13625
14675
  };
13626
14676
 
13627
14677
  // src/orm/query-logger.ts
@@ -13638,6 +14688,9 @@ var createQueryLoggingExecutor = (executor, logger) => {
13638
14688
  beginTransaction: () => executor.beginTransaction(),
13639
14689
  commitTransaction: () => executor.commitTransaction(),
13640
14690
  rollbackTransaction: () => executor.rollbackTransaction(),
14691
+ savepoint: executor.savepoint ? (name) => executor.savepoint(name) : void 0,
14692
+ releaseSavepoint: executor.releaseSavepoint ? (name) => executor.releaseSavepoint(name) : void 0,
14693
+ rollbackToSavepoint: executor.rollbackToSavepoint ? (name) => executor.rollbackToSavepoint(name) : void 0,
13641
14694
  dispose: () => executor.dispose()
13642
14695
  };
13643
14696
  return wrapped;
@@ -13660,7 +14713,7 @@ var runInTransaction = async (executor, action) => {
13660
14713
  };
13661
14714
 
13662
14715
  // src/orm/save-graph.ts
13663
- var toKey8 = (value) => value === null || value === void 0 ? "" : String(value);
14716
+ var toKey11 = (value) => value === null || value === void 0 ? "" : String(value);
13664
14717
  var coerceColumnValue = (table, columnName, value, options) => {
13665
14718
  if (value === null || value === void 0) return value;
13666
14719
  const column = table.columns[columnName];
@@ -13722,11 +14775,11 @@ var isEntityInCollection = (items, pkName, entity) => {
13722
14775
  if (items.includes(entity)) return true;
13723
14776
  const entityPk = entity[pkName];
13724
14777
  if (entityPk === void 0 || entityPk === null) return false;
13725
- return items.some((item) => toKey8(item[pkName]) === toKey8(entityPk));
14778
+ return items.some((item) => toKey11(item[pkName]) === toKey11(entityPk));
13726
14779
  };
13727
14780
  var findInCollectionByPk = (items, pkName, pkValue) => {
13728
14781
  if (pkValue === void 0 || pkValue === null) return void 0;
13729
- return items.find((item) => toKey8(item[pkName]) === toKey8(pkValue));
14782
+ return items.find((item) => toKey11(item[pkName]) === toKey11(pkValue));
13730
14783
  };
13731
14784
  var extractPivotPayload = (payload) => {
13732
14785
  const pivot = payload._pivot ?? payload.pivot;
@@ -13755,13 +14808,13 @@ var handleHasMany = async (session, root, relationName, relation, payload, optio
13755
14808
  collection.attach(entity);
13756
14809
  }
13757
14810
  if (pkValue !== void 0 && pkValue !== null) {
13758
- seen.add(toKey8(pkValue));
14811
+ seen.add(toKey11(pkValue));
13759
14812
  }
13760
14813
  }
13761
14814
  if (options.pruneMissing) {
13762
14815
  for (const item of [...collection.getItems()]) {
13763
14816
  const pkValue = item[targetPk];
13764
- if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
14817
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey11(pkValue))) {
13765
14818
  collection.remove(item);
13766
14819
  }
13767
14820
  }
@@ -13819,7 +14872,7 @@ var handleBelongsToMany = async (session, root, relationName, relation, payload,
13819
14872
  if (typeof item === "number" || typeof item === "string") {
13820
14873
  const id = item;
13821
14874
  collection.attach(id);
13822
- seen.add(toKey8(id));
14875
+ seen.add(toKey11(id));
13823
14876
  continue;
13824
14877
  }
13825
14878
  const asObj = item;
@@ -13837,18 +14890,89 @@ var handleBelongsToMany = async (session, root, relationName, relation, payload,
13837
14890
  collection.attach(entity, pivotPayload);
13838
14891
  }
13839
14892
  if (pkValue !== void 0 && pkValue !== null) {
13840
- seen.add(toKey8(pkValue));
14893
+ seen.add(toKey11(pkValue));
13841
14894
  }
13842
14895
  }
13843
14896
  if (options.pruneMissing) {
13844
14897
  for (const item of [...collection.getItems()]) {
13845
14898
  const pkValue = item[targetPk];
13846
- if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
14899
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey11(pkValue))) {
13847
14900
  collection.detach(item);
13848
14901
  }
13849
14902
  }
13850
14903
  }
13851
14904
  };
14905
+ var handleMorphOne = async (session, root, relationName, relation, payload, options) => {
14906
+ const ref = root[relationName];
14907
+ if (payload === void 0) return;
14908
+ if (payload === null) {
14909
+ ref.set(null);
14910
+ return;
14911
+ }
14912
+ const pk = findPrimaryKey(relation.target);
14913
+ if (typeof payload === "number" || typeof payload === "string") {
14914
+ const entity = ref.set({ [pk]: payload });
14915
+ if (entity) {
14916
+ await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
14917
+ }
14918
+ return;
14919
+ }
14920
+ const attached = ref.set(payload);
14921
+ if (attached) {
14922
+ await applyGraphToEntity(session, relation.target, attached, payload, options);
14923
+ }
14924
+ };
14925
+ var handleMorphMany = async (session, root, relationName, relation, payload, options) => {
14926
+ if (!Array.isArray(payload)) return;
14927
+ const collection = root[relationName];
14928
+ await collection.load();
14929
+ const targetTable = relation.target;
14930
+ const targetPk = findPrimaryKey(targetTable);
14931
+ const existing = collection.getItems();
14932
+ const seen = /* @__PURE__ */ new Set();
14933
+ for (const item of payload) {
14934
+ if (item === null || item === void 0) continue;
14935
+ const asObj = typeof item === "object" ? item : { [targetPk]: item };
14936
+ const pkValue = asObj[targetPk];
14937
+ const current = findInCollectionByPk(existing, targetPk, pkValue) ?? (pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) : void 0);
14938
+ const entity = current ?? ensureEntity(session, targetTable, asObj, options);
14939
+ assignColumns(targetTable, entity, asObj, options);
14940
+ await applyGraphToEntity(session, targetTable, entity, asObj, options);
14941
+ if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
14942
+ collection.attach(entity);
14943
+ }
14944
+ if (pkValue !== void 0 && pkValue !== null) {
14945
+ seen.add(toKey11(pkValue));
14946
+ }
14947
+ }
14948
+ if (options.pruneMissing) {
14949
+ for (const item of [...collection.getItems()]) {
14950
+ const pkValue = item[targetPk];
14951
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey11(pkValue))) {
14952
+ collection.remove(item);
14953
+ }
14954
+ }
14955
+ }
14956
+ };
14957
+ var handleMorphTo = async (session, root, relationName, relation, payload, options) => {
14958
+ const ref = root[relationName];
14959
+ if (payload === void 0) return;
14960
+ if (payload === null) {
14961
+ ref.set(null);
14962
+ return;
14963
+ }
14964
+ const attached = ref.set(payload);
14965
+ if (attached) {
14966
+ for (const [, targetTable] of Object.entries(relation.targets)) {
14967
+ const pk = relation.targetKey || findPrimaryKey(targetTable);
14968
+ const pkValue = attached[pk];
14969
+ if (pkValue !== void 0 && pkValue !== null) {
14970
+ await applyGraphToEntity(session, targetTable, attached, payload, options);
14971
+ break;
14972
+ }
14973
+ }
14974
+ }
14975
+ };
13852
14976
  var applyRelation = async (session, table, entity, relationName, relation, payload, options) => {
13853
14977
  switch (relation.type) {
13854
14978
  case RelationKinds.HasMany:
@@ -13859,6 +14983,12 @@ var applyRelation = async (session, table, entity, relationName, relation, paylo
13859
14983
  return handleBelongsTo(session, entity, relationName, relation, payload, options);
13860
14984
  case RelationKinds.BelongsToMany:
13861
14985
  return handleBelongsToMany(session, entity, relationName, relation, payload, options);
14986
+ case RelationKinds.MorphOne:
14987
+ return handleMorphOne(session, entity, relationName, relation, payload, options);
14988
+ case RelationKinds.MorphMany:
14989
+ return handleMorphMany(session, entity, relationName, relation, payload, options);
14990
+ case RelationKinds.MorphTo:
14991
+ return handleMorphTo(session, entity, relationName, relation, payload, options);
13862
14992
  }
13863
14993
  };
13864
14994
  var applyGraphToEntity = async (session, table, entity, payload, options) => {
@@ -13887,6 +15017,8 @@ var patchGraphInternal = async (session, entityClass, existing, payload, options
13887
15017
  };
13888
15018
 
13889
15019
  // src/orm/orm-session.ts
15020
+ var NESTED_TRANSACTIONS_REQUIRE_SAVEPOINTS = "Nested session.transaction calls require savepoint support in this executor";
15021
+ var ROLLBACK_ONLY_TRANSACTION = "Cannot commit transaction because an inner transaction failed";
13890
15022
  var OrmSession = class {
13891
15023
  /** The ORM instance */
13892
15024
  orm;
@@ -13906,6 +15038,9 @@ var OrmSession = class {
13906
15038
  tenantId;
13907
15039
  interceptors;
13908
15040
  saveGraphDefaults;
15041
+ transactionDepth = 0;
15042
+ savepointCounter = 0;
15043
+ rollbackOnly = false;
13909
15044
  /**
13910
15045
  * Creates a new OrmSession instance.
13911
15046
  * @param opts - Session options
@@ -14248,16 +15383,43 @@ var OrmSession = class {
14248
15383
  await this.commit();
14249
15384
  return result;
14250
15385
  }
14251
- await this.executor.beginTransaction();
15386
+ const isOutermost = this.transactionDepth === 0;
15387
+ let savepointName = null;
15388
+ if (isOutermost) {
15389
+ this.rollbackOnly = false;
15390
+ await this.executor.beginTransaction();
15391
+ } else {
15392
+ this.assertSavepointSupport();
15393
+ savepointName = this.nextSavepointName();
15394
+ await this.executor.savepoint(savepointName);
15395
+ }
15396
+ this.transactionDepth += 1;
14252
15397
  try {
14253
15398
  const result = await fn8(this);
15399
+ this.throwIfRollbackOnly();
14254
15400
  await this.flushWithHooks();
14255
- await this.executor.commitTransaction();
14256
- await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
15401
+ this.throwIfRollbackOnly();
15402
+ if (isOutermost) {
15403
+ await this.executor.commitTransaction();
15404
+ await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
15405
+ } else {
15406
+ await this.executor.releaseSavepoint(savepointName);
15407
+ }
14257
15408
  return result;
14258
15409
  } catch (err) {
14259
- await this.rollback();
15410
+ if (isOutermost) {
15411
+ await this.rollback();
15412
+ } else {
15413
+ this.rollbackOnly = true;
15414
+ await this.executor.rollbackToSavepoint(savepointName);
15415
+ }
14260
15416
  throw err;
15417
+ } finally {
15418
+ this.transactionDepth = Math.max(0, this.transactionDepth - 1);
15419
+ if (this.transactionDepth === 0) {
15420
+ this.rollbackOnly = false;
15421
+ this.savepointCounter = 0;
15422
+ }
14261
15423
  }
14262
15424
  }
14263
15425
  /**
@@ -14267,6 +15429,9 @@ var OrmSession = class {
14267
15429
  if (this.executor.capabilities.transactions) {
14268
15430
  await this.executor.rollbackTransaction();
14269
15431
  }
15432
+ this.transactionDepth = 0;
15433
+ this.savepointCounter = 0;
15434
+ this.rollbackOnly = false;
14270
15435
  this.unitOfWork.reset();
14271
15436
  this.relationChanges.reset();
14272
15437
  }
@@ -14341,6 +15506,23 @@ var OrmSession = class {
14341
15506
  resolveSaveGraphOptions(options) {
14342
15507
  return { ...this.saveGraphDefaults ?? {}, ...options ?? {} };
14343
15508
  }
15509
+ assertSavepointSupport() {
15510
+ if (!this.executor.capabilities.savepoints) {
15511
+ throw new Error(NESTED_TRANSACTIONS_REQUIRE_SAVEPOINTS);
15512
+ }
15513
+ if (typeof this.executor.savepoint !== "function" || typeof this.executor.releaseSavepoint !== "function" || typeof this.executor.rollbackToSavepoint !== "function") {
15514
+ throw new Error(NESTED_TRANSACTIONS_REQUIRE_SAVEPOINTS);
15515
+ }
15516
+ }
15517
+ nextSavepointName() {
15518
+ this.savepointCounter += 1;
15519
+ return `metalorm_sp_${this.savepointCounter}`;
15520
+ }
15521
+ throwIfRollbackOnly() {
15522
+ if (this.rollbackOnly) {
15523
+ throw new Error(ROLLBACK_ONLY_TRANSACTION);
15524
+ }
15525
+ }
14344
15526
  };
14345
15527
  var buildRelationChangeEntry = (root, relationKey, rootTable, relationName, relation, change) => ({
14346
15528
  root,
@@ -14865,7 +16047,7 @@ var TREE_METADATA_KEY = /* @__PURE__ */ Symbol("metal-orm:tree");
14865
16047
  function Tree(options = {}) {
14866
16048
  return function(target, context) {
14867
16049
  const config = resolveTreeConfig(options);
14868
- const metadataBag = readMetadataBag(context) ?? readMetadataBagFromConstructor(target);
16050
+ const metadataBag = context ? readMetadataBag(context) ?? readMetadataBagFromConstructor(target) : readMetadataBagFromConstructor(target);
14869
16051
  const metadata = {
14870
16052
  config,
14871
16053
  parentProperty: metadataBag?.tree?.parentProperty,
@@ -14877,28 +16059,22 @@ function Tree(options = {}) {
14877
16059
  };
14878
16060
  }
14879
16061
  function TreeParent() {
14880
- return function(_value, context) {
14881
- if (!context.name) {
14882
- throw new Error("TreeParent decorator requires a property name");
14883
- }
14884
- if (context.private) {
14885
- throw new Error("TreeParent decorator does not support private fields");
14886
- }
14887
- const propertyName = String(context.name);
14888
- const bag = getOrCreateMetadataBag(context);
16062
+ return function(targetOrValue, contextOrProperty) {
16063
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
16064
+ targetOrValue,
16065
+ contextOrProperty,
16066
+ "TreeParent"
16067
+ );
14889
16068
  bag.tree = { ...bag.tree, parentProperty: propertyName };
14890
16069
  };
14891
16070
  }
14892
16071
  function TreeChildren() {
14893
- return function(_value, context) {
14894
- if (!context.name) {
14895
- throw new Error("TreeChildren decorator requires a property name");
14896
- }
14897
- if (context.private) {
14898
- throw new Error("TreeChildren decorator does not support private fields");
14899
- }
14900
- const propertyName = String(context.name);
14901
- const bag = getOrCreateMetadataBag(context);
16072
+ return function(targetOrValue, contextOrProperty) {
16073
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
16074
+ targetOrValue,
16075
+ contextOrProperty,
16076
+ "TreeChildren"
16077
+ );
14902
16078
  bag.tree = { ...bag.tree, childrenProperty: propertyName };
14903
16079
  };
14904
16080
  }
@@ -15062,7 +16238,7 @@ function Entity(options = {}) {
15062
16238
  const ctor = value;
15063
16239
  const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
15064
16240
  setEntityTableName(ctor, tableName, options.hooks, options.type);
15065
- const bag = readMetadataBag(context);
16241
+ const bag = context ? readMetadataBag(context) : readMetadataBagFromConstructor(ctor);
15066
16242
  if (bag) {
15067
16243
  const meta = ensureEntityMetadata(ctor);
15068
16244
  for (const entry of bag.columns) {
@@ -15116,29 +16292,20 @@ var normalizeColumnInput = (input) => {
15116
16292
  }
15117
16293
  return column;
15118
16294
  };
15119
- var normalizePropertyName = (name) => {
15120
- if (typeof name === "symbol") {
15121
- return name.description ?? name.toString();
15122
- }
15123
- return name;
15124
- };
15125
- var registerColumnFromContext = (context, column) => {
15126
- if (!context.name) {
15127
- throw new Error("Column decorator requires a property name");
15128
- }
15129
- if (context.private) {
15130
- throw new Error("Column decorator does not support private fields");
15131
- }
15132
- const propertyName = normalizePropertyName(context.name);
15133
- const bag = getOrCreateMetadataBag(context);
16295
+ var registerColumnFromContext = (targetOrValue, contextOrProperty, column) => {
16296
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
16297
+ targetOrValue,
16298
+ contextOrProperty,
16299
+ "Column"
16300
+ );
15134
16301
  if (!bag.columns.some((entry) => entry.propertyName === propertyName)) {
15135
16302
  bag.columns.push({ propertyName, column: { ...column } });
15136
16303
  }
15137
16304
  };
15138
16305
  function Column(definition) {
15139
16306
  const normalized = normalizeColumnInput(definition);
15140
- return function(_value, context) {
15141
- registerColumnFromContext(context, normalized);
16307
+ return function(targetOrValue, contextOrProperty) {
16308
+ registerColumnFromContext(targetOrValue, contextOrProperty, normalized);
15142
16309
  };
15143
16310
  }
15144
16311
  function PrimaryKey(definition) {
@@ -15148,22 +16315,13 @@ function PrimaryKey(definition) {
15148
16315
  }
15149
16316
 
15150
16317
  // src/decorators/relations.ts
15151
- var normalizePropertyName2 = (name) => {
15152
- if (typeof name === "symbol") {
15153
- return name.description ?? name.toString();
15154
- }
15155
- return name;
15156
- };
15157
16318
  var createFieldDecorator = (metadataFactory) => {
15158
- return function(_value, context) {
15159
- if (!context.name) {
15160
- throw new Error("Relation decorator requires a property name");
15161
- }
15162
- if (context.private) {
15163
- throw new Error("Relation decorator does not support private fields");
15164
- }
15165
- const propertyName = normalizePropertyName2(context.name);
15166
- const bag = getOrCreateMetadataBag(context);
16319
+ return function(targetOrValue, contextOrProperty) {
16320
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
16321
+ targetOrValue,
16322
+ contextOrProperty,
16323
+ "Relation"
16324
+ );
15167
16325
  const relationMetadata = metadataFactory(propertyName);
15168
16326
  if (!bag.relations.some((entry) => entry.propertyName === propertyName)) {
15169
16327
  bag.relations.push({ propertyName, relation: relationMetadata });
@@ -15215,6 +16373,43 @@ function BelongsToMany(options) {
15215
16373
  cascade: options.cascade
15216
16374
  }));
15217
16375
  }
16376
+ function MorphTo(options) {
16377
+ return createFieldDecorator((propertyName) => ({
16378
+ kind: RelationKinds.MorphTo,
16379
+ propertyKey: propertyName,
16380
+ typeField: options.typeField,
16381
+ idField: options.idField,
16382
+ targets: options.targets,
16383
+ targetKey: options.targetKey,
16384
+ cascade: options.cascade
16385
+ }));
16386
+ }
16387
+ function MorphOne(options) {
16388
+ return createFieldDecorator((propertyName) => ({
16389
+ kind: RelationKinds.MorphOne,
16390
+ propertyKey: propertyName,
16391
+ target: options.target,
16392
+ morphName: options.morphName,
16393
+ typeValue: options.typeValue,
16394
+ typeField: options.typeField,
16395
+ idField: options.idField,
16396
+ localKey: options.localKey,
16397
+ cascade: options.cascade
16398
+ }));
16399
+ }
16400
+ function MorphMany(options) {
16401
+ return createFieldDecorator((propertyName) => ({
16402
+ kind: RelationKinds.MorphMany,
16403
+ propertyKey: propertyName,
16404
+ target: options.target,
16405
+ morphName: options.morphName,
16406
+ typeValue: options.typeValue,
16407
+ typeField: options.typeField,
16408
+ idField: options.idField,
16409
+ localKey: options.localKey,
16410
+ cascade: options.cascade
16411
+ }));
16412
+ }
15218
16413
 
15219
16414
  // src/decorators/transformers/built-in/string-transformers.ts
15220
16415
  var TrimTransformer = class {
@@ -15400,15 +16595,12 @@ var PatternValidator = class {
15400
16595
  };
15401
16596
 
15402
16597
  // src/decorators/transformers/transformer-decorators.ts
15403
- var normalizePropertyName3 = (name) => {
15404
- if (typeof name === "symbol") {
15405
- return name.description ?? name.toString();
15406
- }
15407
- return name;
15408
- };
15409
- var registerTransformerMetadata = (context, metadata) => {
15410
- const propertyName = normalizePropertyName3(context.name);
15411
- const bag = getOrCreateMetadataBag(context);
16598
+ var registerTransformerMetadata = (targetOrValue, contextOrProperty, metadata) => {
16599
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
16600
+ targetOrValue,
16601
+ contextOrProperty,
16602
+ "Transformer"
16603
+ );
15412
16604
  let existing = bag.transformers.find((t) => t.propertyName === propertyName);
15413
16605
  if (!existing) {
15414
16606
  existing = {
@@ -15437,64 +16629,64 @@ var registerTransformerMetadata = (context, metadata) => {
15437
16629
  }
15438
16630
  };
15439
16631
  function Trim(options) {
15440
- return function(_value, context) {
15441
- registerTransformerMetadata(context, {
16632
+ return function(targetOrValue, contextOrProperty) {
16633
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15442
16634
  sanitizers: [new TrimTransformer(options)]
15443
16635
  });
15444
16636
  };
15445
16637
  }
15446
16638
  function Lower() {
15447
- return function(_value, context) {
15448
- registerTransformerMetadata(context, {
16639
+ return function(targetOrValue, contextOrProperty) {
16640
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15449
16641
  sanitizers: [new CaseTransformer("lower")]
15450
16642
  });
15451
16643
  };
15452
16644
  }
15453
16645
  function Upper() {
15454
- return function(_value, context) {
15455
- registerTransformerMetadata(context, {
16646
+ return function(targetOrValue, contextOrProperty) {
16647
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15456
16648
  sanitizers: [new CaseTransformer("upper")]
15457
16649
  });
15458
16650
  };
15459
16651
  }
15460
16652
  function Capitalize() {
15461
- return function(_value, context) {
15462
- registerTransformerMetadata(context, {
16653
+ return function(targetOrValue, contextOrProperty) {
16654
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15463
16655
  sanitizers: [new CaseTransformer("capitalize")]
15464
16656
  });
15465
16657
  };
15466
16658
  }
15467
16659
  function Title() {
15468
- return function(_value, context) {
15469
- registerTransformerMetadata(context, {
16660
+ return function(targetOrValue, contextOrProperty) {
16661
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15470
16662
  sanitizers: [new CaseTransformer("title")]
15471
16663
  });
15472
16664
  };
15473
16665
  }
15474
16666
  function Alphanumeric(options) {
15475
- return function(_value, context) {
15476
- registerTransformerMetadata(context, {
16667
+ return function(targetOrValue, contextOrProperty) {
16668
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15477
16669
  validators: [new AlphanumericValidator(options)]
15478
16670
  });
15479
16671
  };
15480
16672
  }
15481
16673
  function Email(options) {
15482
- return function(_value, context) {
15483
- registerTransformerMetadata(context, {
16674
+ return function(targetOrValue, contextOrProperty) {
16675
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15484
16676
  validators: [new EmailValidator(options)]
15485
16677
  });
15486
16678
  };
15487
16679
  }
15488
16680
  function Length(options) {
15489
- return function(_value, context) {
15490
- registerTransformerMetadata(context, {
16681
+ return function(targetOrValue, contextOrProperty) {
16682
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15491
16683
  validators: [new LengthValidator(options)]
15492
16684
  });
15493
16685
  };
15494
16686
  }
15495
16687
  function Pattern(options) {
15496
- return function(_value, context) {
15497
- registerTransformerMetadata(context, {
16688
+ return function(targetOrValue, contextOrProperty) {
16689
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15498
16690
  validators: [new PatternValidator(options)]
15499
16691
  });
15500
16692
  };
@@ -15721,16 +16913,12 @@ var CEPValidator = class {
15721
16913
  registerValidator("BR", "cpf", () => new CPFValidator());
15722
16914
  registerValidator("BR", "cnpj", () => new CNPJValidator());
15723
16915
  registerValidator("BR", "cep", () => new CEPValidator());
15724
- var normalizePropertyName4 = (name) => {
15725
- if (typeof name === "symbol") {
15726
- return name.description ?? name.toString();
15727
- }
15728
- return name;
16916
+ var resolveCountryDecoratorInfo = (targetOrValue, contextOrProperty) => {
16917
+ return resolveFieldDecoratorInfo(targetOrValue, contextOrProperty, "Country validator");
15729
16918
  };
15730
16919
  function CPF(options) {
15731
- return function(_value, context) {
15732
- const propertyName = normalizePropertyName4(context.name);
15733
- const bag = getOrCreateMetadataBag(context);
16920
+ return function(targetOrValue, contextOrProperty) {
16921
+ const { propertyName, bag } = resolveCountryDecoratorInfo(targetOrValue, contextOrProperty);
15734
16922
  let existing = bag.transformers.find((t) => t.propertyName === propertyName);
15735
16923
  if (!existing) {
15736
16924
  existing = {
@@ -15778,9 +16966,8 @@ function CPF(options) {
15778
16966
  };
15779
16967
  }
15780
16968
  function CNPJ(options) {
15781
- return function(_value, context) {
15782
- const propertyName = normalizePropertyName4(context.name);
15783
- const bag = getOrCreateMetadataBag(context);
16969
+ return function(targetOrValue, contextOrProperty) {
16970
+ const { propertyName, bag } = resolveCountryDecoratorInfo(targetOrValue, contextOrProperty);
15784
16971
  let existing = bag.transformers.find((t) => t.propertyName === propertyName);
15785
16972
  if (!existing) {
15786
16973
  existing = {
@@ -15828,9 +17015,8 @@ function CNPJ(options) {
15828
17015
  };
15829
17016
  }
15830
17017
  function CEP(options) {
15831
- return function(_value, context) {
15832
- const propertyName = normalizePropertyName4(context.name);
15833
- const bag = getOrCreateMetadataBag(context);
17018
+ return function(targetOrValue, contextOrProperty) {
17019
+ const { propertyName, bag } = resolveCountryDecoratorInfo(targetOrValue, contextOrProperty);
15834
17020
  let existing = bag.transformers.find((t) => t.propertyName === propertyName);
15835
17021
  if (!existing) {
15836
17022
  existing = {
@@ -16086,6 +17272,14 @@ var Pool = class {
16086
17272
  };
16087
17273
 
16088
17274
  // src/core/execution/executors/postgres-executor.ts
17275
+ var SAVEPOINT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
17276
+ var sanitizeSavepointName = (name) => {
17277
+ const trimmed = name.trim();
17278
+ if (!SAVEPOINT_NAME_PATTERN.test(trimmed)) {
17279
+ throw new Error(`Invalid savepoint name: "${name}"`);
17280
+ }
17281
+ return trimmed;
17282
+ };
16089
17283
  function createPostgresExecutor(client) {
16090
17284
  return createExecutorFromQueryRunner({
16091
17285
  async query(sql, params) {
@@ -16100,11 +17294,24 @@ function createPostgresExecutor(client) {
16100
17294
  },
16101
17295
  async rollbackTransaction() {
16102
17296
  await client.query("ROLLBACK");
17297
+ },
17298
+ async savepoint(name) {
17299
+ const savepoint = sanitizeSavepointName(name);
17300
+ await client.query(`SAVEPOINT ${savepoint}`);
17301
+ },
17302
+ async releaseSavepoint(name) {
17303
+ const savepoint = sanitizeSavepointName(name);
17304
+ await client.query(`RELEASE SAVEPOINT ${savepoint}`);
17305
+ },
17306
+ async rollbackToSavepoint(name) {
17307
+ const savepoint = sanitizeSavepointName(name);
17308
+ await client.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
16103
17309
  }
16104
17310
  });
16105
17311
  }
16106
17312
 
16107
17313
  // src/core/execution/executors/mysql-executor.ts
17314
+ var SAVEPOINT_NAME_PATTERN2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
16108
17315
  var isRowObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
16109
17316
  var isRowObjectArray = (value) => Array.isArray(value) && value.every(isRowObject);
16110
17317
  var isMysqlResultHeader = (value) => isRowObject(value) && ("affectedRows" in value || "insertId" in value || "warningStatus" in value || "serverStatus" in value);
@@ -16116,6 +17323,13 @@ var headerToQueryResult = (header) => ({
16116
17323
  rowsAffected: header.affectedRows
16117
17324
  }
16118
17325
  });
17326
+ var sanitizeSavepointName2 = (name) => {
17327
+ const trimmed = name.trim();
17328
+ if (!SAVEPOINT_NAME_PATTERN2.test(trimmed)) {
17329
+ throw new Error(`Invalid savepoint name: "${name}"`);
17330
+ }
17331
+ return trimmed;
17332
+ };
16119
17333
  var normalizeMysqlResults = (rows) => {
16120
17334
  if (!Array.isArray(rows)) {
16121
17335
  return isMysqlResultHeader(rows) ? [headerToQueryResult(rows)] : [rowsToQueryResult([])];
@@ -16137,9 +17351,11 @@ var normalizeMysqlResults = (rows) => {
16137
17351
  };
16138
17352
  function createMysqlExecutor(client) {
16139
17353
  const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
17354
+ const supportsSavepoints = supportsTransactions;
16140
17355
  return {
16141
17356
  capabilities: {
16142
- transactions: supportsTransactions
17357
+ transactions: supportsTransactions,
17358
+ ...supportsSavepoints ? { savepoints: true } : {}
16143
17359
  },
16144
17360
  async executeSql(sql, params) {
16145
17361
  const [rows] = await client.query(sql, params);
@@ -16163,17 +17379,55 @@ function createMysqlExecutor(client) {
16163
17379
  }
16164
17380
  await client.rollback();
16165
17381
  },
17382
+ async savepoint(name) {
17383
+ if (!supportsSavepoints) {
17384
+ throw new Error("Savepoints are not supported by this executor");
17385
+ }
17386
+ const savepoint = sanitizeSavepointName2(name);
17387
+ await client.query(`SAVEPOINT ${savepoint}`);
17388
+ },
17389
+ async releaseSavepoint(name) {
17390
+ if (!supportsSavepoints) {
17391
+ throw new Error("Savepoints are not supported by this executor");
17392
+ }
17393
+ const savepoint = sanitizeSavepointName2(name);
17394
+ await client.query(`RELEASE SAVEPOINT ${savepoint}`);
17395
+ },
17396
+ async rollbackToSavepoint(name) {
17397
+ if (!supportsSavepoints) {
17398
+ throw new Error("Savepoints are not supported by this executor");
17399
+ }
17400
+ const savepoint = sanitizeSavepointName2(name);
17401
+ await client.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
17402
+ },
16166
17403
  async dispose() {
16167
17404
  }
16168
17405
  };
16169
17406
  }
16170
17407
 
16171
17408
  // src/core/execution/executors/sqlite-executor.ts
17409
+ var SAVEPOINT_NAME_PATTERN3 = /^[A-Za-z_][A-Za-z0-9_]*$/;
17410
+ var sanitizeSavepointName3 = (name) => {
17411
+ const trimmed = name.trim();
17412
+ if (!SAVEPOINT_NAME_PATTERN3.test(trimmed)) {
17413
+ throw new Error(`Invalid savepoint name: "${name}"`);
17414
+ }
17415
+ return trimmed;
17416
+ };
16172
17417
  function createSqliteExecutor(client) {
16173
17418
  const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commitTransaction === "function" && typeof client.rollbackTransaction === "function";
17419
+ const supportsSavepoints = supportsTransactions;
17420
+ const executeControlStatement = async (sql) => {
17421
+ if (typeof client.run === "function") {
17422
+ await client.run(sql);
17423
+ return;
17424
+ }
17425
+ await client.all(sql);
17426
+ };
16174
17427
  return {
16175
17428
  capabilities: {
16176
- transactions: supportsTransactions
17429
+ transactions: supportsTransactions,
17430
+ ...supportsSavepoints ? { savepoints: true } : {}
16177
17431
  },
16178
17432
  async executeSql(sql, params) {
16179
17433
  const rows = await client.all(sql, params);
@@ -16198,17 +17452,120 @@ function createSqliteExecutor(client) {
16198
17452
  }
16199
17453
  await client.rollbackTransaction();
16200
17454
  },
17455
+ async savepoint(name) {
17456
+ if (!supportsSavepoints) {
17457
+ throw new Error("Savepoints are not supported by this executor");
17458
+ }
17459
+ const savepoint = sanitizeSavepointName3(name);
17460
+ if (typeof client.savepoint === "function") {
17461
+ await client.savepoint(savepoint);
17462
+ return;
17463
+ }
17464
+ await executeControlStatement(`SAVEPOINT ${savepoint}`);
17465
+ },
17466
+ async releaseSavepoint(name) {
17467
+ if (!supportsSavepoints) {
17468
+ throw new Error("Savepoints are not supported by this executor");
17469
+ }
17470
+ const savepoint = sanitizeSavepointName3(name);
17471
+ if (typeof client.releaseSavepoint === "function") {
17472
+ await client.releaseSavepoint(savepoint);
17473
+ return;
17474
+ }
17475
+ await executeControlStatement(`RELEASE SAVEPOINT ${savepoint}`);
17476
+ },
17477
+ async rollbackToSavepoint(name) {
17478
+ if (!supportsSavepoints) {
17479
+ throw new Error("Savepoints are not supported by this executor");
17480
+ }
17481
+ const savepoint = sanitizeSavepointName3(name);
17482
+ if (typeof client.rollbackToSavepoint === "function") {
17483
+ await client.rollbackToSavepoint(savepoint);
17484
+ return;
17485
+ }
17486
+ await executeControlStatement(`ROLLBACK TO SAVEPOINT ${savepoint}`);
17487
+ },
17488
+ async dispose() {
17489
+ }
17490
+ };
17491
+ }
17492
+
17493
+ // src/core/execution/executors/better-sqlite3-executor.ts
17494
+ var SAVEPOINT_NAME_PATTERN4 = /^[A-Za-z_][A-Za-z0-9_]*$/;
17495
+ var sanitizeSavepointName4 = (name) => {
17496
+ const trimmed = name.trim();
17497
+ if (!SAVEPOINT_NAME_PATTERN4.test(trimmed)) {
17498
+ throw new Error(`Invalid savepoint name: "${name}"`);
17499
+ }
17500
+ return trimmed;
17501
+ };
17502
+ function createBetterSqlite3Executor(client) {
17503
+ return {
17504
+ capabilities: {
17505
+ transactions: true,
17506
+ savepoints: true
17507
+ },
17508
+ async executeSql(sql, params) {
17509
+ const stmt = client.prepare(sql);
17510
+ let result;
17511
+ if (stmt.reader) {
17512
+ const rows = stmt.all(...params ?? []);
17513
+ result = rowsToQueryResult(rows);
17514
+ } else {
17515
+ const info = stmt.run(...params ?? []);
17516
+ result = {
17517
+ columns: [],
17518
+ values: [],
17519
+ meta: {
17520
+ rowsAffected: info.changes,
17521
+ insertId: typeof info.lastInsertRowid === "bigint" ? info.lastInsertRowid.toString() : info.lastInsertRowid
17522
+ }
17523
+ };
17524
+ }
17525
+ return toExecutionPayload([result]);
17526
+ },
17527
+ async beginTransaction() {
17528
+ client.prepare("BEGIN").run();
17529
+ },
17530
+ async commitTransaction() {
17531
+ client.prepare("COMMIT").run();
17532
+ },
17533
+ async rollbackTransaction() {
17534
+ client.prepare("ROLLBACK").run();
17535
+ },
17536
+ async savepoint(name) {
17537
+ const savepoint = sanitizeSavepointName4(name);
17538
+ client.prepare(`SAVEPOINT ${savepoint}`).run();
17539
+ },
17540
+ async releaseSavepoint(name) {
17541
+ const savepoint = sanitizeSavepointName4(name);
17542
+ client.prepare(`RELEASE SAVEPOINT ${savepoint}`).run();
17543
+ },
17544
+ async rollbackToSavepoint(name) {
17545
+ const savepoint = sanitizeSavepointName4(name);
17546
+ client.prepare(`ROLLBACK TO SAVEPOINT ${savepoint}`).run();
17547
+ },
16201
17548
  async dispose() {
16202
17549
  }
16203
17550
  };
16204
17551
  }
16205
17552
 
16206
17553
  // src/core/execution/executors/mssql-executor.ts
17554
+ var SAVEPOINT_NAME_PATTERN5 = /^[A-Za-z_][A-Za-z0-9_]*$/;
17555
+ var sanitizeSavepointName5 = (name) => {
17556
+ const trimmed = name.trim();
17557
+ if (!SAVEPOINT_NAME_PATTERN5.test(trimmed)) {
17558
+ throw new Error(`Invalid savepoint name: "${name}"`);
17559
+ }
17560
+ return trimmed;
17561
+ };
16207
17562
  function createMssqlExecutor(client) {
16208
17563
  const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
17564
+ const supportsSavepoints = supportsTransactions;
16209
17565
  return {
16210
17566
  capabilities: {
16211
- transactions: supportsTransactions
17567
+ transactions: supportsTransactions,
17568
+ ...supportsSavepoints ? { savepoints: true } : {}
16212
17569
  },
16213
17570
  async executeSql(sql, params) {
16214
17571
  const { recordset, recordsets } = await client.query(sql, params);
@@ -16233,6 +17590,25 @@ function createMssqlExecutor(client) {
16233
17590
  }
16234
17591
  await client.rollback();
16235
17592
  },
17593
+ async savepoint(name) {
17594
+ if (!supportsSavepoints) {
17595
+ throw new Error("Savepoints are not supported by this executor");
17596
+ }
17597
+ const savepoint = sanitizeSavepointName5(name);
17598
+ await client.query(`SAVE TRANSACTION ${savepoint}`);
17599
+ },
17600
+ async releaseSavepoint(_name) {
17601
+ if (!supportsSavepoints) {
17602
+ throw new Error("Savepoints are not supported by this executor");
17603
+ }
17604
+ },
17605
+ async rollbackToSavepoint(name) {
17606
+ if (!supportsSavepoints) {
17607
+ throw new Error("Savepoints are not supported by this executor");
17608
+ }
17609
+ const savepoint = sanitizeSavepointName5(name);
17610
+ await client.query(`ROLLBACK TRANSACTION ${savepoint}`);
17611
+ },
16236
17612
  async dispose() {
16237
17613
  }
16238
17614
  };
@@ -16308,6 +17684,7 @@ var isQueryResult = (value) => typeof value === "object" && value !== null && Ar
16308
17684
  var isRowArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "object" && item !== null && !Array.isArray(item));
16309
17685
  function createPooledExecutorFactory(opts) {
16310
17686
  const { pool, adapter } = opts;
17687
+ const supportsSavepoints = typeof adapter.savepoint === "function" && typeof adapter.releaseSavepoint === "function" && typeof adapter.rollbackToSavepoint === "function";
16311
17688
  const makeExecutor = (mode) => {
16312
17689
  let lease = null;
16313
17690
  const getLease = async () => {
@@ -16325,8 +17702,17 @@ function createPooledExecutorFactory(opts) {
16325
17702
  }
16326
17703
  return toExecutionPayload([rowsToQueryResult(rows)]);
16327
17704
  };
17705
+ const requireActiveTransactionLease = () => {
17706
+ if (!lease) {
17707
+ throw new Error("savepoint operation called without an active transaction");
17708
+ }
17709
+ return lease;
17710
+ };
16328
17711
  return {
16329
- capabilities: { transactions: true },
17712
+ capabilities: {
17713
+ transactions: true,
17714
+ ...supportsSavepoints ? { savepoints: true } : {}
17715
+ },
16330
17716
  async executeSql(sql, params) {
16331
17717
  if (mode === "sticky") {
16332
17718
  const l2 = await getLease();
@@ -16370,6 +17756,27 @@ function createPooledExecutorFactory(opts) {
16370
17756
  await l.release();
16371
17757
  }
16372
17758
  },
17759
+ async savepoint(name) {
17760
+ if (!supportsSavepoints) {
17761
+ throw new Error("Savepoints are not supported by this executor");
17762
+ }
17763
+ const l = requireActiveTransactionLease();
17764
+ await adapter.savepoint(l.resource, name);
17765
+ },
17766
+ async releaseSavepoint(name) {
17767
+ if (!supportsSavepoints) {
17768
+ throw new Error("Savepoints are not supported by this executor");
17769
+ }
17770
+ const l = requireActiveTransactionLease();
17771
+ await adapter.releaseSavepoint(l.resource, name);
17772
+ },
17773
+ async rollbackToSavepoint(name) {
17774
+ if (!supportsSavepoints) {
17775
+ throw new Error("Savepoints are not supported by this executor");
17776
+ }
17777
+ const l = requireActiveTransactionLease();
17778
+ await adapter.rollbackToSavepoint(l.resource, name);
17779
+ },
16373
17780
  async dispose() {
16374
17781
  if (!lease) return;
16375
17782
  const l = lease;
@@ -16464,6 +17871,9 @@ var updateInclude = (qb, relationPath, updater) => {
16464
17871
  filter: segmentOptions?.filter
16465
17872
  });
16466
17873
  }
17874
+ if (!isSingleTargetRelation(relation)) {
17875
+ continue;
17876
+ }
16467
17877
  const joinForSegment = findJoinByRelationKey(state.ast.joins, relationKey);
16468
17878
  currentAlias = joinForSegment ? getExposedName(joinForSegment.table) ?? relation.target.name : relation.target.name;
16469
17879
  currentTable = relation.target;
@@ -16592,6 +18002,9 @@ function applyRelationFilter(qb, table, relationName, filter) {
16592
18002
  `Relation filter "${relationName}" must include at least one of "some", "none", "every", "isEmpty", or "isNotEmpty".`
16593
18003
  );
16594
18004
  }
18005
+ if (!isSingleTargetRelation(relation)) {
18006
+ return qb;
18007
+ }
16595
18008
  if (filter.some) {
16596
18009
  const predicate = buildFilterExpression(relation.target, filter.some);
16597
18010
  if (predicate) {
@@ -16637,6 +18050,9 @@ function applyRelationFilter(qb, table, relationName, filter) {
16637
18050
  return qb;
16638
18051
  }
16639
18052
  var buildRelationSubqueryBase = (table, relation) => {
18053
+ if (!isSingleTargetRelation(relation)) {
18054
+ throw new Error("MorphTo relations do not support subquery-based filtering");
18055
+ }
16640
18056
  const target = relation.target;
16641
18057
  if (relation.type === RelationKinds.BelongsToMany) {
16642
18058
  const many = relation;
@@ -16682,7 +18098,14 @@ var buildRelationSubqueryBase = (table, relation) => {
16682
18098
  schema: target.schema
16683
18099
  };
16684
18100
  const correlation = buildRelationCorrelation(table, relation);
16685
- const groupByColumnName = relation.type === RelationKinds.BelongsTo ? relation.localKey || findPrimaryKey(target) : relation.foreignKey;
18101
+ let groupByColumnName;
18102
+ if (relation.type === RelationKinds.BelongsTo) {
18103
+ groupByColumnName = relation.localKey || findPrimaryKey(target);
18104
+ } else if (relation.type === RelationKinds.MorphOne || relation.type === RelationKinds.MorphMany) {
18105
+ groupByColumnName = relation.idField;
18106
+ } else {
18107
+ groupByColumnName = relation.foreignKey;
18108
+ }
16686
18109
  return {
16687
18110
  from,
16688
18111
  joins: [],
@@ -16714,6 +18137,9 @@ function buildRelationFilterExpression(table, relationName, filter) {
16714
18137
  where
16715
18138
  };
16716
18139
  };
18140
+ if (!isSingleTargetRelation(relation)) {
18141
+ return null;
18142
+ }
16717
18143
  if (filter.some) {
16718
18144
  const predicate = buildFilterExpression(relation.target, filter.some);
16719
18145
  if (predicate) {
@@ -19683,6 +21109,432 @@ var TagIndex = class {
19683
21109
  };
19684
21110
  }
19685
21111
  };
21112
+
21113
+ // src/bulk/bulk-context.ts
21114
+ function createBulkExecutionContext(session) {
21115
+ const executionContext = session.getExecutionContext();
21116
+ return {
21117
+ session,
21118
+ executionContext,
21119
+ dialect: executionContext.dialect,
21120
+ supportsReturning: executionContext.dialect.supportsDmlReturningClause()
21121
+ };
21122
+ }
21123
+ async function executeCompiled(ctx, compiled) {
21124
+ const payload = await ctx.executionContext.interceptors.run(
21125
+ { sql: compiled.sql, params: compiled.params },
21126
+ ctx.executionContext.executor
21127
+ );
21128
+ return extractResultSets(payload);
21129
+ }
21130
+ function extractResultSets(payload) {
21131
+ const result = payload;
21132
+ if (result.resultSets) {
21133
+ return result.resultSets;
21134
+ }
21135
+ return [];
21136
+ }
21137
+ function flattenQueryResults(resultSets) {
21138
+ const rows = [];
21139
+ for (const rs of resultSets) {
21140
+ for (const valueRow of rs.values) {
21141
+ const obj = {};
21142
+ rs.columns.forEach((col2, idx) => {
21143
+ const bare = col2.split(".").pop().replace(/^["`[\]]+|["`[\]]+$/g, "");
21144
+ obj[bare] = valueRow[idx];
21145
+ });
21146
+ rows.push(obj);
21147
+ }
21148
+ }
21149
+ return rows;
21150
+ }
21151
+ function resolveReturningColumns(ctx, table, returning) {
21152
+ if (!returning) return void 0;
21153
+ if (!ctx.supportsReturning) return void 0;
21154
+ if (returning === true) {
21155
+ return Object.values(table.columns).map((col2) => ({
21156
+ type: "Column",
21157
+ table: table.name,
21158
+ name: col2.name,
21159
+ alias: col2.name
21160
+ }));
21161
+ }
21162
+ return returning.map((col2) => ({
21163
+ type: "Column",
21164
+ table: table.name,
21165
+ name: col2.name,
21166
+ alias: col2.name
21167
+ }));
21168
+ }
21169
+
21170
+ // src/bulk/bulk-utils.ts
21171
+ function splitIntoChunks(items, size) {
21172
+ if (size < 1) throw new RangeError(`chunkSize must be >= 1, got ${size}`);
21173
+ const chunks = [];
21174
+ for (let i = 0; i < items.length; i += size) {
21175
+ chunks.push(items.slice(i, i + size));
21176
+ }
21177
+ return chunks;
21178
+ }
21179
+ async function runWithConcurrency(tasks, concurrency) {
21180
+ const limit = concurrency === "sequential" ? 1 : Math.max(1, concurrency);
21181
+ if (limit === 1) {
21182
+ const results2 = [];
21183
+ for (const task of tasks) {
21184
+ results2.push(await task());
21185
+ }
21186
+ return results2;
21187
+ }
21188
+ const results = new Array(tasks.length);
21189
+ let nextIndex = 0;
21190
+ const worker = async () => {
21191
+ while (nextIndex < tasks.length) {
21192
+ const i = nextIndex++;
21193
+ results[i] = await tasks[i]();
21194
+ }
21195
+ };
21196
+ const workers = Array.from({ length: Math.min(limit, tasks.length) }, worker);
21197
+ await Promise.all(workers);
21198
+ return results;
21199
+ }
21200
+ async function runChunk(task, chunkIndex, totalChunks, rowsInChunk, timing, onChunkComplete) {
21201
+ const start = timing || onChunkComplete ? Date.now() : 0;
21202
+ const result = await task();
21203
+ const elapsedMs = start ? Date.now() - start : 0;
21204
+ if (onChunkComplete) {
21205
+ await onChunkComplete({ chunkIndex, totalChunks, rowsInChunk, elapsedMs });
21206
+ }
21207
+ return result;
21208
+ }
21209
+ async function maybeTransaction(session, transactional, fn8) {
21210
+ if (!transactional) return fn8();
21211
+ const ormSession = session;
21212
+ return ormSession.transaction(fn8);
21213
+ }
21214
+ function aggregateOutcomes(outcomes) {
21215
+ const result = {
21216
+ processedRows: 0,
21217
+ chunksExecuted: outcomes.length,
21218
+ returning: []
21219
+ };
21220
+ for (const o of outcomes) {
21221
+ result.processedRows += o.processedRows;
21222
+ result.returning.push(...o.returning);
21223
+ }
21224
+ return result;
21225
+ }
21226
+ function aggregateOutcomesWithTimings(outcomes) {
21227
+ const result = aggregateOutcomes(outcomes);
21228
+ result.chunkTimings = outcomes.map((o) => o.elapsedMs);
21229
+ return result;
21230
+ }
21231
+
21232
+ // src/bulk/bulk-executor.base.ts
21233
+ var DEFAULT_CHUNK_SIZE = 500;
21234
+ var BulkBaseExecutor = class {
21235
+ session;
21236
+ table;
21237
+ ctx;
21238
+ options;
21239
+ chunks;
21240
+ totalChunks;
21241
+ constructor(session, table, rows, options = {}) {
21242
+ this.session = session;
21243
+ this.table = table;
21244
+ this.ctx = createBulkExecutionContext(session);
21245
+ this.options = {
21246
+ chunkSize: DEFAULT_CHUNK_SIZE,
21247
+ concurrency: "sequential",
21248
+ transactional: true,
21249
+ timing: false,
21250
+ ...options
21251
+ };
21252
+ this.chunks = splitIntoChunks(rows, this.options.chunkSize);
21253
+ this.totalChunks = this.chunks.length;
21254
+ }
21255
+ async execute() {
21256
+ const buildTask = (chunk, chunkIndex) => async () => {
21257
+ return runChunk(
21258
+ () => this.executeChunk(chunk, chunkIndex),
21259
+ chunkIndex,
21260
+ this.totalChunks,
21261
+ chunk.length,
21262
+ this.options.timing,
21263
+ this.options.onChunkComplete
21264
+ );
21265
+ };
21266
+ const tasks = this.chunks.map((chunk, i) => buildTask(chunk, i));
21267
+ const outcomes = await maybeTransaction(
21268
+ this.session,
21269
+ this.options.transactional,
21270
+ () => runWithConcurrency(tasks, this.options.concurrency)
21271
+ );
21272
+ return this.options.timing ? aggregateOutcomesWithTimings(outcomes) : aggregateOutcomes(outcomes);
21273
+ }
21274
+ };
21275
+
21276
+ // src/bulk/bulk-insert-executor.ts
21277
+ var BulkInsertExecutor = class extends BulkBaseExecutor {
21278
+ constructor(session, table, rows, options = {}) {
21279
+ super(session, table, rows, options);
21280
+ }
21281
+ async executeChunk(chunk, chunkIndex) {
21282
+ const returningColumns = resolveReturningColumns(this.ctx, this.table, this.options.returning);
21283
+ let builder = new InsertQueryBuilder(this.table).values(chunk);
21284
+ if (this.options.onConflict) {
21285
+ const conflictColumns = this.options.onConflict.target?.columns ?? [];
21286
+ builder = builder.onConflict(conflictColumns);
21287
+ if (this.options.onConflict.action.type === "DoNothing") {
21288
+ builder = builder.doNothing();
21289
+ } else if (this.options.onConflict.action.type === "DoUpdate" && this.options.onConflict.action.set) {
21290
+ const setMap = {};
21291
+ for (const assignment of this.options.onConflict.action.set) {
21292
+ const colName = typeof assignment.column === "object" ? assignment.column.name : assignment.column;
21293
+ setMap[colName] = assignment.value;
21294
+ }
21295
+ builder = builder.doUpdate(setMap);
21296
+ }
21297
+ }
21298
+ const finalBuilder = builder;
21299
+ if (returningColumns?.length) {
21300
+ finalBuilder.returning(...returningColumns);
21301
+ }
21302
+ const compiled = finalBuilder.compile(this.ctx.dialect);
21303
+ const resultSets = await executeCompiled(this.ctx, compiled);
21304
+ return {
21305
+ processedRows: chunk.length,
21306
+ returning: returningColumns ? flattenQueryResults(resultSets) : [],
21307
+ elapsedMs: 0
21308
+ };
21309
+ }
21310
+ };
21311
+ async function bulkInsert(session, table, rows, options = {}) {
21312
+ if (!rows.length) {
21313
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21314
+ }
21315
+ const executor = new BulkInsertExecutor(session, table, rows, options);
21316
+ return executor.execute();
21317
+ }
21318
+
21319
+ // src/bulk/bulk-update-executor.ts
21320
+ function resolveByColumns(table, by) {
21321
+ if (!by) return [findPrimaryKey(table)];
21322
+ return Array.isArray(by) ? by : [by];
21323
+ }
21324
+ var BulkUpdateExecutor = class extends BulkBaseExecutor {
21325
+ byColumns;
21326
+ constructor(session, table, rows, options = {}) {
21327
+ super(session, table, rows, options);
21328
+ this.byColumns = resolveByColumns(table, options.by);
21329
+ }
21330
+ async executeChunk(chunk, chunkIndex) {
21331
+ const allReturning = [];
21332
+ const returningColumns = resolveReturningColumns(this.ctx, this.table, this.options.returning);
21333
+ const extraWhere = this.options.where;
21334
+ for (const row of chunk) {
21335
+ const predicates = this.byColumns.map((colName) => {
21336
+ const col2 = this.table.columns[colName];
21337
+ if (!col2) {
21338
+ throw new Error(
21339
+ `bulkUpdate: column "${colName}" not found in table "${this.table.name}"`
21340
+ );
21341
+ }
21342
+ const val = row[colName];
21343
+ if (val === void 0) {
21344
+ throw new Error(
21345
+ `bulkUpdate: row is missing the identity column "${colName}" required by the "by" option`
21346
+ );
21347
+ }
21348
+ return eq(col2, val);
21349
+ });
21350
+ const whereExpr = predicates.length === 1 ? predicates[0] : predicates.reduce((acc, p) => and(acc, p), predicates[0]);
21351
+ const finalWhere = extraWhere ? and(whereExpr, extraWhere) : whereExpr;
21352
+ const bySet = new Set(this.byColumns);
21353
+ const setPayload = {};
21354
+ for (const [key, val] of Object.entries(row)) {
21355
+ if (!bySet.has(key) && key in this.table.columns) {
21356
+ setPayload[key] = val;
21357
+ }
21358
+ }
21359
+ if (!Object.keys(setPayload).length) continue;
21360
+ let builder = new UpdateQueryBuilder(this.table).set(setPayload).where(finalWhere);
21361
+ if (returningColumns?.length) {
21362
+ builder = builder.returning(...returningColumns);
21363
+ }
21364
+ const compiled = builder.compile(this.ctx.dialect);
21365
+ const resultSets = await executeCompiled(this.ctx, compiled);
21366
+ if (returningColumns) {
21367
+ allReturning.push(...flattenQueryResults(resultSets));
21368
+ }
21369
+ }
21370
+ return {
21371
+ processedRows: chunk.length,
21372
+ returning: allReturning,
21373
+ elapsedMs: 0
21374
+ };
21375
+ }
21376
+ };
21377
+ async function bulkUpdate(session, table, rows, options = {}) {
21378
+ if (!rows.length) {
21379
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21380
+ }
21381
+ const executor = new BulkUpdateExecutor(session, table, rows, options);
21382
+ return executor.execute();
21383
+ }
21384
+ var DEFAULT_BULK_UPDATE_WHERE_CHUNK_SIZE = 500;
21385
+ async function bulkUpdateWhere(session, table, ids, set, options = {}) {
21386
+ if (!ids.length) {
21387
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21388
+ }
21389
+ const {
21390
+ chunkSize = DEFAULT_BULK_UPDATE_WHERE_CHUNK_SIZE,
21391
+ concurrency = "sequential",
21392
+ transactional = true,
21393
+ timing = false,
21394
+ onChunkComplete,
21395
+ by,
21396
+ where: extraWhere,
21397
+ returning
21398
+ } = options;
21399
+ const ctx = createBulkExecutionContext(session);
21400
+ const byColumnName = by ?? findPrimaryKey(table);
21401
+ const byColumn = table.columns[byColumnName];
21402
+ if (!byColumn) {
21403
+ throw new Error(
21404
+ `bulkUpdateWhere: column "${byColumnName}" not found in table "${table.name}"`
21405
+ );
21406
+ }
21407
+ const returningColumns = resolveReturningColumns(ctx, table, returning);
21408
+ const chunks = splitIntoChunks(ids, chunkSize);
21409
+ const totalChunks = chunks.length;
21410
+ const buildTask = (chunk, chunkIndex) => async () => {
21411
+ return runChunk(
21412
+ async () => {
21413
+ const inExpr = inList(byColumn, chunk);
21414
+ const finalWhere = extraWhere ? and(inExpr, extraWhere) : inExpr;
21415
+ let builder = new UpdateQueryBuilder(table).set(set).where(finalWhere);
21416
+ if (returningColumns?.length) {
21417
+ builder = builder.returning(...returningColumns);
21418
+ }
21419
+ const compiled = builder.compile(ctx.dialect);
21420
+ const resultSets = await executeCompiled(ctx, compiled);
21421
+ return {
21422
+ processedRows: chunk.length,
21423
+ returning: returningColumns ? flattenQueryResults(resultSets) : [],
21424
+ elapsedMs: 0
21425
+ };
21426
+ },
21427
+ chunkIndex,
21428
+ totalChunks,
21429
+ chunk.length,
21430
+ timing,
21431
+ onChunkComplete
21432
+ );
21433
+ };
21434
+ const tasks = chunks.map((chunk, i) => buildTask(chunk, i));
21435
+ const outcomes = await maybeTransaction(
21436
+ session,
21437
+ transactional,
21438
+ () => runWithConcurrency(tasks, concurrency)
21439
+ );
21440
+ return timing ? aggregateOutcomesWithTimings(outcomes) : aggregateOutcomes(outcomes);
21441
+ }
21442
+
21443
+ // src/bulk/bulk-delete-executor.ts
21444
+ var BulkDeleteExecutor = class extends BulkBaseExecutor {
21445
+ byColumnName;
21446
+ constructor(session, table, ids, options = {}) {
21447
+ super(session, table, ids, options);
21448
+ this.byColumnName = options.by ?? findPrimaryKey(table);
21449
+ }
21450
+ async executeChunk(chunk, chunkIndex) {
21451
+ const byColumn = this.table.columns[this.byColumnName];
21452
+ if (!byColumn) {
21453
+ throw new Error(
21454
+ `bulkDelete: column "${this.byColumnName}" not found in table "${this.table.name}"`
21455
+ );
21456
+ }
21457
+ const extraWhere = this.options.where;
21458
+ const inExpr = inList(byColumn, chunk);
21459
+ const finalWhere = extraWhere ? and(inExpr, extraWhere) : inExpr;
21460
+ const builder = new DeleteQueryBuilder(this.table).where(finalWhere);
21461
+ const compiled = builder.compile(this.ctx.dialect);
21462
+ await executeCompiled(this.ctx, compiled);
21463
+ return {
21464
+ processedRows: chunk.length,
21465
+ returning: [],
21466
+ elapsedMs: 0
21467
+ };
21468
+ }
21469
+ };
21470
+ async function bulkDelete(session, table, ids, options = {}) {
21471
+ if (!ids.length) {
21472
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21473
+ }
21474
+ const executor = new BulkDeleteExecutor(session, table, ids, options);
21475
+ return executor.execute();
21476
+ }
21477
+ async function bulkDeleteWhere(session, table, where, options = {}) {
21478
+ const { transactional = false } = options;
21479
+ const ctx = createBulkExecutionContext(session);
21480
+ const builder = new DeleteQueryBuilder(table).where(where);
21481
+ const compiled = builder.compile(ctx.dialect);
21482
+ const execute = async () => {
21483
+ await executeCompiled(ctx, compiled);
21484
+ return { processedRows: 0, chunksExecuted: 1, returning: [] };
21485
+ };
21486
+ return maybeTransaction(session, transactional, execute);
21487
+ }
21488
+
21489
+ // src/bulk/bulk-upsert-executor.ts
21490
+ var DEFAULT_CHUNK_SIZE2 = 500;
21491
+ var BulkUpsertExecutor = class extends BulkBaseExecutor {
21492
+ conflictTargetNodes;
21493
+ updateColumns;
21494
+ constructor(session, table, rows, options = {}) {
21495
+ super(session, table, rows, { ...options, chunkSize: options.chunkSize ?? DEFAULT_CHUNK_SIZE2 });
21496
+ const pkName = findPrimaryKey(table);
21497
+ const conflictTargetNames = options.conflictColumns ?? [pkName];
21498
+ this.conflictTargetNodes = conflictTargetNames.map((name) => ({
21499
+ type: "Column",
21500
+ table: table.name,
21501
+ name
21502
+ }));
21503
+ const conflictSet = new Set(conflictTargetNames);
21504
+ this.updateColumns = options.updateColumns ?? Object.keys(rows[0] ?? {}).filter((col2) => !conflictSet.has(col2) && col2 in table.columns);
21505
+ }
21506
+ async executeChunk(chunk, chunkIndex) {
21507
+ const returningColumns = resolveReturningColumns(this.ctx, this.table, this.options.returning);
21508
+ const set = {};
21509
+ for (const col2 of this.updateColumns) {
21510
+ set[col2] = { type: "ExcludedColumn", name: col2 };
21511
+ }
21512
+ let builder;
21513
+ if (this.updateColumns.length === 0) {
21514
+ builder = new InsertQueryBuilder(this.table).values(chunk).onConflict(this.conflictTargetNodes).doNothing();
21515
+ } else {
21516
+ builder = new InsertQueryBuilder(this.table).values(chunk).onConflict(this.conflictTargetNodes).doUpdate(set);
21517
+ }
21518
+ const finalBuilder = builder;
21519
+ if (returningColumns?.length) {
21520
+ finalBuilder.returning(...returningColumns);
21521
+ }
21522
+ const compiled = finalBuilder.compile(this.ctx.dialect);
21523
+ const resultSets = await executeCompiled(this.ctx, compiled);
21524
+ return {
21525
+ processedRows: chunk.length,
21526
+ returning: returningColumns ? flattenQueryResults(resultSets) : [],
21527
+ elapsedMs: 0
21528
+ };
21529
+ }
21530
+ };
21531
+ async function bulkUpsert(session, table, rows, options = {}) {
21532
+ if (!rows.length) {
21533
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21534
+ }
21535
+ const executor = new BulkUpsertExecutor(session, table, rows, options);
21536
+ return executor.execute();
21537
+ }
19686
21538
  export {
19687
21539
  Alphanumeric,
19688
21540
  AsyncLocalStorage,
@@ -19691,6 +21543,10 @@ export {
19691
21543
  BigIntTypeStrategy,
19692
21544
  BinaryTypeStrategy,
19693
21545
  BooleanTypeStrategy,
21546
+ BulkDeleteExecutor,
21547
+ BulkInsertExecutor,
21548
+ BulkUpdateExecutor,
21549
+ BulkUpsertExecutor,
19694
21550
  CEP,
19695
21551
  CNPJ,
19696
21552
  CPF,
@@ -19706,6 +21562,9 @@ export {
19706
21562
  DefaultEntityMaterializer,
19707
21563
  DefaultHasManyCollection,
19708
21564
  DefaultManyToManyCollection,
21565
+ DefaultMorphManyCollection,
21566
+ DefaultMorphOneReference,
21567
+ DefaultMorphToReference,
19709
21568
  DefaultTypeStrategy,
19710
21569
  DeleteQueryBuilder,
19711
21570
  DomainEventBus,
@@ -19721,6 +21580,9 @@ export {
19721
21580
  Length,
19722
21581
  Lower,
19723
21582
  MemoryCacheAdapter,
21583
+ MorphMany,
21584
+ MorphOne,
21585
+ MorphTo,
19724
21586
  MySqlDialect,
19725
21587
  NestedSetStrategy,
19726
21588
  Orm,
@@ -19779,6 +21641,12 @@ export {
19779
21641
  bootstrapEntities,
19780
21642
  buildFilterExpression,
19781
21643
  buildScopeConditions,
21644
+ bulkDelete,
21645
+ bulkDeleteWhere,
21646
+ bulkInsert,
21647
+ bulkUpdate,
21648
+ bulkUpdateWhere,
21649
+ bulkUpsert,
19782
21650
  calculateRowDepths,
19783
21651
  calculateTotalPages,
19784
21652
  callProcedure,
@@ -19811,6 +21679,7 @@ export {
19811
21679
  count,
19812
21680
  countAll,
19813
21681
  createApiComponentsSection,
21682
+ createBetterSqlite3Executor,
19814
21683
  createDeterministicNamingState,
19815
21684
  createDtoToOpenApiSchema,
19816
21685
  createEntityFromRow,
@@ -19912,12 +21781,16 @@ export {
19912
21781
  isCastExpressionNode,
19913
21782
  isCollateExpressionNode,
19914
21783
  isComponentReference,
21784
+ isDistinctFrom,
19915
21785
  isExpressionSelectionNode,
19916
21786
  isFunctionNode,
21787
+ isMorphRelation,
21788
+ isNotDistinctFrom,
19917
21789
  isNotNull,
19918
21790
  isNull,
19919
21791
  isNullableColumn,
19920
21792
  isOperandNode,
21793
+ isSingleTargetRelation,
19921
21794
  isTableDef2 as isTableDef,
19922
21795
  isTreeConfig,
19923
21796
  isValidDuration,
@@ -19941,6 +21814,9 @@ export {
19941
21814
  loadBelongsToRelation,
19942
21815
  loadHasManyRelation,
19943
21816
  loadHasOneRelation,
21817
+ loadMorphManyRelation,
21818
+ loadMorphOneRelation,
21819
+ loadMorphToRelation,
19944
21820
  localTime,
19945
21821
  localTimestamp,
19946
21822
  locate,
@@ -19962,11 +21838,15 @@ export {
19962
21838
  minute,
19963
21839
  mod,
19964
21840
  month,
21841
+ morphMany,
21842
+ morphOne,
21843
+ morphTo,
19965
21844
  mul,
19966
21845
  neq,
19967
21846
  nestedDtoToOpenApiSchema,
19968
21847
  nestedWhereInputToOpenApiSchema,
19969
21848
  normalizeColumnType,
21849
+ not,
19970
21850
  notBetween,
19971
21851
  notExists,
19972
21852
  notInList,