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.cjs CHANGED
@@ -50,6 +50,10 @@ __export(index_exports, {
50
50
  BigIntTypeStrategy: () => BigIntTypeStrategy,
51
51
  BinaryTypeStrategy: () => BinaryTypeStrategy,
52
52
  BooleanTypeStrategy: () => BooleanTypeStrategy,
53
+ BulkDeleteExecutor: () => BulkDeleteExecutor,
54
+ BulkInsertExecutor: () => BulkInsertExecutor,
55
+ BulkUpdateExecutor: () => BulkUpdateExecutor,
56
+ BulkUpsertExecutor: () => BulkUpsertExecutor,
53
57
  CEP: () => CEP,
54
58
  CNPJ: () => CNPJ,
55
59
  CPF: () => CPF,
@@ -65,6 +69,9 @@ __export(index_exports, {
65
69
  DefaultEntityMaterializer: () => DefaultEntityMaterializer,
66
70
  DefaultHasManyCollection: () => DefaultHasManyCollection,
67
71
  DefaultManyToManyCollection: () => DefaultManyToManyCollection,
72
+ DefaultMorphManyCollection: () => DefaultMorphManyCollection,
73
+ DefaultMorphOneReference: () => DefaultMorphOneReference,
74
+ DefaultMorphToReference: () => DefaultMorphToReference,
68
75
  DefaultTypeStrategy: () => DefaultTypeStrategy,
69
76
  DeleteQueryBuilder: () => DeleteQueryBuilder,
70
77
  DomainEventBus: () => DomainEventBus,
@@ -80,6 +87,9 @@ __export(index_exports, {
80
87
  Length: () => Length,
81
88
  Lower: () => Lower,
82
89
  MemoryCacheAdapter: () => MemoryCacheAdapter,
90
+ MorphMany: () => MorphMany,
91
+ MorphOne: () => MorphOne,
92
+ MorphTo: () => MorphTo,
83
93
  MySqlDialect: () => MySqlDialect,
84
94
  NestedSetStrategy: () => NestedSetStrategy,
85
95
  Orm: () => Orm,
@@ -138,6 +148,12 @@ __export(index_exports, {
138
148
  bootstrapEntities: () => bootstrapEntities,
139
149
  buildFilterExpression: () => buildFilterExpression,
140
150
  buildScopeConditions: () => buildScopeConditions,
151
+ bulkDelete: () => bulkDelete,
152
+ bulkDeleteWhere: () => bulkDeleteWhere,
153
+ bulkInsert: () => bulkInsert,
154
+ bulkUpdate: () => bulkUpdate,
155
+ bulkUpdateWhere: () => bulkUpdateWhere,
156
+ bulkUpsert: () => bulkUpsert,
141
157
  calculateRowDepths: () => calculateRowDepths,
142
158
  calculateTotalPages: () => calculateTotalPages,
143
159
  callProcedure: () => callProcedure,
@@ -170,6 +186,7 @@ __export(index_exports, {
170
186
  count: () => count,
171
187
  countAll: () => countAll,
172
188
  createApiComponentsSection: () => createApiComponentsSection,
189
+ createBetterSqlite3Executor: () => createBetterSqlite3Executor,
173
190
  createDeterministicNamingState: () => createDeterministicNamingState,
174
191
  createDtoToOpenApiSchema: () => createDtoToOpenApiSchema,
175
192
  createEntityFromRow: () => createEntityFromRow,
@@ -271,12 +288,16 @@ __export(index_exports, {
271
288
  isCastExpressionNode: () => isCastExpressionNode,
272
289
  isCollateExpressionNode: () => isCollateExpressionNode,
273
290
  isComponentReference: () => isComponentReference,
291
+ isDistinctFrom: () => isDistinctFrom,
274
292
  isExpressionSelectionNode: () => isExpressionSelectionNode,
275
293
  isFunctionNode: () => isFunctionNode,
294
+ isMorphRelation: () => isMorphRelation,
295
+ isNotDistinctFrom: () => isNotDistinctFrom,
276
296
  isNotNull: () => isNotNull,
277
297
  isNull: () => isNull,
278
298
  isNullableColumn: () => isNullableColumn,
279
299
  isOperandNode: () => isOperandNode,
300
+ isSingleTargetRelation: () => isSingleTargetRelation,
280
301
  isTableDef: () => isTableDef2,
281
302
  isTreeConfig: () => isTreeConfig,
282
303
  isValidDuration: () => isValidDuration,
@@ -300,6 +321,9 @@ __export(index_exports, {
300
321
  loadBelongsToRelation: () => loadBelongsToRelation,
301
322
  loadHasManyRelation: () => loadHasManyRelation,
302
323
  loadHasOneRelation: () => loadHasOneRelation,
324
+ loadMorphManyRelation: () => loadMorphManyRelation,
325
+ loadMorphOneRelation: () => loadMorphOneRelation,
326
+ loadMorphToRelation: () => loadMorphToRelation,
303
327
  localTime: () => localTime,
304
328
  localTimestamp: () => localTimestamp,
305
329
  locate: () => locate,
@@ -321,11 +345,15 @@ __export(index_exports, {
321
345
  minute: () => minute,
322
346
  mod: () => mod,
323
347
  month: () => month,
348
+ morphMany: () => morphMany,
349
+ morphOne: () => morphOne,
350
+ morphTo: () => morphTo,
324
351
  mul: () => mul,
325
352
  neq: () => neq,
326
353
  nestedDtoToOpenApiSchema: () => nestedDtoToOpenApiSchema,
327
354
  nestedWhereInputToOpenApiSchema: () => nestedWhereInputToOpenApiSchema,
328
355
  normalizeColumnType: () => normalizeColumnType,
356
+ not: () => not,
329
357
  notBetween: () => notBetween,
330
358
  notExists: () => notExists,
331
359
  notInList: () => notInList,
@@ -732,7 +760,13 @@ var RelationKinds = {
732
760
  /** Many-to-one relationship */
733
761
  BelongsTo: "BELONGS_TO",
734
762
  /** Many-to-many relationship with pivot metadata */
735
- BelongsToMany: "BELONGS_TO_MANY"
763
+ BelongsToMany: "BELONGS_TO_MANY",
764
+ /** Polymorphic inverse (child side) */
765
+ MorphTo: "MORPH_TO",
766
+ /** Polymorphic one-to-one (parent side) */
767
+ MorphOne: "MORPH_ONE",
768
+ /** Polymorphic one-to-many (parent side) */
769
+ MorphMany: "MORPH_MANY"
736
770
  };
737
771
  var hasMany = (target, foreignKey, localKey, cascade) => ({
738
772
  type: RelationKinds.HasMany,
@@ -767,6 +801,32 @@ var belongsToMany = (target, pivotTable, options) => ({
767
801
  defaultPivotColumns: options.defaultPivotColumns,
768
802
  cascade: options.cascade
769
803
  });
804
+ var isSingleTargetRelation = (rel) => rel.type !== RelationKinds.MorphTo;
805
+ var isMorphRelation = (rel) => rel.type === RelationKinds.MorphTo || rel.type === RelationKinds.MorphOne || rel.type === RelationKinds.MorphMany;
806
+ var morphTo = (opts) => ({
807
+ type: RelationKinds.MorphTo,
808
+ ...opts
809
+ });
810
+ var morphOne = (target, opts) => ({
811
+ type: RelationKinds.MorphOne,
812
+ target,
813
+ morphName: opts.as,
814
+ typeField: opts.typeField ?? `${opts.as}Type`,
815
+ idField: opts.idField ?? `${opts.as}Id`,
816
+ typeValue: opts.typeValue,
817
+ localKey: opts.localKey,
818
+ cascade: opts.cascade
819
+ });
820
+ var morphMany = (target, opts) => ({
821
+ type: RelationKinds.MorphMany,
822
+ target,
823
+ morphName: opts.as,
824
+ typeField: opts.typeField ?? `${opts.as}Type`,
825
+ idField: opts.idField ?? `${opts.as}Id`,
826
+ typeValue: opts.typeValue,
827
+ localKey: opts.localKey,
828
+ cascade: opts.cascade
829
+ });
770
830
 
771
831
  // src/core/ast/expression-nodes.ts
772
832
  var operandTypes = /* @__PURE__ */ new Set([
@@ -894,6 +954,10 @@ var or = (...operands) => ({
894
954
  operator: "OR",
895
955
  operands
896
956
  });
957
+ var not = (operand) => ({
958
+ type: "NotExpression",
959
+ operand
960
+ });
897
961
  var isNull = (left2) => ({
898
962
  type: "NullExpression",
899
963
  left: toOperandNode(left2),
@@ -977,6 +1041,18 @@ var collate = (expression, collation) => ({
977
1041
  expression: toOperand(expression),
978
1042
  collation
979
1043
  });
1044
+ var isDistinctFrom = (left2, right2) => ({
1045
+ type: "IsDistinctExpression",
1046
+ left: toOperandNode(left2),
1047
+ operator: "IS DISTINCT FROM",
1048
+ right: toOperand(right2)
1049
+ });
1050
+ var isNotDistinctFrom = (left2, right2) => ({
1051
+ type: "IsDistinctExpression",
1052
+ left: toOperandNode(left2),
1053
+ operator: "IS NOT DISTINCT FROM",
1054
+ right: toOperand(right2)
1055
+ });
980
1056
 
981
1057
  // src/core/ast/window-functions.ts
982
1058
  var buildWindowFunction = (name, args = [], partitionBy, orderBy) => {
@@ -1183,6 +1259,9 @@ var visitExpression = (node, visitor) => {
1183
1259
  case "LogicalExpression":
1184
1260
  if (visitor.visitLogicalExpression) return visitor.visitLogicalExpression(node);
1185
1261
  break;
1262
+ case "NotExpression":
1263
+ if (visitor.visitNotExpression) return visitor.visitNotExpression(node);
1264
+ break;
1186
1265
  case "NullExpression":
1187
1266
  if (visitor.visitNullExpression) return visitor.visitNullExpression(node);
1188
1267
  break;
@@ -1201,6 +1280,9 @@ var visitExpression = (node, visitor) => {
1201
1280
  case "BitwiseExpression":
1202
1281
  if (visitor.visitBitwiseExpression) return visitor.visitBitwiseExpression(node);
1203
1282
  break;
1283
+ case "IsDistinctExpression":
1284
+ if (visitor.visitIsDistinctExpression) return visitor.visitIsDistinctExpression(node);
1285
+ break;
1204
1286
  default:
1205
1287
  break;
1206
1288
  }
@@ -1924,6 +2006,10 @@ var Dialect = class _Dialect {
1924
2006
  });
1925
2007
  return parts.join(` ${logical.operator} `);
1926
2008
  });
2009
+ this.registerExpressionCompiler("NotExpression", (notExpr, ctx) => {
2010
+ const operand = this.compileExpression(notExpr.operand, ctx);
2011
+ return `NOT (${operand})`;
2012
+ });
1927
2013
  this.registerExpressionCompiler("NullExpression", (nullExpr, ctx) => {
1928
2014
  const left2 = this.compileOperand(nullExpr.left, ctx);
1929
2015
  return `${left2} ${nullExpr.operator}`;
@@ -1957,6 +2043,11 @@ var Dialect = class _Dialect {
1957
2043
  const right2 = this.compileOperand(bitwise.right, ctx);
1958
2044
  return `${left2} ${bitwise.operator} ${right2}`;
1959
2045
  });
2046
+ this.registerExpressionCompiler("IsDistinctExpression", (node, ctx) => {
2047
+ const left2 = this.compileOperand(node.left, ctx);
2048
+ const right2 = this.compileOperand(node.right, ctx);
2049
+ return `${left2} ${node.operator} ${right2}`;
2050
+ });
1960
2051
  }
1961
2052
  registerDefaultOperandCompilers() {
1962
2053
  this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
@@ -2878,6 +2969,18 @@ var MySqlDialect = class extends SqlDialectBase {
2878
2969
  */
2879
2970
  constructor() {
2880
2971
  super(new MysqlFunctionStrategy());
2972
+ this.registerExpressionCompiler(
2973
+ "IsDistinctExpression",
2974
+ (node, ctx) => {
2975
+ const left2 = this.compileOperand(node.left, ctx);
2976
+ const right2 = this.compileOperand(node.right, ctx);
2977
+ const spaceship = `${left2} <=> ${right2}`;
2978
+ if (node.operator === "IS NOT DISTINCT FROM") {
2979
+ return spaceship;
2980
+ }
2981
+ return `NOT (${spaceship})`;
2982
+ }
2983
+ );
2881
2984
  }
2882
2985
  /**
2883
2986
  * Quotes an identifier using MySQL backtick syntax
@@ -3977,7 +4080,7 @@ var HydrationManager = class _HydrationManager {
3977
4080
  */
3978
4081
  hasMultiplyingRelations(plan) {
3979
4082
  return plan.relations.some(
3980
- (rel) => rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany
4083
+ (rel) => rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany || rel.type === RelationKinds.MorphMany
3981
4084
  );
3982
4085
  }
3983
4086
  /**
@@ -4311,6 +4414,36 @@ var HydrationPlanner = class _HydrationPlanner {
4311
4414
  }
4312
4415
  };
4313
4416
  }
4417
+ case RelationKinds.MorphOne: {
4418
+ const morphRel = rel;
4419
+ const localKey = morphRel.localKey || findPrimaryKey(this.table);
4420
+ return {
4421
+ name: relationName,
4422
+ aliasPrefix,
4423
+ type: rel.type,
4424
+ targetTable: morphRel.target.name,
4425
+ targetPrimaryKey: findPrimaryKey(morphRel.target),
4426
+ foreignKey: morphRel.idField,
4427
+ localKey,
4428
+ columns
4429
+ };
4430
+ }
4431
+ case RelationKinds.MorphMany: {
4432
+ const morphRel = rel;
4433
+ const localKey = morphRel.localKey || findPrimaryKey(this.table);
4434
+ return {
4435
+ name: relationName,
4436
+ aliasPrefix,
4437
+ type: rel.type,
4438
+ targetTable: morphRel.target.name,
4439
+ targetPrimaryKey: findPrimaryKey(morphRel.target),
4440
+ foreignKey: morphRel.idField,
4441
+ localKey,
4442
+ columns
4443
+ };
4444
+ }
4445
+ case RelationKinds.MorphTo:
4446
+ throw new Error("MorphTo relations do not support hydration planning via JOIN.");
4314
4447
  }
4315
4448
  }
4316
4449
  };
@@ -4615,23 +4748,44 @@ var assertNever = (value) => {
4615
4748
  };
4616
4749
  var baseRelationCondition = (root, relation, rootAlias, targetTableName) => {
4617
4750
  const rootTable = rootAlias || root.name;
4751
+ if (relation.type === RelationKinds.MorphTo) {
4752
+ throw new Error("MorphTo relations do not support the standard join condition builder");
4753
+ }
4618
4754
  const targetTable = targetTableName ?? relation.target.name;
4619
- const defaultLocalKey = relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne ? findPrimaryKey(root) : findPrimaryKey(relation.target);
4620
- const localKey = relation.localKey || defaultLocalKey;
4621
4755
  switch (relation.type) {
4622
4756
  case RelationKinds.HasMany:
4623
- case RelationKinds.HasOne:
4757
+ case RelationKinds.HasOne: {
4758
+ const defaultLocalKey = findPrimaryKey(root);
4759
+ const localKey = relation.localKey || defaultLocalKey;
4624
4760
  return eq(
4625
4761
  { type: "Column", table: targetTable, name: relation.foreignKey },
4626
4762
  { type: "Column", table: rootTable, name: localKey }
4627
4763
  );
4628
- case RelationKinds.BelongsTo:
4764
+ }
4765
+ case RelationKinds.BelongsTo: {
4766
+ const defaultLocalKey = findPrimaryKey(relation.target);
4767
+ const localKey = relation.localKey || defaultLocalKey;
4629
4768
  return eq(
4630
4769
  { type: "Column", table: targetTable, name: localKey },
4631
4770
  { type: "Column", table: rootTable, name: relation.foreignKey }
4632
4771
  );
4772
+ }
4633
4773
  case RelationKinds.BelongsToMany:
4634
4774
  throw new Error("BelongsToMany relations do not support the standard join condition builder");
4775
+ case RelationKinds.MorphOne:
4776
+ case RelationKinds.MorphMany: {
4777
+ const morphRel = relation;
4778
+ const morphLocalKey = morphRel.localKey || findPrimaryKey(root);
4779
+ const baseCondition = eq(
4780
+ { type: "Column", table: targetTable, name: morphRel.idField },
4781
+ { type: "Column", table: rootTable, name: morphLocalKey }
4782
+ );
4783
+ const discriminatorCondition = eq(
4784
+ { type: "Column", table: targetTable, name: morphRel.typeField },
4785
+ { type: "Literal", value: morphRel.typeValue }
4786
+ );
4787
+ return and(baseCondition, discriminatorCondition);
4788
+ }
4635
4789
  default:
4636
4790
  return assertNever(relation);
4637
4791
  }
@@ -4727,6 +4881,9 @@ var collectFromExpression = (expr, collector) => {
4727
4881
  case "LogicalExpression":
4728
4882
  expr.operands.forEach((operand) => collectFromExpression(operand, collector));
4729
4883
  break;
4884
+ case "NotExpression":
4885
+ collectFromExpression(expr.operand, collector);
4886
+ break;
4730
4887
  case "NullExpression":
4731
4888
  collectFromOperand(expr.left, collector);
4732
4889
  break;
@@ -4833,6 +4990,11 @@ var mapExpression = (expr, fromTable, toTable) => {
4833
4990
  if (nextOperands.every((op, i) => op === expr.operands[i])) return expr;
4834
4991
  return { ...expr, operands: nextOperands };
4835
4992
  }
4993
+ case "NotExpression": {
4994
+ const operand = mapExpression(expr.operand, fromTable, toTable);
4995
+ if (operand === expr.operand) return expr;
4996
+ return { ...expr, operand };
4997
+ }
4836
4998
  case "NullExpression": {
4837
4999
  const left2 = mapOperand(expr.left, fromTable, toTable);
4838
5000
  if (left2 === expr.left) return expr;
@@ -5133,6 +5295,9 @@ var addRelationJoin = (params) => {
5133
5295
  );
5134
5296
  return joins.reduce((curr, join) => curr.withJoin(join), state);
5135
5297
  }
5298
+ if (!isSingleTargetRelation(relation)) {
5299
+ throw new Error("Polymorphic MorphTo relations do not support join-based strategies");
5300
+ }
5136
5301
  let targetSource = tableSource ?? {
5137
5302
  type: "Table",
5138
5303
  name: relation.target.name,
@@ -5148,6 +5313,9 @@ var addRelationJoin = (params) => {
5148
5313
  var updateRelationJoin = (params) => {
5149
5314
  const { joins, joinIndex, relation, currentTable, currentAlias, options } = params;
5150
5315
  const join = joins[joinIndex];
5316
+ if (!isSingleTargetRelation(relation)) {
5317
+ throw new Error("Polymorphic MorphTo relations do not support join updates");
5318
+ }
5151
5319
  const targetName = resolveTargetTableName(join.table, relation.target.name);
5152
5320
  const extra = remapExpressionTable(options.filter, relation.target.name, targetName);
5153
5321
  if (relation.type === RelationKinds.BelongsToMany) {
@@ -5207,6 +5375,9 @@ var RelationCteBuilder = class {
5207
5375
  if (!predicate) {
5208
5376
  throw new Error("Unable to build filter CTE without predicates.");
5209
5377
  }
5378
+ if (!isSingleTargetRelation(relation)) {
5379
+ throw new Error("Polymorphic MorphTo relations do not support filter CTEs");
5380
+ }
5210
5381
  const columns = Object.keys(relation.target.columns).map((name) => ({
5211
5382
  type: "Column",
5212
5383
  table: relation.target.name,
@@ -5254,6 +5425,9 @@ var buildTypedSelection = (columns, prefix, keys, missingMsg, tableOverride) =>
5254
5425
  }, {});
5255
5426
  };
5256
5427
  var resolveTargetColumns = (relation, options) => {
5428
+ if (!isSingleTargetRelation(relation)) {
5429
+ return [];
5430
+ }
5257
5431
  const requestedColumns = options?.columns?.length ? [...options.columns] : Object.keys(relation.target.columns);
5258
5432
  const targetPrimaryKey = findPrimaryKey(relation.target);
5259
5433
  if (!requestedColumns.includes(targetPrimaryKey)) {
@@ -5345,11 +5519,41 @@ var belongsToManyStrategy = (context) => {
5345
5519
  );
5346
5520
  return { state, hydration };
5347
5521
  };
5522
+ var morphIncludeStrategy = (context) => {
5523
+ let { state, hydration } = context;
5524
+ const relation = context.relation;
5525
+ const targetColumns = resolveTargetColumns(relation, context.options);
5526
+ const tableOverride = getJoinCorrelationName(state, context.relationName, relation.target.name);
5527
+ const targetSelection = buildTypedSelection(
5528
+ relation.target.columns,
5529
+ context.aliasPrefix,
5530
+ targetColumns,
5531
+ (key) => `Column '${key}' not found on relation '${context.relationName}'`,
5532
+ tableOverride
5533
+ );
5534
+ const relationSelectionResult = context.selectColumns(state, hydration, targetSelection);
5535
+ state = relationSelectionResult.state;
5536
+ hydration = relationSelectionResult.hydration;
5537
+ hydration = hydration.onRelationIncluded(
5538
+ state,
5539
+ relation,
5540
+ context.relationName,
5541
+ context.aliasPrefix,
5542
+ targetColumns
5543
+ );
5544
+ return { state, hydration };
5545
+ };
5546
+ var morphToIncludeStrategy = () => {
5547
+ throw new Error("MorphTo relations do not support JOIN-based include. Use lazy loading instead.");
5548
+ };
5348
5549
  var relationIncludeStrategies = {
5349
5550
  [RelationKinds.HasMany]: standardIncludeStrategy,
5350
5551
  [RelationKinds.HasOne]: standardIncludeStrategy,
5351
5552
  [RelationKinds.BelongsTo]: standardIncludeStrategy,
5352
- [RelationKinds.BelongsToMany]: belongsToManyStrategy
5553
+ [RelationKinds.BelongsToMany]: belongsToManyStrategy,
5554
+ [RelationKinds.MorphOne]: morphIncludeStrategy,
5555
+ [RelationKinds.MorphMany]: morphIncludeStrategy,
5556
+ [RelationKinds.MorphTo]: morphToIncludeStrategy
5353
5557
  };
5354
5558
 
5355
5559
  // src/query-builder/relation-service.ts
@@ -5418,6 +5622,12 @@ var RelationService = class {
5418
5622
  let state = this.state;
5419
5623
  let hydration = this.hydration;
5420
5624
  const relation = this.getRelation(relationName);
5625
+ if (relation.type === RelationKinds.MorphTo) {
5626
+ throw new Error(`MorphTo relation '${relationName}' does not support include() via JOIN. Use lazy loading ($load) instead.`);
5627
+ }
5628
+ if (!isSingleTargetRelation(relation)) {
5629
+ return { state, hydration };
5630
+ }
5421
5631
  const aliasPrefix = options?.aliasPrefix ?? relationName;
5422
5632
  const alreadyJoined = hasJoinForRelationKey(state.ast.joins, relationName);
5423
5633
  const { selfFilters, crossFilters } = splitFilterExpressions(
@@ -5476,6 +5686,9 @@ var RelationService = class {
5476
5686
  */
5477
5687
  applyRelationCorrelation(relationName, ast, additionalCorrelation) {
5478
5688
  const relation = this.getRelation(relationName);
5689
+ if (relation.type === RelationKinds.MorphTo) {
5690
+ throw new Error(`MorphTo relation '${relationName}' does not support correlation-based operations.`);
5691
+ }
5479
5692
  const rootAlias = this.state.ast.from.type === "Table" ? this.state.ast.from.alias : void 0;
5480
5693
  let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
5481
5694
  if (additionalCorrelation) {
@@ -5935,6 +6148,45 @@ var populateHydrationCache = (entity, row, meta) => {
5935
6148
  }
5936
6149
  }
5937
6150
  }
6151
+ for (const relationName of Object.keys(meta.table.relations)) {
6152
+ const relation = meta.table.relations[relationName];
6153
+ const data = row[relationName];
6154
+ if (relation.type === RelationKinds.MorphOne) {
6155
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
6156
+ const rootValue = entity[localKey];
6157
+ if (rootValue === void 0 || rootValue === null) continue;
6158
+ if (!data || typeof data !== "object") continue;
6159
+ const cache = /* @__PURE__ */ new Map();
6160
+ cache.set(toKey2(rootValue), data);
6161
+ meta.relationHydration.set(relationName, cache);
6162
+ meta.relationCache.set(relationName, Promise.resolve(cache));
6163
+ continue;
6164
+ }
6165
+ if (relation.type === RelationKinds.MorphMany) {
6166
+ if (!Array.isArray(data)) continue;
6167
+ const localKey = relation.localKey || findPrimaryKey(meta.table);
6168
+ const rootValue = entity[localKey];
6169
+ if (rootValue === void 0 || rootValue === null) continue;
6170
+ const cache = /* @__PURE__ */ new Map();
6171
+ cache.set(toKey2(rootValue), data);
6172
+ meta.relationHydration.set(relationName, cache);
6173
+ meta.relationCache.set(relationName, Promise.resolve(cache));
6174
+ continue;
6175
+ }
6176
+ if (relation.type === RelationKinds.MorphTo) {
6177
+ if (!data || typeof data !== "object") continue;
6178
+ const morphTo2 = relation;
6179
+ const typeValue = entity[morphTo2.typeField];
6180
+ const idValue = entity[morphTo2.idField];
6181
+ if (!typeValue || idValue === void 0 || idValue === null) continue;
6182
+ const compositeKey = `${toKey2(typeValue)}:${toKey2(idValue)}`;
6183
+ const cache = /* @__PURE__ */ new Map();
6184
+ cache.set(compositeKey, data);
6185
+ meta.relationHydration.set(relationName, cache);
6186
+ meta.relationCache.set(relationName, Promise.resolve(cache));
6187
+ continue;
6188
+ }
6189
+ }
5938
6190
  };
5939
6191
 
5940
6192
  // src/orm/relations/has-many.ts
@@ -6607,113 +6859,519 @@ var DefaultManyToManyCollection = class {
6607
6859
  }
6608
6860
  };
6609
6861
 
6610
- // src/orm/lazy-batch/shared.ts
6611
- var hasColumns = (columns) => Boolean(columns && columns.length > 0);
6612
- var buildColumnSelection = (table, columns, missingMsg) => {
6613
- return columns.reduce((acc, column) => {
6614
- const def = table.columns[column];
6615
- if (!def) {
6616
- throw new Error(missingMsg(column));
6617
- }
6618
- acc[column] = def;
6619
- return acc;
6620
- }, {});
6621
- };
6622
- var filterRow = (row, columns) => {
6623
- const filtered = {};
6624
- for (const column of columns) {
6625
- if (column in row) {
6626
- filtered[column] = row[column];
6627
- }
6628
- }
6629
- return filtered;
6630
- };
6631
- var filterRows = (rows, columns) => rows.map((row) => filterRow(row, columns));
6632
- var rowsFromResults = (results) => {
6633
- const rows = [];
6634
- for (const result of results) {
6635
- const { columns, values } = result;
6636
- for (const valueRow of values) {
6637
- const row = {};
6638
- columns.forEach((column, idx) => {
6639
- row[column] = valueRow[idx];
6640
- });
6641
- rows.push(row);
6642
- }
6643
- }
6644
- return rows;
6645
- };
6646
- var executeQuery = async (ctx, qb) => {
6647
- const compiled = ctx.dialect.compileSelect(qb.getAST());
6648
- const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
6649
- return rowsFromResults(results);
6650
- };
6862
+ // src/orm/relations/morph-one.ts
6651
6863
  var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
6652
- var collectKeysFromRoots = (roots, key) => {
6653
- const collected = /* @__PURE__ */ new Set();
6654
- for (const tracked of roots) {
6655
- const value = tracked.entity[key];
6656
- if (value !== null && value !== void 0) {
6657
- collected.add(value);
6658
- }
6864
+ var hideInternal5 = (obj, keys) => {
6865
+ for (const key of keys) {
6866
+ Object.defineProperty(obj, key, {
6867
+ value: obj[key],
6868
+ writable: false,
6869
+ configurable: false,
6870
+ enumerable: false
6871
+ });
6659
6872
  }
6660
- return collected;
6661
6873
  };
6662
- var buildInListValues = (keys) => Array.from(keys);
6663
- var fetchRowsForKeys = async (ctx, table, column, keys, selection, filter) => {
6664
- let qb = new SelectQueryBuilder(table).select(selection);
6665
- qb = qb.where(inList(column, buildInListValues(keys)));
6666
- if (filter) {
6667
- qb = qb.where(filter);
6874
+ var hideWritable5 = (obj, keys) => {
6875
+ for (const key of keys) {
6876
+ const value = obj[key];
6877
+ Object.defineProperty(obj, key, {
6878
+ value,
6879
+ writable: true,
6880
+ configurable: true,
6881
+ enumerable: false
6882
+ });
6668
6883
  }
6669
- return executeQuery(ctx, qb);
6670
6884
  };
6671
- var groupRowsByMany = (rows, keyColumn) => {
6672
- const grouped = /* @__PURE__ */ new Map();
6673
- for (const row of rows) {
6674
- const value = row[keyColumn];
6675
- if (value === null || value === void 0) continue;
6676
- const key = toKey7(value);
6677
- const bucket = grouped.get(key) ?? [];
6678
- bucket.push(row);
6679
- grouped.set(key, bucket);
6885
+ var DefaultMorphOneReference = class {
6886
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
6887
+ this.ctx = ctx;
6888
+ this.meta = meta;
6889
+ this.root = root;
6890
+ this.relationName = relationName;
6891
+ this.relation = relation;
6892
+ this.rootTable = rootTable;
6893
+ this.loader = loader;
6894
+ this.createEntity = createEntity;
6895
+ this.localKey = localKey;
6896
+ hideInternal5(this, [
6897
+ "ctx",
6898
+ "meta",
6899
+ "root",
6900
+ "relationName",
6901
+ "relation",
6902
+ "rootTable",
6903
+ "loader",
6904
+ "createEntity",
6905
+ "localKey"
6906
+ ]);
6907
+ hideWritable5(this, ["loaded", "current"]);
6908
+ this.populateFromHydrationCache();
6680
6909
  }
6681
- return grouped;
6682
- };
6683
- var groupRowsByUnique = (rows, keyColumn) => {
6684
- const lookup = /* @__PURE__ */ new Map();
6685
- for (const row of rows) {
6686
- const value = row[keyColumn];
6687
- if (value === null || value === void 0) continue;
6688
- const key = toKey7(value);
6689
- if (!lookup.has(key)) {
6690
- lookup.set(key, row);
6910
+ loaded = false;
6911
+ current = null;
6912
+ async load() {
6913
+ if (this.loaded) return this.current;
6914
+ const map = await this.loader();
6915
+ const keyValue = this.root[this.localKey];
6916
+ if (keyValue === void 0 || keyValue === null) {
6917
+ this.loaded = true;
6918
+ return this.current;
6691
6919
  }
6920
+ const row = map.get(toKey7(keyValue));
6921
+ this.current = row ? this.createEntity(row) : null;
6922
+ this.loaded = true;
6923
+ return this.current;
6692
6924
  }
6693
- return lookup;
6694
- };
6695
-
6696
- // src/orm/lazy-batch/has-many.ts
6697
- var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options) => {
6698
- const localKey = relation.localKey || findPrimaryKey(rootTable);
6699
- const roots = ctx.getEntitiesForTable(rootTable);
6700
- const keys = collectKeysFromRoots(roots, localKey);
6701
- if (!keys.size) {
6702
- return /* @__PURE__ */ new Map();
6703
- }
6704
- const fkColumn = relation.target.columns[relation.foreignKey];
6705
- if (!fkColumn) return /* @__PURE__ */ new Map();
6706
- const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
6707
- const targetPrimaryKey = findPrimaryKey(relation.target);
6708
- const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
6709
- if (!selectedColumns.includes(targetPrimaryKey)) {
6710
- selectedColumns.push(targetPrimaryKey);
6925
+ get() {
6926
+ return this.current;
6711
6927
  }
6712
- const queryColumns = new Set(selectedColumns);
6713
- queryColumns.add(relation.foreignKey);
6714
- const selection = buildColumnSelection(
6715
- relation.target,
6716
- Array.from(queryColumns),
6928
+ set(data) {
6929
+ if (data === null) {
6930
+ return this.detachCurrent();
6931
+ }
6932
+ const entity = hasEntityMeta(data) ? data : this.createEntity(data);
6933
+ if (this.current && this.current !== entity) {
6934
+ this.ctx.registerRelationChange(
6935
+ this.root,
6936
+ this.relationKey,
6937
+ this.rootTable,
6938
+ this.relationName,
6939
+ this.relation,
6940
+ { kind: "remove", entity: this.current }
6941
+ );
6942
+ }
6943
+ this.assignMorphKeys(entity);
6944
+ this.current = entity;
6945
+ this.loaded = true;
6946
+ this.ctx.registerRelationChange(
6947
+ this.root,
6948
+ this.relationKey,
6949
+ this.rootTable,
6950
+ this.relationName,
6951
+ this.relation,
6952
+ { kind: "attach", entity }
6953
+ );
6954
+ return entity;
6955
+ }
6956
+ toJSON() {
6957
+ if (!this.current) return null;
6958
+ const entityWithToJSON = this.current;
6959
+ return typeof entityWithToJSON.toJSON === "function" ? entityWithToJSON.toJSON() : this.current;
6960
+ }
6961
+ detachCurrent() {
6962
+ const previous = this.current;
6963
+ if (!previous) return null;
6964
+ this.current = null;
6965
+ this.loaded = true;
6966
+ this.ctx.registerRelationChange(
6967
+ this.root,
6968
+ this.relationKey,
6969
+ this.rootTable,
6970
+ this.relationName,
6971
+ this.relation,
6972
+ { kind: "remove", entity: previous }
6973
+ );
6974
+ return null;
6975
+ }
6976
+ assignMorphKeys(entity) {
6977
+ const keyValue = this.root[this.localKey];
6978
+ entity[this.relation.idField] = keyValue;
6979
+ entity[this.relation.typeField] = this.relation.typeValue;
6980
+ }
6981
+ get relationKey() {
6982
+ return `${this.rootTable.name}.${this.relationName}`;
6983
+ }
6984
+ populateFromHydrationCache() {
6985
+ const keyValue = this.root[this.localKey];
6986
+ if (keyValue === void 0 || keyValue === null) return;
6987
+ const row = getHydrationRecord(this.meta, this.relationName, keyValue);
6988
+ if (!row) return;
6989
+ this.current = this.createEntity(row);
6990
+ this.loaded = true;
6991
+ }
6992
+ };
6993
+
6994
+ // src/orm/relations/morph-many.ts
6995
+ var toKey8 = (value) => value === null || value === void 0 ? "" : String(value);
6996
+ var hideInternal6 = (obj, keys) => {
6997
+ for (const key of keys) {
6998
+ Object.defineProperty(obj, key, {
6999
+ value: obj[key],
7000
+ writable: false,
7001
+ configurable: false,
7002
+ enumerable: false
7003
+ });
7004
+ }
7005
+ };
7006
+ var hideWritable6 = (obj, keys) => {
7007
+ for (const key of keys) {
7008
+ const value = obj[key];
7009
+ Object.defineProperty(obj, key, {
7010
+ value,
7011
+ writable: true,
7012
+ configurable: true,
7013
+ enumerable: false
7014
+ });
7015
+ }
7016
+ };
7017
+ var DefaultMorphManyCollection = class {
7018
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, localKey) {
7019
+ this.ctx = ctx;
7020
+ this.meta = meta;
7021
+ this.root = root;
7022
+ this.relationName = relationName;
7023
+ this.relation = relation;
7024
+ this.rootTable = rootTable;
7025
+ this.loader = loader;
7026
+ this.createEntity = createEntity;
7027
+ this.localKey = localKey;
7028
+ hideInternal6(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
7029
+ hideWritable6(this, ["loaded", "items", "added", "removed"]);
7030
+ this.hydrateFromCache();
7031
+ }
7032
+ loaded = false;
7033
+ items = [];
7034
+ added = /* @__PURE__ */ new Set();
7035
+ removed = /* @__PURE__ */ new Set();
7036
+ async load() {
7037
+ if (this.loaded) return this.items;
7038
+ const map = await this.loader();
7039
+ const key = toKey8(this.root[this.localKey]);
7040
+ const rows = map.get(key) ?? [];
7041
+ this.items = rows.map((row) => this.createEntity(row));
7042
+ this.loaded = true;
7043
+ return this.items;
7044
+ }
7045
+ getItems() {
7046
+ return this.items;
7047
+ }
7048
+ get length() {
7049
+ return this.items.length;
7050
+ }
7051
+ [Symbol.iterator]() {
7052
+ return this.items[Symbol.iterator]();
7053
+ }
7054
+ add(data) {
7055
+ const keyValue = this.root[this.localKey];
7056
+ const childRow = {
7057
+ ...data,
7058
+ [this.relation.idField]: keyValue,
7059
+ [this.relation.typeField]: this.relation.typeValue
7060
+ };
7061
+ const entity = this.createEntity(childRow);
7062
+ this.added.add(entity);
7063
+ this.items.push(entity);
7064
+ this.ctx.registerRelationChange(
7065
+ this.root,
7066
+ this.relationKey,
7067
+ this.rootTable,
7068
+ this.relationName,
7069
+ this.relation,
7070
+ { kind: "add", entity }
7071
+ );
7072
+ return entity;
7073
+ }
7074
+ attach(entity) {
7075
+ const keyValue = this.root[this.localKey];
7076
+ entity[this.relation.idField] = keyValue;
7077
+ entity[this.relation.typeField] = this.relation.typeValue;
7078
+ this.ctx.markDirty(entity);
7079
+ this.items.push(entity);
7080
+ this.ctx.registerRelationChange(
7081
+ this.root,
7082
+ this.relationKey,
7083
+ this.rootTable,
7084
+ this.relationName,
7085
+ this.relation,
7086
+ { kind: "attach", entity }
7087
+ );
7088
+ }
7089
+ remove(entity) {
7090
+ this.items = this.items.filter((item) => item !== entity);
7091
+ this.removed.add(entity);
7092
+ this.ctx.registerRelationChange(
7093
+ this.root,
7094
+ this.relationKey,
7095
+ this.rootTable,
7096
+ this.relationName,
7097
+ this.relation,
7098
+ { kind: "remove", entity }
7099
+ );
7100
+ }
7101
+ clear() {
7102
+ for (const entity of [...this.items]) {
7103
+ this.remove(entity);
7104
+ }
7105
+ }
7106
+ get relationKey() {
7107
+ return `${this.rootTable.name}.${this.relationName}`;
7108
+ }
7109
+ hydrateFromCache() {
7110
+ const keyValue = this.root[this.localKey];
7111
+ if (keyValue === void 0 || keyValue === null) return;
7112
+ const rows = getHydrationRows(this.meta, this.relationName, keyValue);
7113
+ if (!rows?.length) return;
7114
+ this.items = rows.map((row) => this.createEntity(row));
7115
+ this.loaded = true;
7116
+ }
7117
+ toJSON() {
7118
+ return this.items.map((item) => {
7119
+ const entityWithToJSON = item;
7120
+ return typeof entityWithToJSON.toJSON === "function" ? entityWithToJSON.toJSON() : item;
7121
+ });
7122
+ }
7123
+ };
7124
+
7125
+ // src/orm/relations/morph-to.ts
7126
+ var toKey9 = (value) => value === null || value === void 0 ? "" : String(value);
7127
+ var hideInternal7 = (obj, keys) => {
7128
+ for (const key of keys) {
7129
+ Object.defineProperty(obj, key, {
7130
+ value: obj[key],
7131
+ writable: false,
7132
+ configurable: false,
7133
+ enumerable: false
7134
+ });
7135
+ }
7136
+ };
7137
+ var hideWritable7 = (obj, keys) => {
7138
+ for (const key of keys) {
7139
+ const value = obj[key];
7140
+ Object.defineProperty(obj, key, {
7141
+ value,
7142
+ writable: true,
7143
+ configurable: true,
7144
+ enumerable: false
7145
+ });
7146
+ }
7147
+ };
7148
+ var DefaultMorphToReference = class {
7149
+ constructor(ctx, meta, root, relationName, relation, rootTable, loader, createEntity, resolveTargetTable) {
7150
+ this.ctx = ctx;
7151
+ this.meta = meta;
7152
+ this.root = root;
7153
+ this.relationName = relationName;
7154
+ this.relation = relation;
7155
+ this.rootTable = rootTable;
7156
+ this.loader = loader;
7157
+ this.createEntity = createEntity;
7158
+ this.resolveTargetTable = resolveTargetTable;
7159
+ hideInternal7(this, [
7160
+ "ctx",
7161
+ "meta",
7162
+ "root",
7163
+ "relationName",
7164
+ "relation",
7165
+ "rootTable",
7166
+ "loader",
7167
+ "createEntity",
7168
+ "resolveTargetTable"
7169
+ ]);
7170
+ hideWritable7(this, ["loaded", "current"]);
7171
+ this.populateFromHydrationCache();
7172
+ }
7173
+ loaded = false;
7174
+ current = null;
7175
+ async load() {
7176
+ if (this.loaded) return this.current;
7177
+ const rootObj = this.root;
7178
+ const typeValue = rootObj[this.relation.typeField];
7179
+ const idValue = rootObj[this.relation.idField];
7180
+ if (!typeValue || idValue === void 0 || idValue === null) {
7181
+ this.loaded = true;
7182
+ return this.current;
7183
+ }
7184
+ const map = await this.loader();
7185
+ const compositeKey = `${toKey9(typeValue)}:${toKey9(idValue)}`;
7186
+ const row = map.get(compositeKey);
7187
+ if (row) {
7188
+ const targetTable = this.resolveTargetTable(toKey9(typeValue));
7189
+ if (targetTable) {
7190
+ this.current = this.createEntity(targetTable, row);
7191
+ }
7192
+ }
7193
+ this.loaded = true;
7194
+ return this.current;
7195
+ }
7196
+ get() {
7197
+ return this.current;
7198
+ }
7199
+ set(data) {
7200
+ if (data === null) {
7201
+ return this.detachCurrent();
7202
+ }
7203
+ const entity = hasEntityMeta(data) ? data : data;
7204
+ if (this.current && this.current !== entity) {
7205
+ this.ctx.registerRelationChange(
7206
+ this.root,
7207
+ this.relationKey,
7208
+ this.rootTable,
7209
+ this.relationName,
7210
+ this.relation,
7211
+ { kind: "remove", entity: this.current }
7212
+ );
7213
+ }
7214
+ this.current = entity;
7215
+ this.loaded = true;
7216
+ this.ctx.registerRelationChange(
7217
+ this.root,
7218
+ this.relationKey,
7219
+ this.rootTable,
7220
+ this.relationName,
7221
+ this.relation,
7222
+ { kind: "attach", entity }
7223
+ );
7224
+ return entity;
7225
+ }
7226
+ toJSON() {
7227
+ if (!this.current) return null;
7228
+ const entityWithToJSON = this.current;
7229
+ return typeof entityWithToJSON.toJSON === "function" ? entityWithToJSON.toJSON() : this.current;
7230
+ }
7231
+ detachCurrent() {
7232
+ const previous = this.current;
7233
+ if (!previous) return null;
7234
+ this.current = null;
7235
+ this.loaded = true;
7236
+ const rootObj = this.root;
7237
+ rootObj[this.relation.typeField] = null;
7238
+ rootObj[this.relation.idField] = null;
7239
+ this.ctx.registerRelationChange(
7240
+ this.root,
7241
+ this.relationKey,
7242
+ this.rootTable,
7243
+ this.relationName,
7244
+ this.relation,
7245
+ { kind: "remove", entity: previous }
7246
+ );
7247
+ return null;
7248
+ }
7249
+ get relationKey() {
7250
+ return `${this.rootTable.name}.${this.relationName}`;
7251
+ }
7252
+ populateFromHydrationCache() {
7253
+ const rootObj = this.root;
7254
+ const typeValue = rootObj[this.relation.typeField];
7255
+ const idValue = rootObj[this.relation.idField];
7256
+ if (!typeValue || idValue === void 0 || idValue === null) return;
7257
+ const compositeKey = `${toKey9(typeValue)}:${toKey9(idValue)}`;
7258
+ const row = getHydrationRecord(this.meta, this.relationName, compositeKey);
7259
+ if (!row) return;
7260
+ const targetTable = this.resolveTargetTable(toKey9(typeValue));
7261
+ if (targetTable) {
7262
+ this.current = this.createEntity(targetTable, row);
7263
+ this.loaded = true;
7264
+ }
7265
+ }
7266
+ };
7267
+
7268
+ // src/orm/lazy-batch/shared.ts
7269
+ var hasColumns = (columns) => Boolean(columns && columns.length > 0);
7270
+ var buildColumnSelection = (table, columns, missingMsg) => {
7271
+ return columns.reduce((acc, column) => {
7272
+ const def = table.columns[column];
7273
+ if (!def) {
7274
+ throw new Error(missingMsg(column));
7275
+ }
7276
+ acc[column] = def;
7277
+ return acc;
7278
+ }, {});
7279
+ };
7280
+ var filterRow = (row, columns) => {
7281
+ const filtered = {};
7282
+ for (const column of columns) {
7283
+ if (column in row) {
7284
+ filtered[column] = row[column];
7285
+ }
7286
+ }
7287
+ return filtered;
7288
+ };
7289
+ var filterRows = (rows, columns) => rows.map((row) => filterRow(row, columns));
7290
+ var rowsFromResults = (results) => {
7291
+ const rows = [];
7292
+ for (const result of results) {
7293
+ const { columns, values } = result;
7294
+ for (const valueRow of values) {
7295
+ const row = {};
7296
+ columns.forEach((column, idx) => {
7297
+ row[column] = valueRow[idx];
7298
+ });
7299
+ rows.push(row);
7300
+ }
7301
+ }
7302
+ return rows;
7303
+ };
7304
+ var executeQuery = async (ctx, qb) => {
7305
+ const compiled = ctx.dialect.compileSelect(qb.getAST());
7306
+ const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
7307
+ return rowsFromResults(results);
7308
+ };
7309
+ var toKey10 = (value) => value === null || value === void 0 ? "" : String(value);
7310
+ var collectKeysFromRoots = (roots, key) => {
7311
+ const collected = /* @__PURE__ */ new Set();
7312
+ for (const tracked of roots) {
7313
+ const value = tracked.entity[key];
7314
+ if (value !== null && value !== void 0) {
7315
+ collected.add(value);
7316
+ }
7317
+ }
7318
+ return collected;
7319
+ };
7320
+ var buildInListValues = (keys) => Array.from(keys);
7321
+ var fetchRowsForKeys = async (ctx, table, column, keys, selection, filter) => {
7322
+ let qb = new SelectQueryBuilder(table).select(selection);
7323
+ qb = qb.where(inList(column, buildInListValues(keys)));
7324
+ if (filter) {
7325
+ qb = qb.where(filter);
7326
+ }
7327
+ return executeQuery(ctx, qb);
7328
+ };
7329
+ var groupRowsByMany = (rows, keyColumn) => {
7330
+ const grouped = /* @__PURE__ */ new Map();
7331
+ for (const row of rows) {
7332
+ const value = row[keyColumn];
7333
+ if (value === null || value === void 0) continue;
7334
+ const key = toKey10(value);
7335
+ const bucket = grouped.get(key) ?? [];
7336
+ bucket.push(row);
7337
+ grouped.set(key, bucket);
7338
+ }
7339
+ return grouped;
7340
+ };
7341
+ var groupRowsByUnique = (rows, keyColumn) => {
7342
+ const lookup = /* @__PURE__ */ new Map();
7343
+ for (const row of rows) {
7344
+ const value = row[keyColumn];
7345
+ if (value === null || value === void 0) continue;
7346
+ const key = toKey10(value);
7347
+ if (!lookup.has(key)) {
7348
+ lookup.set(key, row);
7349
+ }
7350
+ }
7351
+ return lookup;
7352
+ };
7353
+
7354
+ // src/orm/lazy-batch/has-many.ts
7355
+ var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options) => {
7356
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
7357
+ const roots = ctx.getEntitiesForTable(rootTable);
7358
+ const keys = collectKeysFromRoots(roots, localKey);
7359
+ if (!keys.size) {
7360
+ return /* @__PURE__ */ new Map();
7361
+ }
7362
+ const fkColumn = relation.target.columns[relation.foreignKey];
7363
+ if (!fkColumn) return /* @__PURE__ */ new Map();
7364
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
7365
+ const targetPrimaryKey = findPrimaryKey(relation.target);
7366
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
7367
+ if (!selectedColumns.includes(targetPrimaryKey)) {
7368
+ selectedColumns.push(targetPrimaryKey);
7369
+ }
7370
+ const queryColumns = new Set(selectedColumns);
7371
+ queryColumns.add(relation.foreignKey);
7372
+ const selection = buildColumnSelection(
7373
+ relation.target,
7374
+ Array.from(queryColumns),
6717
7375
  (column) => `Column '${column}' not found on relation '${relationName}'`
6718
7376
  );
6719
7377
  const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, options?.filter);
@@ -6870,12 +7528,12 @@ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, o
6870
7528
  if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
6871
7529
  continue;
6872
7530
  }
6873
- const bucket = rootLookup.get(toKey7(rootValue)) ?? [];
7531
+ const bucket = rootLookup.get(toKey10(rootValue)) ?? [];
6874
7532
  bucket.push({
6875
7533
  targetId: targetValue,
6876
7534
  pivot: pivotVisibleColumns.size ? filterRow(pivot, pivotVisibleColumns) : {}
6877
7535
  });
6878
- rootLookup.set(toKey7(rootValue), bucket);
7536
+ rootLookup.set(toKey10(rootValue), bucket);
6879
7537
  targetIds.add(targetValue);
6880
7538
  }
6881
7539
  if (!targetIds.size) {
@@ -6905,27 +7563,144 @@ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, o
6905
7563
  const targetMap = groupRowsByUnique(targetRows, targetKey);
6906
7564
  const targetVisibleColumns = new Set(targetSelectedColumns);
6907
7565
  const result = /* @__PURE__ */ new Map();
6908
- for (const [rootId, entries] of rootLookup.entries()) {
6909
- const bucket = [];
6910
- for (const entry of entries) {
6911
- const targetRow = targetMap.get(toKey7(entry.targetId));
6912
- if (!targetRow) continue;
6913
- const row = targetRequestedColumns ? filterRow(targetRow, targetVisibleColumns) : { ...targetRow };
6914
- if (options?.pivot?.merge) {
6915
- mergePivotIntoRow(row, entry.pivot);
6916
- }
6917
- bucket.push({ ...row, _pivot: entry.pivot });
7566
+ for (const [rootId, entries] of rootLookup.entries()) {
7567
+ const bucket = [];
7568
+ for (const entry of entries) {
7569
+ const targetRow = targetMap.get(toKey10(entry.targetId));
7570
+ if (!targetRow) continue;
7571
+ const row = targetRequestedColumns ? filterRow(targetRow, targetVisibleColumns) : { ...targetRow };
7572
+ if (options?.pivot?.merge) {
7573
+ mergePivotIntoRow(row, entry.pivot);
7574
+ }
7575
+ bucket.push({ ...row, _pivot: entry.pivot });
7576
+ }
7577
+ result.set(rootId, bucket);
7578
+ }
7579
+ return result;
7580
+ };
7581
+ var mergePivotIntoRow = (row, pivot) => {
7582
+ for (const [key, value] of Object.entries(pivot)) {
7583
+ if (key in row) continue;
7584
+ row[key] = value;
7585
+ }
7586
+ };
7587
+
7588
+ // src/orm/lazy-batch/morph-one.ts
7589
+ var loadMorphOneRelation = async (ctx, rootTable, relationName, relation, options) => {
7590
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
7591
+ const roots = ctx.getEntitiesForTable(rootTable);
7592
+ const keys = collectKeysFromRoots(roots, localKey);
7593
+ if (!keys.size) {
7594
+ return /* @__PURE__ */ new Map();
7595
+ }
7596
+ const fkColumn = relation.target.columns[relation.idField];
7597
+ if (!fkColumn) return /* @__PURE__ */ new Map();
7598
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
7599
+ const targetPrimaryKey = findPrimaryKey(relation.target);
7600
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
7601
+ if (!selectedColumns.includes(targetPrimaryKey)) {
7602
+ selectedColumns.push(targetPrimaryKey);
7603
+ }
7604
+ const queryColumns = new Set(selectedColumns);
7605
+ queryColumns.add(relation.idField);
7606
+ const selection = buildColumnSelection(
7607
+ relation.target,
7608
+ Array.from(queryColumns),
7609
+ (column) => `Column '${column}' not found on relation '${relationName}'`
7610
+ );
7611
+ const typeColumn = relation.target.columns[relation.typeField];
7612
+ const discriminatorFilter = eq(
7613
+ typeColumn ?? { type: "Column", table: relation.target.name, name: relation.typeField },
7614
+ { type: "Literal", value: relation.typeValue }
7615
+ );
7616
+ const combinedFilter = options?.filter ? and(options.filter, discriminatorFilter) : discriminatorFilter;
7617
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, combinedFilter);
7618
+ const grouped = groupRowsByUnique(rows, relation.idField);
7619
+ if (!requestedColumns) return grouped;
7620
+ const visibleColumns = new Set(selectedColumns);
7621
+ const filtered = /* @__PURE__ */ new Map();
7622
+ for (const [key, row] of grouped.entries()) {
7623
+ filtered.set(key, filterRow(row, visibleColumns));
7624
+ }
7625
+ return filtered;
7626
+ };
7627
+
7628
+ // src/orm/lazy-batch/morph-many.ts
7629
+ var loadMorphManyRelation = async (ctx, rootTable, relationName, relation, options) => {
7630
+ const localKey = relation.localKey || findPrimaryKey(rootTable);
7631
+ const roots = ctx.getEntitiesForTable(rootTable);
7632
+ const keys = collectKeysFromRoots(roots, localKey);
7633
+ if (!keys.size) {
7634
+ return /* @__PURE__ */ new Map();
7635
+ }
7636
+ const fkColumn = relation.target.columns[relation.idField];
7637
+ if (!fkColumn) return /* @__PURE__ */ new Map();
7638
+ const requestedColumns = hasColumns(options?.columns) ? [...options.columns] : void 0;
7639
+ const targetPrimaryKey = findPrimaryKey(relation.target);
7640
+ const selectedColumns = requestedColumns ? [...requestedColumns] : Object.keys(relation.target.columns);
7641
+ if (!selectedColumns.includes(targetPrimaryKey)) {
7642
+ selectedColumns.push(targetPrimaryKey);
7643
+ }
7644
+ const queryColumns = new Set(selectedColumns);
7645
+ queryColumns.add(relation.idField);
7646
+ const selection = buildColumnSelection(
7647
+ relation.target,
7648
+ Array.from(queryColumns),
7649
+ (column) => `Column '${column}' not found on relation '${relationName}'`
7650
+ );
7651
+ const typeColumn = relation.target.columns[relation.typeField];
7652
+ const discriminatorFilter = eq(
7653
+ typeColumn ?? { type: "Column", table: relation.target.name, name: relation.typeField },
7654
+ { type: "Literal", value: relation.typeValue }
7655
+ );
7656
+ const combinedFilter = options?.filter ? and(options.filter, discriminatorFilter) : discriminatorFilter;
7657
+ const rows = await fetchRowsForKeys(ctx, relation.target, fkColumn, keys, selection, combinedFilter);
7658
+ const grouped = groupRowsByMany(rows, relation.idField);
7659
+ if (!requestedColumns) return grouped;
7660
+ const visibleColumns = new Set(selectedColumns);
7661
+ const filtered = /* @__PURE__ */ new Map();
7662
+ for (const [key, bucket] of grouped.entries()) {
7663
+ filtered.set(key, filterRows(bucket, visibleColumns));
7664
+ }
7665
+ return filtered;
7666
+ };
7667
+
7668
+ // src/orm/lazy-batch/morph-to.ts
7669
+ var loadMorphToRelation = async (ctx, rootTable, _relationName, relation) => {
7670
+ const roots = ctx.getEntitiesForTable(rootTable);
7671
+ const result = /* @__PURE__ */ new Map();
7672
+ const grouped = /* @__PURE__ */ new Map();
7673
+ for (const tracked of roots) {
7674
+ const entity = tracked.entity;
7675
+ const typeValue = entity[relation.typeField];
7676
+ const idValue = entity[relation.idField];
7677
+ if (!typeValue || idValue === void 0 || idValue === null) continue;
7678
+ const typeKey = toKey10(typeValue);
7679
+ const ids = grouped.get(typeKey) ?? /* @__PURE__ */ new Set();
7680
+ ids.add(idValue);
7681
+ grouped.set(typeKey, ids);
7682
+ }
7683
+ for (const [typeKey, ids] of grouped.entries()) {
7684
+ const targetTable = relation.targets[typeKey];
7685
+ if (!targetTable) continue;
7686
+ const targetPk = relation.targetKey || findPrimaryKey(targetTable);
7687
+ const pkColumn = targetTable.columns[targetPk];
7688
+ if (!pkColumn) continue;
7689
+ const selection = buildColumnSelection(
7690
+ targetTable,
7691
+ Object.keys(targetTable.columns),
7692
+ (column) => `Column '${column}' not found on target '${targetTable.name}'`
7693
+ );
7694
+ const rows = await fetchRowsForKeys(ctx, targetTable, pkColumn, ids, selection);
7695
+ for (const row of rows) {
7696
+ const pkValue = row[targetPk];
7697
+ if (pkValue === void 0 || pkValue === null) continue;
7698
+ const compositeKey = `${typeKey}:${toKey10(pkValue)}`;
7699
+ result.set(compositeKey, row);
6918
7700
  }
6919
- result.set(rootId, bucket);
6920
7701
  }
6921
7702
  return result;
6922
7703
  };
6923
- var mergePivotIntoRow = (row, pivot) => {
6924
- for (const [key, value] of Object.entries(pivot)) {
6925
- if (key in row) continue;
6926
- row[key] = value;
6927
- }
6928
- };
6929
7704
 
6930
7705
  // src/orm/entity-relation-cache.ts
6931
7706
  var relationLoaderCache = (meta, relationName, factory) => {
@@ -7092,6 +7867,60 @@ var instantiateWrapper = (meta, relationName, relation, owner, createEntity) =>
7092
7867
  localKey
7093
7868
  );
7094
7869
  }
7870
+ case RelationKinds.MorphOne: {
7871
+ const morphOne2 = relation;
7872
+ const localKey = morphOne2.localKey || findPrimaryKey(meta.table);
7873
+ const loader = () => loadCached(
7874
+ () => loadMorphOneRelation(meta.ctx, meta.table, relationName, morphOne2, resolveOptions())
7875
+ );
7876
+ return new DefaultMorphOneReference(
7877
+ meta.ctx,
7878
+ metaBase,
7879
+ owner,
7880
+ relationName,
7881
+ morphOne2,
7882
+ meta.table,
7883
+ loader,
7884
+ (row) => createEntity(morphOne2.target, row),
7885
+ localKey
7886
+ );
7887
+ }
7888
+ case RelationKinds.MorphMany: {
7889
+ const morphMany2 = relation;
7890
+ const localKey = morphMany2.localKey || findPrimaryKey(meta.table);
7891
+ const loader = () => loadCached(
7892
+ () => loadMorphManyRelation(meta.ctx, meta.table, relationName, morphMany2, resolveOptions())
7893
+ );
7894
+ return new DefaultMorphManyCollection(
7895
+ meta.ctx,
7896
+ metaBase,
7897
+ owner,
7898
+ relationName,
7899
+ morphMany2,
7900
+ meta.table,
7901
+ loader,
7902
+ (row) => createEntity(morphMany2.target, row),
7903
+ localKey
7904
+ );
7905
+ }
7906
+ case RelationKinds.MorphTo: {
7907
+ const morphTo2 = relation;
7908
+ const loader = () => loadCached(
7909
+ () => loadMorphToRelation(meta.ctx, meta.table, relationName, morphTo2)
7910
+ );
7911
+ const resolveTargetTable = (typeValue) => morphTo2.targets[typeValue];
7912
+ return new DefaultMorphToReference(
7913
+ meta.ctx,
7914
+ metaBase,
7915
+ owner,
7916
+ relationName,
7917
+ morphTo2,
7918
+ meta.table,
7919
+ loader,
7920
+ (table, row) => createEntity(table, row),
7921
+ resolveTargetTable
7922
+ );
7923
+ }
7095
7924
  default:
7096
7925
  return void 0;
7097
7926
  }
@@ -7117,7 +7946,7 @@ var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOption
7117
7946
  const isCollectionRelation = (relationName) => {
7118
7947
  const rel = table.relations[relationName];
7119
7948
  if (!rel) return false;
7120
- return rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany;
7949
+ return rel.type === RelationKinds.HasMany || rel.type === RelationKinds.BelongsToMany || rel.type === RelationKinds.MorphMany;
7121
7950
  };
7122
7951
  const buildJson = (self, options) => {
7123
7952
  const json = {};
@@ -7299,9 +8128,11 @@ function rowsToQueryResult(rows) {
7299
8128
  }
7300
8129
  function createExecutorFromQueryRunner(runner) {
7301
8130
  const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
8131
+ const supportsSavepoints = supportsTransactions && typeof runner.savepoint === "function" && typeof runner.releaseSavepoint === "function" && typeof runner.rollbackToSavepoint === "function";
7302
8132
  return {
7303
8133
  capabilities: {
7304
- transactions: supportsTransactions
8134
+ transactions: supportsTransactions,
8135
+ ...supportsSavepoints ? { savepoints: true } : {}
7305
8136
  },
7306
8137
  async executeSql(sql, params) {
7307
8138
  const rows = await runner.query(sql, params);
@@ -7326,6 +8157,24 @@ function createExecutorFromQueryRunner(runner) {
7326
8157
  }
7327
8158
  await runner.rollbackTransaction.call(runner);
7328
8159
  },
8160
+ async savepoint(name) {
8161
+ if (!supportsSavepoints) {
8162
+ throw new Error("Savepoints are not supported by this executor");
8163
+ }
8164
+ await runner.savepoint.call(runner, name);
8165
+ },
8166
+ async releaseSavepoint(name) {
8167
+ if (!supportsSavepoints) {
8168
+ throw new Error("Savepoints are not supported by this executor");
8169
+ }
8170
+ await runner.releaseSavepoint.call(runner, name);
8171
+ },
8172
+ async rollbackToSavepoint(name) {
8173
+ if (!supportsSavepoints) {
8174
+ throw new Error("Savepoints are not supported by this executor");
8175
+ }
8176
+ await runner.rollbackToSavepoint.call(runner, name);
8177
+ },
7329
8178
  async dispose() {
7330
8179
  await runner.dispose?.call(runner);
7331
8180
  }
@@ -7700,6 +8549,9 @@ function buildWhereHasPredicate(env, context, relationFacet, createChildBuilder,
7700
8549
  }
7701
8550
  const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
7702
8551
  const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
8552
+ if (!isSingleTargetRelation(relation)) {
8553
+ throw new Error(`Polymorphic relation '${relationName}' does not support whereHas/whereHasNot`);
8554
+ }
7703
8555
  let subQb = createChildBuilder(relation.target);
7704
8556
  if (callback) {
7705
8557
  subQb = callback(subQb);
@@ -9286,6 +10138,7 @@ var isTableDef = (value) => {
9286
10138
 
9287
10139
  // src/decorators/decorator-metadata.ts
9288
10140
  var METADATA_KEY = "metal-orm:decorators";
10141
+ var LEGACY_METADATA_KEY = /* @__PURE__ */ Symbol.for("metal-orm:decorators:legacy");
9289
10142
  var getOrCreateMetadataBag = (context) => {
9290
10143
  const metadata = context.metadata || (context.metadata = {});
9291
10144
  let bag = metadata[METADATA_KEY];
@@ -9295,16 +10148,65 @@ var getOrCreateMetadataBag = (context) => {
9295
10148
  }
9296
10149
  return bag;
9297
10150
  };
10151
+ var getOrCreateMetadataBagOnConstructor = (ctor) => {
10152
+ const carrier = ctor;
10153
+ let bag = carrier[LEGACY_METADATA_KEY];
10154
+ if (!bag) {
10155
+ bag = { columns: [], relations: [], transformers: [] };
10156
+ carrier[LEGACY_METADATA_KEY] = bag;
10157
+ }
10158
+ return bag;
10159
+ };
9298
10160
  var readMetadataBag = (context) => {
9299
10161
  return context.metadata?.[METADATA_KEY];
9300
10162
  };
9301
10163
  var readMetadataBagFromConstructor = (ctor) => {
9302
10164
  const metadataSymbol = Symbol.metadata;
9303
- if (!metadataSymbol) return void 0;
9304
- const metadata = Reflect.get(ctor, metadataSymbol);
9305
- return metadata?.[METADATA_KEY];
10165
+ if (metadataSymbol) {
10166
+ const metadata = Reflect.get(ctor, metadataSymbol);
10167
+ const stage3Bag = metadata?.[METADATA_KEY];
10168
+ if (stage3Bag) {
10169
+ return stage3Bag;
10170
+ }
10171
+ }
10172
+ return ctor[LEGACY_METADATA_KEY];
9306
10173
  };
9307
10174
  var getDecoratorMetadata = (ctor) => readMetadataBagFromConstructor(ctor);
10175
+ var normalizePropertyName = (name) => {
10176
+ if (typeof name === "symbol") {
10177
+ return name.description ?? name.toString();
10178
+ }
10179
+ return name;
10180
+ };
10181
+ var isStage3FieldContext = (value) => {
10182
+ return typeof value === "object" && value !== null && "name" in value && "private" in value && "metadata" in value;
10183
+ };
10184
+ var resolveFieldDecoratorInfo = (targetOrValue, contextOrProperty, decoratorName) => {
10185
+ if (isStage3FieldContext(contextOrProperty)) {
10186
+ if (!contextOrProperty.name) {
10187
+ throw new Error(`${decoratorName} decorator requires a property name`);
10188
+ }
10189
+ if (contextOrProperty.private) {
10190
+ throw new Error(`${decoratorName} decorator does not support private fields`);
10191
+ }
10192
+ return {
10193
+ propertyName: normalizePropertyName(contextOrProperty.name),
10194
+ bag: getOrCreateMetadataBag(contextOrProperty)
10195
+ };
10196
+ }
10197
+ if (typeof contextOrProperty === "string" || typeof contextOrProperty === "symbol") {
10198
+ const legacyTarget = targetOrValue;
10199
+ const ctor = typeof legacyTarget === "function" ? legacyTarget : legacyTarget?.constructor;
10200
+ if (!ctor || typeof ctor !== "function" && typeof ctor !== "object") {
10201
+ throw new Error(`${decoratorName} decorator requires a class field target`);
10202
+ }
10203
+ return {
10204
+ propertyName: normalizePropertyName(contextOrProperty),
10205
+ bag: getOrCreateMetadataBagOnConstructor(ctor)
10206
+ };
10207
+ }
10208
+ throw new Error(`${decoratorName} decorator received an unsupported decorator context`);
10209
+ };
9308
10210
 
9309
10211
  // src/decorators/bootstrap.ts
9310
10212
  var unwrapTarget = (target) => {
@@ -9394,6 +10296,48 @@ var buildRelationDefinitions = (meta, tableMap) => {
9394
10296
  );
9395
10297
  break;
9396
10298
  }
10299
+ case RelationKinds.MorphOne: {
10300
+ relations[name] = morphOne(
10301
+ resolveTableTarget(relation.target, tableMap),
10302
+ {
10303
+ as: relation.morphName,
10304
+ typeValue: relation.typeValue,
10305
+ typeField: relation.typeField,
10306
+ idField: relation.idField,
10307
+ localKey: relation.localKey,
10308
+ cascade: relation.cascade
10309
+ }
10310
+ );
10311
+ break;
10312
+ }
10313
+ case RelationKinds.MorphMany: {
10314
+ relations[name] = morphMany(
10315
+ resolveTableTarget(relation.target, tableMap),
10316
+ {
10317
+ as: relation.morphName,
10318
+ typeValue: relation.typeValue,
10319
+ typeField: relation.typeField,
10320
+ idField: relation.idField,
10321
+ localKey: relation.localKey,
10322
+ cascade: relation.cascade
10323
+ }
10324
+ );
10325
+ break;
10326
+ }
10327
+ case RelationKinds.MorphTo: {
10328
+ const resolvedTargets = {};
10329
+ for (const [typeValue, targetResolver] of Object.entries(relation.targets)) {
10330
+ resolvedTargets[typeValue] = resolveTableTarget(targetResolver, tableMap);
10331
+ }
10332
+ relations[name] = morphTo({
10333
+ typeField: relation.typeField,
10334
+ idField: relation.idField,
10335
+ targets: resolvedTargets,
10336
+ targetKey: relation.targetKey,
10337
+ cascade: relation.cascade
10338
+ });
10339
+ break;
10340
+ }
9397
10341
  }
9398
10342
  }
9399
10343
  return relations;
@@ -9467,6 +10411,45 @@ var resolveSingleRelation = (relationName, relation, rootMeta) => {
9467
10411
  }
9468
10412
  );
9469
10413
  }
10414
+ case RelationKinds.MorphOne: {
10415
+ return morphOne(
10416
+ resolveTableTarget(relation.target, tableMap),
10417
+ {
10418
+ as: relation.morphName,
10419
+ typeValue: relation.typeValue,
10420
+ typeField: relation.typeField,
10421
+ idField: relation.idField,
10422
+ localKey: relation.localKey,
10423
+ cascade: relation.cascade
10424
+ }
10425
+ );
10426
+ }
10427
+ case RelationKinds.MorphMany: {
10428
+ return morphMany(
10429
+ resolveTableTarget(relation.target, tableMap),
10430
+ {
10431
+ as: relation.morphName,
10432
+ typeValue: relation.typeValue,
10433
+ typeField: relation.typeField,
10434
+ idField: relation.idField,
10435
+ localKey: relation.localKey,
10436
+ cascade: relation.cascade
10437
+ }
10438
+ );
10439
+ }
10440
+ case RelationKinds.MorphTo: {
10441
+ const resolvedTargets = {};
10442
+ for (const [typeValue, targetResolver] of Object.entries(relation.targets)) {
10443
+ resolvedTargets[typeValue] = resolveTableTarget(targetResolver, tableMap);
10444
+ }
10445
+ return morphTo({
10446
+ typeField: relation.typeField,
10447
+ idField: relation.idField,
10448
+ targets: resolvedTargets,
10449
+ targetKey: relation.targetKey,
10450
+ cascade: relation.cascade
10451
+ });
10452
+ }
9470
10453
  default:
9471
10454
  throw new Error(`Unknown relation kind for relation '${relationName}'`);
9472
10455
  }
@@ -13056,6 +14039,9 @@ var TypeScriptGenerator = class {
13056
14039
  visitLogicalExpression(logical) {
13057
14040
  return this.printLogicalExpression(logical);
13058
14041
  }
14042
+ visitNotExpression(notExpr) {
14043
+ return this.printNotExpression(notExpr);
14044
+ }
13059
14045
  visitNullExpression(nullExpr) {
13060
14046
  return this.printNullExpression(nullExpr);
13061
14047
  }
@@ -13131,6 +14117,9 @@ var TypeScriptGenerator = class {
13131
14117
  ${parts.join(",\n ")}
13132
14118
  )`;
13133
14119
  }
14120
+ printNotExpression(notExpr) {
14121
+ return `not(${this.printExpression(notExpr.operand)})`;
14122
+ }
13134
14123
  printArithmeticExpression(expr) {
13135
14124
  const left2 = this.printOperand(expr.left);
13136
14125
  const right2 = this.printOperand(expr.right);
@@ -13824,6 +14813,15 @@ var RelationChangeProcessor = class {
13824
14813
  case RelationKinds.BelongsTo:
13825
14814
  await this.handleBelongsToChange(entry);
13826
14815
  break;
14816
+ case RelationKinds.MorphOne:
14817
+ await this.handleMorphOneChange(entry);
14818
+ break;
14819
+ case RelationKinds.MorphMany:
14820
+ await this.handleMorphManyChange(entry);
14821
+ break;
14822
+ case RelationKinds.MorphTo:
14823
+ await this.handleMorphToChange(entry);
14824
+ break;
13827
14825
  }
13828
14826
  }
13829
14827
  }
@@ -14026,6 +15024,86 @@ var RelationChangeProcessor = class {
14026
15024
  }
14027
15025
  return Object.keys(payload).length ? payload : void 0;
14028
15026
  }
15027
+ async handleMorphOneChange(entry) {
15028
+ const relation = entry.relation;
15029
+ const target = entry.change.entity;
15030
+ if (!target) return;
15031
+ const tracked = this.unitOfWork.findTracked(target);
15032
+ if (!tracked) return;
15033
+ const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
15034
+ const rootValue = entry.root[localKey];
15035
+ if (rootValue === void 0 || rootValue === null) return;
15036
+ if (entry.change.kind === "add" || entry.change.kind === "attach") {
15037
+ const child = tracked.entity;
15038
+ child[relation.idField] = rootValue;
15039
+ child[relation.typeField] = relation.typeValue;
15040
+ this.unitOfWork.markDirty(tracked.entity);
15041
+ return;
15042
+ }
15043
+ if (entry.change.kind === "remove") {
15044
+ const child = tracked.entity;
15045
+ if (relation.cascade === "all" || relation.cascade === "remove") {
15046
+ this.unitOfWork.markRemoved(child);
15047
+ return;
15048
+ }
15049
+ child[relation.idField] = null;
15050
+ child[relation.typeField] = null;
15051
+ this.unitOfWork.markDirty(child);
15052
+ }
15053
+ }
15054
+ async handleMorphManyChange(entry) {
15055
+ const relation = entry.relation;
15056
+ const target = entry.change.entity;
15057
+ if (!target) return;
15058
+ const tracked = this.unitOfWork.findTracked(target);
15059
+ if (!tracked) return;
15060
+ const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
15061
+ const rootValue = entry.root[localKey];
15062
+ if (rootValue === void 0 || rootValue === null) return;
15063
+ if (entry.change.kind === "add" || entry.change.kind === "attach") {
15064
+ const child = tracked.entity;
15065
+ child[relation.idField] = rootValue;
15066
+ child[relation.typeField] = relation.typeValue;
15067
+ this.unitOfWork.markDirty(tracked.entity);
15068
+ return;
15069
+ }
15070
+ if (entry.change.kind === "remove") {
15071
+ const child = tracked.entity;
15072
+ if (relation.cascade === "all" || relation.cascade === "remove") {
15073
+ this.unitOfWork.markRemoved(child);
15074
+ return;
15075
+ }
15076
+ child[relation.idField] = null;
15077
+ child[relation.typeField] = null;
15078
+ this.unitOfWork.markDirty(child);
15079
+ }
15080
+ }
15081
+ async handleMorphToChange(entry) {
15082
+ const relation = entry.relation;
15083
+ const target = entry.change.entity;
15084
+ if (!target) return;
15085
+ if (entry.change.kind === "attach" || entry.change.kind === "add") {
15086
+ const targetEntity = target;
15087
+ for (const [typeValue, targetTable] of Object.entries(relation.targets)) {
15088
+ const targetPk = relation.targetKey || findPrimaryKey(targetTable);
15089
+ const pkValue = targetEntity[targetPk];
15090
+ if (pkValue !== void 0 && pkValue !== null) {
15091
+ const rootEntity = entry.root;
15092
+ rootEntity[relation.typeField] = typeValue;
15093
+ rootEntity[relation.idField] = pkValue;
15094
+ this.unitOfWork.markDirty(entry.root);
15095
+ break;
15096
+ }
15097
+ }
15098
+ return;
15099
+ }
15100
+ if (entry.change.kind === "remove") {
15101
+ const rootEntity = entry.root;
15102
+ rootEntity[relation.typeField] = null;
15103
+ rootEntity[relation.idField] = null;
15104
+ this.unitOfWork.markDirty(entry.root);
15105
+ }
15106
+ }
14029
15107
  };
14030
15108
 
14031
15109
  // src/orm/query-logger.ts
@@ -14042,6 +15120,9 @@ var createQueryLoggingExecutor = (executor, logger) => {
14042
15120
  beginTransaction: () => executor.beginTransaction(),
14043
15121
  commitTransaction: () => executor.commitTransaction(),
14044
15122
  rollbackTransaction: () => executor.rollbackTransaction(),
15123
+ savepoint: executor.savepoint ? (name) => executor.savepoint(name) : void 0,
15124
+ releaseSavepoint: executor.releaseSavepoint ? (name) => executor.releaseSavepoint(name) : void 0,
15125
+ rollbackToSavepoint: executor.rollbackToSavepoint ? (name) => executor.rollbackToSavepoint(name) : void 0,
14045
15126
  dispose: () => executor.dispose()
14046
15127
  };
14047
15128
  return wrapped;
@@ -14064,7 +15145,7 @@ var runInTransaction = async (executor, action) => {
14064
15145
  };
14065
15146
 
14066
15147
  // src/orm/save-graph.ts
14067
- var toKey8 = (value) => value === null || value === void 0 ? "" : String(value);
15148
+ var toKey11 = (value) => value === null || value === void 0 ? "" : String(value);
14068
15149
  var coerceColumnValue = (table, columnName, value, options) => {
14069
15150
  if (value === null || value === void 0) return value;
14070
15151
  const column = table.columns[columnName];
@@ -14126,11 +15207,11 @@ var isEntityInCollection = (items, pkName, entity) => {
14126
15207
  if (items.includes(entity)) return true;
14127
15208
  const entityPk = entity[pkName];
14128
15209
  if (entityPk === void 0 || entityPk === null) return false;
14129
- return items.some((item) => toKey8(item[pkName]) === toKey8(entityPk));
15210
+ return items.some((item) => toKey11(item[pkName]) === toKey11(entityPk));
14130
15211
  };
14131
15212
  var findInCollectionByPk = (items, pkName, pkValue) => {
14132
15213
  if (pkValue === void 0 || pkValue === null) return void 0;
14133
- return items.find((item) => toKey8(item[pkName]) === toKey8(pkValue));
15214
+ return items.find((item) => toKey11(item[pkName]) === toKey11(pkValue));
14134
15215
  };
14135
15216
  var extractPivotPayload = (payload) => {
14136
15217
  const pivot = payload._pivot ?? payload.pivot;
@@ -14159,13 +15240,13 @@ var handleHasMany = async (session, root, relationName, relation, payload, optio
14159
15240
  collection.attach(entity);
14160
15241
  }
14161
15242
  if (pkValue !== void 0 && pkValue !== null) {
14162
- seen.add(toKey8(pkValue));
15243
+ seen.add(toKey11(pkValue));
14163
15244
  }
14164
15245
  }
14165
15246
  if (options.pruneMissing) {
14166
15247
  for (const item of [...collection.getItems()]) {
14167
15248
  const pkValue = item[targetPk];
14168
- if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
15249
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey11(pkValue))) {
14169
15250
  collection.remove(item);
14170
15251
  }
14171
15252
  }
@@ -14223,7 +15304,7 @@ var handleBelongsToMany = async (session, root, relationName, relation, payload,
14223
15304
  if (typeof item === "number" || typeof item === "string") {
14224
15305
  const id = item;
14225
15306
  collection.attach(id);
14226
- seen.add(toKey8(id));
15307
+ seen.add(toKey11(id));
14227
15308
  continue;
14228
15309
  }
14229
15310
  const asObj = item;
@@ -14241,18 +15322,89 @@ var handleBelongsToMany = async (session, root, relationName, relation, payload,
14241
15322
  collection.attach(entity, pivotPayload);
14242
15323
  }
14243
15324
  if (pkValue !== void 0 && pkValue !== null) {
14244
- seen.add(toKey8(pkValue));
15325
+ seen.add(toKey11(pkValue));
14245
15326
  }
14246
15327
  }
14247
15328
  if (options.pruneMissing) {
14248
15329
  for (const item of [...collection.getItems()]) {
14249
15330
  const pkValue = item[targetPk];
14250
- if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
15331
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey11(pkValue))) {
14251
15332
  collection.detach(item);
14252
15333
  }
14253
15334
  }
14254
15335
  }
14255
15336
  };
15337
+ var handleMorphOne = async (session, root, relationName, relation, payload, options) => {
15338
+ const ref = root[relationName];
15339
+ if (payload === void 0) return;
15340
+ if (payload === null) {
15341
+ ref.set(null);
15342
+ return;
15343
+ }
15344
+ const pk = findPrimaryKey(relation.target);
15345
+ if (typeof payload === "number" || typeof payload === "string") {
15346
+ const entity = ref.set({ [pk]: payload });
15347
+ if (entity) {
15348
+ await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
15349
+ }
15350
+ return;
15351
+ }
15352
+ const attached = ref.set(payload);
15353
+ if (attached) {
15354
+ await applyGraphToEntity(session, relation.target, attached, payload, options);
15355
+ }
15356
+ };
15357
+ var handleMorphMany = async (session, root, relationName, relation, payload, options) => {
15358
+ if (!Array.isArray(payload)) return;
15359
+ const collection = root[relationName];
15360
+ await collection.load();
15361
+ const targetTable = relation.target;
15362
+ const targetPk = findPrimaryKey(targetTable);
15363
+ const existing = collection.getItems();
15364
+ const seen = /* @__PURE__ */ new Set();
15365
+ for (const item of payload) {
15366
+ if (item === null || item === void 0) continue;
15367
+ const asObj = typeof item === "object" ? item : { [targetPk]: item };
15368
+ const pkValue = asObj[targetPk];
15369
+ const current = findInCollectionByPk(existing, targetPk, pkValue) ?? (pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) : void 0);
15370
+ const entity = current ?? ensureEntity(session, targetTable, asObj, options);
15371
+ assignColumns(targetTable, entity, asObj, options);
15372
+ await applyGraphToEntity(session, targetTable, entity, asObj, options);
15373
+ if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
15374
+ collection.attach(entity);
15375
+ }
15376
+ if (pkValue !== void 0 && pkValue !== null) {
15377
+ seen.add(toKey11(pkValue));
15378
+ }
15379
+ }
15380
+ if (options.pruneMissing) {
15381
+ for (const item of [...collection.getItems()]) {
15382
+ const pkValue = item[targetPk];
15383
+ if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey11(pkValue))) {
15384
+ collection.remove(item);
15385
+ }
15386
+ }
15387
+ }
15388
+ };
15389
+ var handleMorphTo = async (session, root, relationName, relation, payload, options) => {
15390
+ const ref = root[relationName];
15391
+ if (payload === void 0) return;
15392
+ if (payload === null) {
15393
+ ref.set(null);
15394
+ return;
15395
+ }
15396
+ const attached = ref.set(payload);
15397
+ if (attached) {
15398
+ for (const [, targetTable] of Object.entries(relation.targets)) {
15399
+ const pk = relation.targetKey || findPrimaryKey(targetTable);
15400
+ const pkValue = attached[pk];
15401
+ if (pkValue !== void 0 && pkValue !== null) {
15402
+ await applyGraphToEntity(session, targetTable, attached, payload, options);
15403
+ break;
15404
+ }
15405
+ }
15406
+ }
15407
+ };
14256
15408
  var applyRelation = async (session, table, entity, relationName, relation, payload, options) => {
14257
15409
  switch (relation.type) {
14258
15410
  case RelationKinds.HasMany:
@@ -14263,6 +15415,12 @@ var applyRelation = async (session, table, entity, relationName, relation, paylo
14263
15415
  return handleBelongsTo(session, entity, relationName, relation, payload, options);
14264
15416
  case RelationKinds.BelongsToMany:
14265
15417
  return handleBelongsToMany(session, entity, relationName, relation, payload, options);
15418
+ case RelationKinds.MorphOne:
15419
+ return handleMorphOne(session, entity, relationName, relation, payload, options);
15420
+ case RelationKinds.MorphMany:
15421
+ return handleMorphMany(session, entity, relationName, relation, payload, options);
15422
+ case RelationKinds.MorphTo:
15423
+ return handleMorphTo(session, entity, relationName, relation, payload, options);
14266
15424
  }
14267
15425
  };
14268
15426
  var applyGraphToEntity = async (session, table, entity, payload, options) => {
@@ -14291,6 +15449,8 @@ var patchGraphInternal = async (session, entityClass, existing, payload, options
14291
15449
  };
14292
15450
 
14293
15451
  // src/orm/orm-session.ts
15452
+ var NESTED_TRANSACTIONS_REQUIRE_SAVEPOINTS = "Nested session.transaction calls require savepoint support in this executor";
15453
+ var ROLLBACK_ONLY_TRANSACTION = "Cannot commit transaction because an inner transaction failed";
14294
15454
  var OrmSession = class {
14295
15455
  /** The ORM instance */
14296
15456
  orm;
@@ -14310,6 +15470,9 @@ var OrmSession = class {
14310
15470
  tenantId;
14311
15471
  interceptors;
14312
15472
  saveGraphDefaults;
15473
+ transactionDepth = 0;
15474
+ savepointCounter = 0;
15475
+ rollbackOnly = false;
14313
15476
  /**
14314
15477
  * Creates a new OrmSession instance.
14315
15478
  * @param opts - Session options
@@ -14652,16 +15815,43 @@ var OrmSession = class {
14652
15815
  await this.commit();
14653
15816
  return result;
14654
15817
  }
14655
- await this.executor.beginTransaction();
15818
+ const isOutermost = this.transactionDepth === 0;
15819
+ let savepointName = null;
15820
+ if (isOutermost) {
15821
+ this.rollbackOnly = false;
15822
+ await this.executor.beginTransaction();
15823
+ } else {
15824
+ this.assertSavepointSupport();
15825
+ savepointName = this.nextSavepointName();
15826
+ await this.executor.savepoint(savepointName);
15827
+ }
15828
+ this.transactionDepth += 1;
14656
15829
  try {
14657
15830
  const result = await fn8(this);
15831
+ this.throwIfRollbackOnly();
14658
15832
  await this.flushWithHooks();
14659
- await this.executor.commitTransaction();
14660
- await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
15833
+ this.throwIfRollbackOnly();
15834
+ if (isOutermost) {
15835
+ await this.executor.commitTransaction();
15836
+ await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
15837
+ } else {
15838
+ await this.executor.releaseSavepoint(savepointName);
15839
+ }
14661
15840
  return result;
14662
15841
  } catch (err) {
14663
- await this.rollback();
15842
+ if (isOutermost) {
15843
+ await this.rollback();
15844
+ } else {
15845
+ this.rollbackOnly = true;
15846
+ await this.executor.rollbackToSavepoint(savepointName);
15847
+ }
14664
15848
  throw err;
15849
+ } finally {
15850
+ this.transactionDepth = Math.max(0, this.transactionDepth - 1);
15851
+ if (this.transactionDepth === 0) {
15852
+ this.rollbackOnly = false;
15853
+ this.savepointCounter = 0;
15854
+ }
14665
15855
  }
14666
15856
  }
14667
15857
  /**
@@ -14671,6 +15861,9 @@ var OrmSession = class {
14671
15861
  if (this.executor.capabilities.transactions) {
14672
15862
  await this.executor.rollbackTransaction();
14673
15863
  }
15864
+ this.transactionDepth = 0;
15865
+ this.savepointCounter = 0;
15866
+ this.rollbackOnly = false;
14674
15867
  this.unitOfWork.reset();
14675
15868
  this.relationChanges.reset();
14676
15869
  }
@@ -14745,6 +15938,23 @@ var OrmSession = class {
14745
15938
  resolveSaveGraphOptions(options) {
14746
15939
  return { ...this.saveGraphDefaults ?? {}, ...options ?? {} };
14747
15940
  }
15941
+ assertSavepointSupport() {
15942
+ if (!this.executor.capabilities.savepoints) {
15943
+ throw new Error(NESTED_TRANSACTIONS_REQUIRE_SAVEPOINTS);
15944
+ }
15945
+ if (typeof this.executor.savepoint !== "function" || typeof this.executor.releaseSavepoint !== "function" || typeof this.executor.rollbackToSavepoint !== "function") {
15946
+ throw new Error(NESTED_TRANSACTIONS_REQUIRE_SAVEPOINTS);
15947
+ }
15948
+ }
15949
+ nextSavepointName() {
15950
+ this.savepointCounter += 1;
15951
+ return `metalorm_sp_${this.savepointCounter}`;
15952
+ }
15953
+ throwIfRollbackOnly() {
15954
+ if (this.rollbackOnly) {
15955
+ throw new Error(ROLLBACK_ONLY_TRANSACTION);
15956
+ }
15957
+ }
14748
15958
  };
14749
15959
  var buildRelationChangeEntry = (root, relationKey, rootTable, relationName, relation, change) => ({
14750
15960
  root,
@@ -15269,7 +16479,7 @@ var TREE_METADATA_KEY = /* @__PURE__ */ Symbol("metal-orm:tree");
15269
16479
  function Tree(options = {}) {
15270
16480
  return function(target, context) {
15271
16481
  const config = resolveTreeConfig(options);
15272
- const metadataBag = readMetadataBag(context) ?? readMetadataBagFromConstructor(target);
16482
+ const metadataBag = context ? readMetadataBag(context) ?? readMetadataBagFromConstructor(target) : readMetadataBagFromConstructor(target);
15273
16483
  const metadata = {
15274
16484
  config,
15275
16485
  parentProperty: metadataBag?.tree?.parentProperty,
@@ -15281,28 +16491,22 @@ function Tree(options = {}) {
15281
16491
  };
15282
16492
  }
15283
16493
  function TreeParent() {
15284
- return function(_value, context) {
15285
- if (!context.name) {
15286
- throw new Error("TreeParent decorator requires a property name");
15287
- }
15288
- if (context.private) {
15289
- throw new Error("TreeParent decorator does not support private fields");
15290
- }
15291
- const propertyName = String(context.name);
15292
- const bag = getOrCreateMetadataBag(context);
16494
+ return function(targetOrValue, contextOrProperty) {
16495
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
16496
+ targetOrValue,
16497
+ contextOrProperty,
16498
+ "TreeParent"
16499
+ );
15293
16500
  bag.tree = { ...bag.tree, parentProperty: propertyName };
15294
16501
  };
15295
16502
  }
15296
16503
  function TreeChildren() {
15297
- return function(_value, context) {
15298
- if (!context.name) {
15299
- throw new Error("TreeChildren decorator requires a property name");
15300
- }
15301
- if (context.private) {
15302
- throw new Error("TreeChildren decorator does not support private fields");
15303
- }
15304
- const propertyName = String(context.name);
15305
- const bag = getOrCreateMetadataBag(context);
16504
+ return function(targetOrValue, contextOrProperty) {
16505
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
16506
+ targetOrValue,
16507
+ contextOrProperty,
16508
+ "TreeChildren"
16509
+ );
15306
16510
  bag.tree = { ...bag.tree, childrenProperty: propertyName };
15307
16511
  };
15308
16512
  }
@@ -15466,7 +16670,7 @@ function Entity(options = {}) {
15466
16670
  const ctor = value;
15467
16671
  const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
15468
16672
  setEntityTableName(ctor, tableName, options.hooks, options.type);
15469
- const bag = readMetadataBag(context);
16673
+ const bag = context ? readMetadataBag(context) : readMetadataBagFromConstructor(ctor);
15470
16674
  if (bag) {
15471
16675
  const meta = ensureEntityMetadata(ctor);
15472
16676
  for (const entry of bag.columns) {
@@ -15520,29 +16724,20 @@ var normalizeColumnInput = (input) => {
15520
16724
  }
15521
16725
  return column;
15522
16726
  };
15523
- var normalizePropertyName = (name) => {
15524
- if (typeof name === "symbol") {
15525
- return name.description ?? name.toString();
15526
- }
15527
- return name;
15528
- };
15529
- var registerColumnFromContext = (context, column) => {
15530
- if (!context.name) {
15531
- throw new Error("Column decorator requires a property name");
15532
- }
15533
- if (context.private) {
15534
- throw new Error("Column decorator does not support private fields");
15535
- }
15536
- const propertyName = normalizePropertyName(context.name);
15537
- const bag = getOrCreateMetadataBag(context);
16727
+ var registerColumnFromContext = (targetOrValue, contextOrProperty, column) => {
16728
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
16729
+ targetOrValue,
16730
+ contextOrProperty,
16731
+ "Column"
16732
+ );
15538
16733
  if (!bag.columns.some((entry) => entry.propertyName === propertyName)) {
15539
16734
  bag.columns.push({ propertyName, column: { ...column } });
15540
16735
  }
15541
16736
  };
15542
16737
  function Column(definition) {
15543
16738
  const normalized = normalizeColumnInput(definition);
15544
- return function(_value, context) {
15545
- registerColumnFromContext(context, normalized);
16739
+ return function(targetOrValue, contextOrProperty) {
16740
+ registerColumnFromContext(targetOrValue, contextOrProperty, normalized);
15546
16741
  };
15547
16742
  }
15548
16743
  function PrimaryKey(definition) {
@@ -15552,22 +16747,13 @@ function PrimaryKey(definition) {
15552
16747
  }
15553
16748
 
15554
16749
  // src/decorators/relations.ts
15555
- var normalizePropertyName2 = (name) => {
15556
- if (typeof name === "symbol") {
15557
- return name.description ?? name.toString();
15558
- }
15559
- return name;
15560
- };
15561
16750
  var createFieldDecorator = (metadataFactory) => {
15562
- return function(_value, context) {
15563
- if (!context.name) {
15564
- throw new Error("Relation decorator requires a property name");
15565
- }
15566
- if (context.private) {
15567
- throw new Error("Relation decorator does not support private fields");
15568
- }
15569
- const propertyName = normalizePropertyName2(context.name);
15570
- const bag = getOrCreateMetadataBag(context);
16751
+ return function(targetOrValue, contextOrProperty) {
16752
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
16753
+ targetOrValue,
16754
+ contextOrProperty,
16755
+ "Relation"
16756
+ );
15571
16757
  const relationMetadata = metadataFactory(propertyName);
15572
16758
  if (!bag.relations.some((entry) => entry.propertyName === propertyName)) {
15573
16759
  bag.relations.push({ propertyName, relation: relationMetadata });
@@ -15619,6 +16805,43 @@ function BelongsToMany(options) {
15619
16805
  cascade: options.cascade
15620
16806
  }));
15621
16807
  }
16808
+ function MorphTo(options) {
16809
+ return createFieldDecorator((propertyName) => ({
16810
+ kind: RelationKinds.MorphTo,
16811
+ propertyKey: propertyName,
16812
+ typeField: options.typeField,
16813
+ idField: options.idField,
16814
+ targets: options.targets,
16815
+ targetKey: options.targetKey,
16816
+ cascade: options.cascade
16817
+ }));
16818
+ }
16819
+ function MorphOne(options) {
16820
+ return createFieldDecorator((propertyName) => ({
16821
+ kind: RelationKinds.MorphOne,
16822
+ propertyKey: propertyName,
16823
+ target: options.target,
16824
+ morphName: options.morphName,
16825
+ typeValue: options.typeValue,
16826
+ typeField: options.typeField,
16827
+ idField: options.idField,
16828
+ localKey: options.localKey,
16829
+ cascade: options.cascade
16830
+ }));
16831
+ }
16832
+ function MorphMany(options) {
16833
+ return createFieldDecorator((propertyName) => ({
16834
+ kind: RelationKinds.MorphMany,
16835
+ propertyKey: propertyName,
16836
+ target: options.target,
16837
+ morphName: options.morphName,
16838
+ typeValue: options.typeValue,
16839
+ typeField: options.typeField,
16840
+ idField: options.idField,
16841
+ localKey: options.localKey,
16842
+ cascade: options.cascade
16843
+ }));
16844
+ }
15622
16845
 
15623
16846
  // src/decorators/transformers/built-in/string-transformers.ts
15624
16847
  var TrimTransformer = class {
@@ -15804,15 +17027,12 @@ var PatternValidator = class {
15804
17027
  };
15805
17028
 
15806
17029
  // src/decorators/transformers/transformer-decorators.ts
15807
- var normalizePropertyName3 = (name) => {
15808
- if (typeof name === "symbol") {
15809
- return name.description ?? name.toString();
15810
- }
15811
- return name;
15812
- };
15813
- var registerTransformerMetadata = (context, metadata) => {
15814
- const propertyName = normalizePropertyName3(context.name);
15815
- const bag = getOrCreateMetadataBag(context);
17030
+ var registerTransformerMetadata = (targetOrValue, contextOrProperty, metadata) => {
17031
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
17032
+ targetOrValue,
17033
+ contextOrProperty,
17034
+ "Transformer"
17035
+ );
15816
17036
  let existing = bag.transformers.find((t) => t.propertyName === propertyName);
15817
17037
  if (!existing) {
15818
17038
  existing = {
@@ -15841,64 +17061,64 @@ var registerTransformerMetadata = (context, metadata) => {
15841
17061
  }
15842
17062
  };
15843
17063
  function Trim(options) {
15844
- return function(_value, context) {
15845
- registerTransformerMetadata(context, {
17064
+ return function(targetOrValue, contextOrProperty) {
17065
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15846
17066
  sanitizers: [new TrimTransformer(options)]
15847
17067
  });
15848
17068
  };
15849
17069
  }
15850
17070
  function Lower() {
15851
- return function(_value, context) {
15852
- registerTransformerMetadata(context, {
17071
+ return function(targetOrValue, contextOrProperty) {
17072
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15853
17073
  sanitizers: [new CaseTransformer("lower")]
15854
17074
  });
15855
17075
  };
15856
17076
  }
15857
17077
  function Upper() {
15858
- return function(_value, context) {
15859
- registerTransformerMetadata(context, {
17078
+ return function(targetOrValue, contextOrProperty) {
17079
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15860
17080
  sanitizers: [new CaseTransformer("upper")]
15861
17081
  });
15862
17082
  };
15863
17083
  }
15864
17084
  function Capitalize() {
15865
- return function(_value, context) {
15866
- registerTransformerMetadata(context, {
17085
+ return function(targetOrValue, contextOrProperty) {
17086
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15867
17087
  sanitizers: [new CaseTransformer("capitalize")]
15868
17088
  });
15869
17089
  };
15870
17090
  }
15871
17091
  function Title() {
15872
- return function(_value, context) {
15873
- registerTransformerMetadata(context, {
17092
+ return function(targetOrValue, contextOrProperty) {
17093
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15874
17094
  sanitizers: [new CaseTransformer("title")]
15875
17095
  });
15876
17096
  };
15877
17097
  }
15878
17098
  function Alphanumeric(options) {
15879
- return function(_value, context) {
15880
- registerTransformerMetadata(context, {
17099
+ return function(targetOrValue, contextOrProperty) {
17100
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15881
17101
  validators: [new AlphanumericValidator(options)]
15882
17102
  });
15883
17103
  };
15884
17104
  }
15885
17105
  function Email(options) {
15886
- return function(_value, context) {
15887
- registerTransformerMetadata(context, {
17106
+ return function(targetOrValue, contextOrProperty) {
17107
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15888
17108
  validators: [new EmailValidator(options)]
15889
17109
  });
15890
17110
  };
15891
17111
  }
15892
17112
  function Length(options) {
15893
- return function(_value, context) {
15894
- registerTransformerMetadata(context, {
17113
+ return function(targetOrValue, contextOrProperty) {
17114
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15895
17115
  validators: [new LengthValidator(options)]
15896
17116
  });
15897
17117
  };
15898
17118
  }
15899
17119
  function Pattern(options) {
15900
- return function(_value, context) {
15901
- registerTransformerMetadata(context, {
17120
+ return function(targetOrValue, contextOrProperty) {
17121
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
15902
17122
  validators: [new PatternValidator(options)]
15903
17123
  });
15904
17124
  };
@@ -16125,16 +17345,12 @@ var CEPValidator = class {
16125
17345
  registerValidator("BR", "cpf", () => new CPFValidator());
16126
17346
  registerValidator("BR", "cnpj", () => new CNPJValidator());
16127
17347
  registerValidator("BR", "cep", () => new CEPValidator());
16128
- var normalizePropertyName4 = (name) => {
16129
- if (typeof name === "symbol") {
16130
- return name.description ?? name.toString();
16131
- }
16132
- return name;
17348
+ var resolveCountryDecoratorInfo = (targetOrValue, contextOrProperty) => {
17349
+ return resolveFieldDecoratorInfo(targetOrValue, contextOrProperty, "Country validator");
16133
17350
  };
16134
17351
  function CPF(options) {
16135
- return function(_value, context) {
16136
- const propertyName = normalizePropertyName4(context.name);
16137
- const bag = getOrCreateMetadataBag(context);
17352
+ return function(targetOrValue, contextOrProperty) {
17353
+ const { propertyName, bag } = resolveCountryDecoratorInfo(targetOrValue, contextOrProperty);
16138
17354
  let existing = bag.transformers.find((t) => t.propertyName === propertyName);
16139
17355
  if (!existing) {
16140
17356
  existing = {
@@ -16182,9 +17398,8 @@ function CPF(options) {
16182
17398
  };
16183
17399
  }
16184
17400
  function CNPJ(options) {
16185
- return function(_value, context) {
16186
- const propertyName = normalizePropertyName4(context.name);
16187
- const bag = getOrCreateMetadataBag(context);
17401
+ return function(targetOrValue, contextOrProperty) {
17402
+ const { propertyName, bag } = resolveCountryDecoratorInfo(targetOrValue, contextOrProperty);
16188
17403
  let existing = bag.transformers.find((t) => t.propertyName === propertyName);
16189
17404
  if (!existing) {
16190
17405
  existing = {
@@ -16232,9 +17447,8 @@ function CNPJ(options) {
16232
17447
  };
16233
17448
  }
16234
17449
  function CEP(options) {
16235
- return function(_value, context) {
16236
- const propertyName = normalizePropertyName4(context.name);
16237
- const bag = getOrCreateMetadataBag(context);
17450
+ return function(targetOrValue, contextOrProperty) {
17451
+ const { propertyName, bag } = resolveCountryDecoratorInfo(targetOrValue, contextOrProperty);
16238
17452
  let existing = bag.transformers.find((t) => t.propertyName === propertyName);
16239
17453
  if (!existing) {
16240
17454
  existing = {
@@ -16490,6 +17704,14 @@ var Pool = class {
16490
17704
  };
16491
17705
 
16492
17706
  // src/core/execution/executors/postgres-executor.ts
17707
+ var SAVEPOINT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
17708
+ var sanitizeSavepointName = (name) => {
17709
+ const trimmed = name.trim();
17710
+ if (!SAVEPOINT_NAME_PATTERN.test(trimmed)) {
17711
+ throw new Error(`Invalid savepoint name: "${name}"`);
17712
+ }
17713
+ return trimmed;
17714
+ };
16493
17715
  function createPostgresExecutor(client) {
16494
17716
  return createExecutorFromQueryRunner({
16495
17717
  async query(sql, params) {
@@ -16504,11 +17726,24 @@ function createPostgresExecutor(client) {
16504
17726
  },
16505
17727
  async rollbackTransaction() {
16506
17728
  await client.query("ROLLBACK");
17729
+ },
17730
+ async savepoint(name) {
17731
+ const savepoint = sanitizeSavepointName(name);
17732
+ await client.query(`SAVEPOINT ${savepoint}`);
17733
+ },
17734
+ async releaseSavepoint(name) {
17735
+ const savepoint = sanitizeSavepointName(name);
17736
+ await client.query(`RELEASE SAVEPOINT ${savepoint}`);
17737
+ },
17738
+ async rollbackToSavepoint(name) {
17739
+ const savepoint = sanitizeSavepointName(name);
17740
+ await client.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
16507
17741
  }
16508
17742
  });
16509
17743
  }
16510
17744
 
16511
17745
  // src/core/execution/executors/mysql-executor.ts
17746
+ var SAVEPOINT_NAME_PATTERN2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
16512
17747
  var isRowObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
16513
17748
  var isRowObjectArray = (value) => Array.isArray(value) && value.every(isRowObject);
16514
17749
  var isMysqlResultHeader = (value) => isRowObject(value) && ("affectedRows" in value || "insertId" in value || "warningStatus" in value || "serverStatus" in value);
@@ -16520,6 +17755,13 @@ var headerToQueryResult = (header) => ({
16520
17755
  rowsAffected: header.affectedRows
16521
17756
  }
16522
17757
  });
17758
+ var sanitizeSavepointName2 = (name) => {
17759
+ const trimmed = name.trim();
17760
+ if (!SAVEPOINT_NAME_PATTERN2.test(trimmed)) {
17761
+ throw new Error(`Invalid savepoint name: "${name}"`);
17762
+ }
17763
+ return trimmed;
17764
+ };
16523
17765
  var normalizeMysqlResults = (rows) => {
16524
17766
  if (!Array.isArray(rows)) {
16525
17767
  return isMysqlResultHeader(rows) ? [headerToQueryResult(rows)] : [rowsToQueryResult([])];
@@ -16541,9 +17783,11 @@ var normalizeMysqlResults = (rows) => {
16541
17783
  };
16542
17784
  function createMysqlExecutor(client) {
16543
17785
  const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
17786
+ const supportsSavepoints = supportsTransactions;
16544
17787
  return {
16545
17788
  capabilities: {
16546
- transactions: supportsTransactions
17789
+ transactions: supportsTransactions,
17790
+ ...supportsSavepoints ? { savepoints: true } : {}
16547
17791
  },
16548
17792
  async executeSql(sql, params) {
16549
17793
  const [rows] = await client.query(sql, params);
@@ -16567,17 +17811,55 @@ function createMysqlExecutor(client) {
16567
17811
  }
16568
17812
  await client.rollback();
16569
17813
  },
17814
+ async savepoint(name) {
17815
+ if (!supportsSavepoints) {
17816
+ throw new Error("Savepoints are not supported by this executor");
17817
+ }
17818
+ const savepoint = sanitizeSavepointName2(name);
17819
+ await client.query(`SAVEPOINT ${savepoint}`);
17820
+ },
17821
+ async releaseSavepoint(name) {
17822
+ if (!supportsSavepoints) {
17823
+ throw new Error("Savepoints are not supported by this executor");
17824
+ }
17825
+ const savepoint = sanitizeSavepointName2(name);
17826
+ await client.query(`RELEASE SAVEPOINT ${savepoint}`);
17827
+ },
17828
+ async rollbackToSavepoint(name) {
17829
+ if (!supportsSavepoints) {
17830
+ throw new Error("Savepoints are not supported by this executor");
17831
+ }
17832
+ const savepoint = sanitizeSavepointName2(name);
17833
+ await client.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
17834
+ },
16570
17835
  async dispose() {
16571
17836
  }
16572
17837
  };
16573
17838
  }
16574
17839
 
16575
17840
  // src/core/execution/executors/sqlite-executor.ts
17841
+ var SAVEPOINT_NAME_PATTERN3 = /^[A-Za-z_][A-Za-z0-9_]*$/;
17842
+ var sanitizeSavepointName3 = (name) => {
17843
+ const trimmed = name.trim();
17844
+ if (!SAVEPOINT_NAME_PATTERN3.test(trimmed)) {
17845
+ throw new Error(`Invalid savepoint name: "${name}"`);
17846
+ }
17847
+ return trimmed;
17848
+ };
16576
17849
  function createSqliteExecutor(client) {
16577
17850
  const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commitTransaction === "function" && typeof client.rollbackTransaction === "function";
17851
+ const supportsSavepoints = supportsTransactions;
17852
+ const executeControlStatement = async (sql) => {
17853
+ if (typeof client.run === "function") {
17854
+ await client.run(sql);
17855
+ return;
17856
+ }
17857
+ await client.all(sql);
17858
+ };
16578
17859
  return {
16579
17860
  capabilities: {
16580
- transactions: supportsTransactions
17861
+ transactions: supportsTransactions,
17862
+ ...supportsSavepoints ? { savepoints: true } : {}
16581
17863
  },
16582
17864
  async executeSql(sql, params) {
16583
17865
  const rows = await client.all(sql, params);
@@ -16602,17 +17884,120 @@ function createSqliteExecutor(client) {
16602
17884
  }
16603
17885
  await client.rollbackTransaction();
16604
17886
  },
17887
+ async savepoint(name) {
17888
+ if (!supportsSavepoints) {
17889
+ throw new Error("Savepoints are not supported by this executor");
17890
+ }
17891
+ const savepoint = sanitizeSavepointName3(name);
17892
+ if (typeof client.savepoint === "function") {
17893
+ await client.savepoint(savepoint);
17894
+ return;
17895
+ }
17896
+ await executeControlStatement(`SAVEPOINT ${savepoint}`);
17897
+ },
17898
+ async releaseSavepoint(name) {
17899
+ if (!supportsSavepoints) {
17900
+ throw new Error("Savepoints are not supported by this executor");
17901
+ }
17902
+ const savepoint = sanitizeSavepointName3(name);
17903
+ if (typeof client.releaseSavepoint === "function") {
17904
+ await client.releaseSavepoint(savepoint);
17905
+ return;
17906
+ }
17907
+ await executeControlStatement(`RELEASE SAVEPOINT ${savepoint}`);
17908
+ },
17909
+ async rollbackToSavepoint(name) {
17910
+ if (!supportsSavepoints) {
17911
+ throw new Error("Savepoints are not supported by this executor");
17912
+ }
17913
+ const savepoint = sanitizeSavepointName3(name);
17914
+ if (typeof client.rollbackToSavepoint === "function") {
17915
+ await client.rollbackToSavepoint(savepoint);
17916
+ return;
17917
+ }
17918
+ await executeControlStatement(`ROLLBACK TO SAVEPOINT ${savepoint}`);
17919
+ },
17920
+ async dispose() {
17921
+ }
17922
+ };
17923
+ }
17924
+
17925
+ // src/core/execution/executors/better-sqlite3-executor.ts
17926
+ var SAVEPOINT_NAME_PATTERN4 = /^[A-Za-z_][A-Za-z0-9_]*$/;
17927
+ var sanitizeSavepointName4 = (name) => {
17928
+ const trimmed = name.trim();
17929
+ if (!SAVEPOINT_NAME_PATTERN4.test(trimmed)) {
17930
+ throw new Error(`Invalid savepoint name: "${name}"`);
17931
+ }
17932
+ return trimmed;
17933
+ };
17934
+ function createBetterSqlite3Executor(client) {
17935
+ return {
17936
+ capabilities: {
17937
+ transactions: true,
17938
+ savepoints: true
17939
+ },
17940
+ async executeSql(sql, params) {
17941
+ const stmt = client.prepare(sql);
17942
+ let result;
17943
+ if (stmt.reader) {
17944
+ const rows = stmt.all(...params ?? []);
17945
+ result = rowsToQueryResult(rows);
17946
+ } else {
17947
+ const info = stmt.run(...params ?? []);
17948
+ result = {
17949
+ columns: [],
17950
+ values: [],
17951
+ meta: {
17952
+ rowsAffected: info.changes,
17953
+ insertId: typeof info.lastInsertRowid === "bigint" ? info.lastInsertRowid.toString() : info.lastInsertRowid
17954
+ }
17955
+ };
17956
+ }
17957
+ return toExecutionPayload([result]);
17958
+ },
17959
+ async beginTransaction() {
17960
+ client.prepare("BEGIN").run();
17961
+ },
17962
+ async commitTransaction() {
17963
+ client.prepare("COMMIT").run();
17964
+ },
17965
+ async rollbackTransaction() {
17966
+ client.prepare("ROLLBACK").run();
17967
+ },
17968
+ async savepoint(name) {
17969
+ const savepoint = sanitizeSavepointName4(name);
17970
+ client.prepare(`SAVEPOINT ${savepoint}`).run();
17971
+ },
17972
+ async releaseSavepoint(name) {
17973
+ const savepoint = sanitizeSavepointName4(name);
17974
+ client.prepare(`RELEASE SAVEPOINT ${savepoint}`).run();
17975
+ },
17976
+ async rollbackToSavepoint(name) {
17977
+ const savepoint = sanitizeSavepointName4(name);
17978
+ client.prepare(`ROLLBACK TO SAVEPOINT ${savepoint}`).run();
17979
+ },
16605
17980
  async dispose() {
16606
17981
  }
16607
17982
  };
16608
17983
  }
16609
17984
 
16610
17985
  // src/core/execution/executors/mssql-executor.ts
17986
+ var SAVEPOINT_NAME_PATTERN5 = /^[A-Za-z_][A-Za-z0-9_]*$/;
17987
+ var sanitizeSavepointName5 = (name) => {
17988
+ const trimmed = name.trim();
17989
+ if (!SAVEPOINT_NAME_PATTERN5.test(trimmed)) {
17990
+ throw new Error(`Invalid savepoint name: "${name}"`);
17991
+ }
17992
+ return trimmed;
17993
+ };
16611
17994
  function createMssqlExecutor(client) {
16612
17995
  const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
17996
+ const supportsSavepoints = supportsTransactions;
16613
17997
  return {
16614
17998
  capabilities: {
16615
- transactions: supportsTransactions
17999
+ transactions: supportsTransactions,
18000
+ ...supportsSavepoints ? { savepoints: true } : {}
16616
18001
  },
16617
18002
  async executeSql(sql, params) {
16618
18003
  const { recordset, recordsets } = await client.query(sql, params);
@@ -16637,6 +18022,25 @@ function createMssqlExecutor(client) {
16637
18022
  }
16638
18023
  await client.rollback();
16639
18024
  },
18025
+ async savepoint(name) {
18026
+ if (!supportsSavepoints) {
18027
+ throw new Error("Savepoints are not supported by this executor");
18028
+ }
18029
+ const savepoint = sanitizeSavepointName5(name);
18030
+ await client.query(`SAVE TRANSACTION ${savepoint}`);
18031
+ },
18032
+ async releaseSavepoint(_name) {
18033
+ if (!supportsSavepoints) {
18034
+ throw new Error("Savepoints are not supported by this executor");
18035
+ }
18036
+ },
18037
+ async rollbackToSavepoint(name) {
18038
+ if (!supportsSavepoints) {
18039
+ throw new Error("Savepoints are not supported by this executor");
18040
+ }
18041
+ const savepoint = sanitizeSavepointName5(name);
18042
+ await client.query(`ROLLBACK TRANSACTION ${savepoint}`);
18043
+ },
16640
18044
  async dispose() {
16641
18045
  }
16642
18046
  };
@@ -16712,6 +18116,7 @@ var isQueryResult = (value) => typeof value === "object" && value !== null && Ar
16712
18116
  var isRowArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "object" && item !== null && !Array.isArray(item));
16713
18117
  function createPooledExecutorFactory(opts) {
16714
18118
  const { pool, adapter } = opts;
18119
+ const supportsSavepoints = typeof adapter.savepoint === "function" && typeof adapter.releaseSavepoint === "function" && typeof adapter.rollbackToSavepoint === "function";
16715
18120
  const makeExecutor = (mode) => {
16716
18121
  let lease = null;
16717
18122
  const getLease = async () => {
@@ -16729,8 +18134,17 @@ function createPooledExecutorFactory(opts) {
16729
18134
  }
16730
18135
  return toExecutionPayload([rowsToQueryResult(rows)]);
16731
18136
  };
18137
+ const requireActiveTransactionLease = () => {
18138
+ if (!lease) {
18139
+ throw new Error("savepoint operation called without an active transaction");
18140
+ }
18141
+ return lease;
18142
+ };
16732
18143
  return {
16733
- capabilities: { transactions: true },
18144
+ capabilities: {
18145
+ transactions: true,
18146
+ ...supportsSavepoints ? { savepoints: true } : {}
18147
+ },
16734
18148
  async executeSql(sql, params) {
16735
18149
  if (mode === "sticky") {
16736
18150
  const l2 = await getLease();
@@ -16774,6 +18188,27 @@ function createPooledExecutorFactory(opts) {
16774
18188
  await l.release();
16775
18189
  }
16776
18190
  },
18191
+ async savepoint(name) {
18192
+ if (!supportsSavepoints) {
18193
+ throw new Error("Savepoints are not supported by this executor");
18194
+ }
18195
+ const l = requireActiveTransactionLease();
18196
+ await adapter.savepoint(l.resource, name);
18197
+ },
18198
+ async releaseSavepoint(name) {
18199
+ if (!supportsSavepoints) {
18200
+ throw new Error("Savepoints are not supported by this executor");
18201
+ }
18202
+ const l = requireActiveTransactionLease();
18203
+ await adapter.releaseSavepoint(l.resource, name);
18204
+ },
18205
+ async rollbackToSavepoint(name) {
18206
+ if (!supportsSavepoints) {
18207
+ throw new Error("Savepoints are not supported by this executor");
18208
+ }
18209
+ const l = requireActiveTransactionLease();
18210
+ await adapter.rollbackToSavepoint(l.resource, name);
18211
+ },
16777
18212
  async dispose() {
16778
18213
  if (!lease) return;
16779
18214
  const l = lease;
@@ -16868,6 +18303,9 @@ var updateInclude = (qb, relationPath, updater) => {
16868
18303
  filter: segmentOptions?.filter
16869
18304
  });
16870
18305
  }
18306
+ if (!isSingleTargetRelation(relation)) {
18307
+ continue;
18308
+ }
16871
18309
  const joinForSegment = findJoinByRelationKey(state.ast.joins, relationKey);
16872
18310
  currentAlias = joinForSegment ? getExposedName(joinForSegment.table) ?? relation.target.name : relation.target.name;
16873
18311
  currentTable = relation.target;
@@ -16996,6 +18434,9 @@ function applyRelationFilter(qb, table, relationName, filter) {
16996
18434
  `Relation filter "${relationName}" must include at least one of "some", "none", "every", "isEmpty", or "isNotEmpty".`
16997
18435
  );
16998
18436
  }
18437
+ if (!isSingleTargetRelation(relation)) {
18438
+ return qb;
18439
+ }
16999
18440
  if (filter.some) {
17000
18441
  const predicate = buildFilterExpression(relation.target, filter.some);
17001
18442
  if (predicate) {
@@ -17041,6 +18482,9 @@ function applyRelationFilter(qb, table, relationName, filter) {
17041
18482
  return qb;
17042
18483
  }
17043
18484
  var buildRelationSubqueryBase = (table, relation) => {
18485
+ if (!isSingleTargetRelation(relation)) {
18486
+ throw new Error("MorphTo relations do not support subquery-based filtering");
18487
+ }
17044
18488
  const target = relation.target;
17045
18489
  if (relation.type === RelationKinds.BelongsToMany) {
17046
18490
  const many = relation;
@@ -17086,7 +18530,14 @@ var buildRelationSubqueryBase = (table, relation) => {
17086
18530
  schema: target.schema
17087
18531
  };
17088
18532
  const correlation = buildRelationCorrelation(table, relation);
17089
- const groupByColumnName = relation.type === RelationKinds.BelongsTo ? relation.localKey || findPrimaryKey(target) : relation.foreignKey;
18533
+ let groupByColumnName;
18534
+ if (relation.type === RelationKinds.BelongsTo) {
18535
+ groupByColumnName = relation.localKey || findPrimaryKey(target);
18536
+ } else if (relation.type === RelationKinds.MorphOne || relation.type === RelationKinds.MorphMany) {
18537
+ groupByColumnName = relation.idField;
18538
+ } else {
18539
+ groupByColumnName = relation.foreignKey;
18540
+ }
17090
18541
  return {
17091
18542
  from,
17092
18543
  joins: [],
@@ -17118,6 +18569,9 @@ function buildRelationFilterExpression(table, relationName, filter) {
17118
18569
  where
17119
18570
  };
17120
18571
  };
18572
+ if (!isSingleTargetRelation(relation)) {
18573
+ return null;
18574
+ }
17121
18575
  if (filter.some) {
17122
18576
  const predicate = buildFilterExpression(relation.target, filter.some);
17123
18577
  if (predicate) {
@@ -20087,6 +21541,432 @@ var TagIndex = class {
20087
21541
  };
20088
21542
  }
20089
21543
  };
21544
+
21545
+ // src/bulk/bulk-context.ts
21546
+ function createBulkExecutionContext(session) {
21547
+ const executionContext = session.getExecutionContext();
21548
+ return {
21549
+ session,
21550
+ executionContext,
21551
+ dialect: executionContext.dialect,
21552
+ supportsReturning: executionContext.dialect.supportsDmlReturningClause()
21553
+ };
21554
+ }
21555
+ async function executeCompiled(ctx, compiled) {
21556
+ const payload = await ctx.executionContext.interceptors.run(
21557
+ { sql: compiled.sql, params: compiled.params },
21558
+ ctx.executionContext.executor
21559
+ );
21560
+ return extractResultSets(payload);
21561
+ }
21562
+ function extractResultSets(payload) {
21563
+ const result = payload;
21564
+ if (result.resultSets) {
21565
+ return result.resultSets;
21566
+ }
21567
+ return [];
21568
+ }
21569
+ function flattenQueryResults(resultSets) {
21570
+ const rows = [];
21571
+ for (const rs of resultSets) {
21572
+ for (const valueRow of rs.values) {
21573
+ const obj = {};
21574
+ rs.columns.forEach((col2, idx) => {
21575
+ const bare = col2.split(".").pop().replace(/^["`[\]]+|["`[\]]+$/g, "");
21576
+ obj[bare] = valueRow[idx];
21577
+ });
21578
+ rows.push(obj);
21579
+ }
21580
+ }
21581
+ return rows;
21582
+ }
21583
+ function resolveReturningColumns(ctx, table, returning) {
21584
+ if (!returning) return void 0;
21585
+ if (!ctx.supportsReturning) return void 0;
21586
+ if (returning === true) {
21587
+ return Object.values(table.columns).map((col2) => ({
21588
+ type: "Column",
21589
+ table: table.name,
21590
+ name: col2.name,
21591
+ alias: col2.name
21592
+ }));
21593
+ }
21594
+ return returning.map((col2) => ({
21595
+ type: "Column",
21596
+ table: table.name,
21597
+ name: col2.name,
21598
+ alias: col2.name
21599
+ }));
21600
+ }
21601
+
21602
+ // src/bulk/bulk-utils.ts
21603
+ function splitIntoChunks(items, size) {
21604
+ if (size < 1) throw new RangeError(`chunkSize must be >= 1, got ${size}`);
21605
+ const chunks = [];
21606
+ for (let i = 0; i < items.length; i += size) {
21607
+ chunks.push(items.slice(i, i + size));
21608
+ }
21609
+ return chunks;
21610
+ }
21611
+ async function runWithConcurrency(tasks, concurrency) {
21612
+ const limit = concurrency === "sequential" ? 1 : Math.max(1, concurrency);
21613
+ if (limit === 1) {
21614
+ const results2 = [];
21615
+ for (const task of tasks) {
21616
+ results2.push(await task());
21617
+ }
21618
+ return results2;
21619
+ }
21620
+ const results = new Array(tasks.length);
21621
+ let nextIndex = 0;
21622
+ const worker = async () => {
21623
+ while (nextIndex < tasks.length) {
21624
+ const i = nextIndex++;
21625
+ results[i] = await tasks[i]();
21626
+ }
21627
+ };
21628
+ const workers = Array.from({ length: Math.min(limit, tasks.length) }, worker);
21629
+ await Promise.all(workers);
21630
+ return results;
21631
+ }
21632
+ async function runChunk(task, chunkIndex, totalChunks, rowsInChunk, timing, onChunkComplete) {
21633
+ const start = timing || onChunkComplete ? Date.now() : 0;
21634
+ const result = await task();
21635
+ const elapsedMs = start ? Date.now() - start : 0;
21636
+ if (onChunkComplete) {
21637
+ await onChunkComplete({ chunkIndex, totalChunks, rowsInChunk, elapsedMs });
21638
+ }
21639
+ return result;
21640
+ }
21641
+ async function maybeTransaction(session, transactional, fn8) {
21642
+ if (!transactional) return fn8();
21643
+ const ormSession = session;
21644
+ return ormSession.transaction(fn8);
21645
+ }
21646
+ function aggregateOutcomes(outcomes) {
21647
+ const result = {
21648
+ processedRows: 0,
21649
+ chunksExecuted: outcomes.length,
21650
+ returning: []
21651
+ };
21652
+ for (const o of outcomes) {
21653
+ result.processedRows += o.processedRows;
21654
+ result.returning.push(...o.returning);
21655
+ }
21656
+ return result;
21657
+ }
21658
+ function aggregateOutcomesWithTimings(outcomes) {
21659
+ const result = aggregateOutcomes(outcomes);
21660
+ result.chunkTimings = outcomes.map((o) => o.elapsedMs);
21661
+ return result;
21662
+ }
21663
+
21664
+ // src/bulk/bulk-executor.base.ts
21665
+ var DEFAULT_CHUNK_SIZE = 500;
21666
+ var BulkBaseExecutor = class {
21667
+ session;
21668
+ table;
21669
+ ctx;
21670
+ options;
21671
+ chunks;
21672
+ totalChunks;
21673
+ constructor(session, table, rows, options = {}) {
21674
+ this.session = session;
21675
+ this.table = table;
21676
+ this.ctx = createBulkExecutionContext(session);
21677
+ this.options = {
21678
+ chunkSize: DEFAULT_CHUNK_SIZE,
21679
+ concurrency: "sequential",
21680
+ transactional: true,
21681
+ timing: false,
21682
+ ...options
21683
+ };
21684
+ this.chunks = splitIntoChunks(rows, this.options.chunkSize);
21685
+ this.totalChunks = this.chunks.length;
21686
+ }
21687
+ async execute() {
21688
+ const buildTask = (chunk, chunkIndex) => async () => {
21689
+ return runChunk(
21690
+ () => this.executeChunk(chunk, chunkIndex),
21691
+ chunkIndex,
21692
+ this.totalChunks,
21693
+ chunk.length,
21694
+ this.options.timing,
21695
+ this.options.onChunkComplete
21696
+ );
21697
+ };
21698
+ const tasks = this.chunks.map((chunk, i) => buildTask(chunk, i));
21699
+ const outcomes = await maybeTransaction(
21700
+ this.session,
21701
+ this.options.transactional,
21702
+ () => runWithConcurrency(tasks, this.options.concurrency)
21703
+ );
21704
+ return this.options.timing ? aggregateOutcomesWithTimings(outcomes) : aggregateOutcomes(outcomes);
21705
+ }
21706
+ };
21707
+
21708
+ // src/bulk/bulk-insert-executor.ts
21709
+ var BulkInsertExecutor = class extends BulkBaseExecutor {
21710
+ constructor(session, table, rows, options = {}) {
21711
+ super(session, table, rows, options);
21712
+ }
21713
+ async executeChunk(chunk, chunkIndex) {
21714
+ const returningColumns = resolveReturningColumns(this.ctx, this.table, this.options.returning);
21715
+ let builder = new InsertQueryBuilder(this.table).values(chunk);
21716
+ if (this.options.onConflict) {
21717
+ const conflictColumns = this.options.onConflict.target?.columns ?? [];
21718
+ builder = builder.onConflict(conflictColumns);
21719
+ if (this.options.onConflict.action.type === "DoNothing") {
21720
+ builder = builder.doNothing();
21721
+ } else if (this.options.onConflict.action.type === "DoUpdate" && this.options.onConflict.action.set) {
21722
+ const setMap = {};
21723
+ for (const assignment of this.options.onConflict.action.set) {
21724
+ const colName = typeof assignment.column === "object" ? assignment.column.name : assignment.column;
21725
+ setMap[colName] = assignment.value;
21726
+ }
21727
+ builder = builder.doUpdate(setMap);
21728
+ }
21729
+ }
21730
+ const finalBuilder = builder;
21731
+ if (returningColumns?.length) {
21732
+ finalBuilder.returning(...returningColumns);
21733
+ }
21734
+ const compiled = finalBuilder.compile(this.ctx.dialect);
21735
+ const resultSets = await executeCompiled(this.ctx, compiled);
21736
+ return {
21737
+ processedRows: chunk.length,
21738
+ returning: returningColumns ? flattenQueryResults(resultSets) : [],
21739
+ elapsedMs: 0
21740
+ };
21741
+ }
21742
+ };
21743
+ async function bulkInsert(session, table, rows, options = {}) {
21744
+ if (!rows.length) {
21745
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21746
+ }
21747
+ const executor = new BulkInsertExecutor(session, table, rows, options);
21748
+ return executor.execute();
21749
+ }
21750
+
21751
+ // src/bulk/bulk-update-executor.ts
21752
+ function resolveByColumns(table, by) {
21753
+ if (!by) return [findPrimaryKey(table)];
21754
+ return Array.isArray(by) ? by : [by];
21755
+ }
21756
+ var BulkUpdateExecutor = class extends BulkBaseExecutor {
21757
+ byColumns;
21758
+ constructor(session, table, rows, options = {}) {
21759
+ super(session, table, rows, options);
21760
+ this.byColumns = resolveByColumns(table, options.by);
21761
+ }
21762
+ async executeChunk(chunk, chunkIndex) {
21763
+ const allReturning = [];
21764
+ const returningColumns = resolveReturningColumns(this.ctx, this.table, this.options.returning);
21765
+ const extraWhere = this.options.where;
21766
+ for (const row of chunk) {
21767
+ const predicates = this.byColumns.map((colName) => {
21768
+ const col2 = this.table.columns[colName];
21769
+ if (!col2) {
21770
+ throw new Error(
21771
+ `bulkUpdate: column "${colName}" not found in table "${this.table.name}"`
21772
+ );
21773
+ }
21774
+ const val = row[colName];
21775
+ if (val === void 0) {
21776
+ throw new Error(
21777
+ `bulkUpdate: row is missing the identity column "${colName}" required by the "by" option`
21778
+ );
21779
+ }
21780
+ return eq(col2, val);
21781
+ });
21782
+ const whereExpr = predicates.length === 1 ? predicates[0] : predicates.reduce((acc, p) => and(acc, p), predicates[0]);
21783
+ const finalWhere = extraWhere ? and(whereExpr, extraWhere) : whereExpr;
21784
+ const bySet = new Set(this.byColumns);
21785
+ const setPayload = {};
21786
+ for (const [key, val] of Object.entries(row)) {
21787
+ if (!bySet.has(key) && key in this.table.columns) {
21788
+ setPayload[key] = val;
21789
+ }
21790
+ }
21791
+ if (!Object.keys(setPayload).length) continue;
21792
+ let builder = new UpdateQueryBuilder(this.table).set(setPayload).where(finalWhere);
21793
+ if (returningColumns?.length) {
21794
+ builder = builder.returning(...returningColumns);
21795
+ }
21796
+ const compiled = builder.compile(this.ctx.dialect);
21797
+ const resultSets = await executeCompiled(this.ctx, compiled);
21798
+ if (returningColumns) {
21799
+ allReturning.push(...flattenQueryResults(resultSets));
21800
+ }
21801
+ }
21802
+ return {
21803
+ processedRows: chunk.length,
21804
+ returning: allReturning,
21805
+ elapsedMs: 0
21806
+ };
21807
+ }
21808
+ };
21809
+ async function bulkUpdate(session, table, rows, options = {}) {
21810
+ if (!rows.length) {
21811
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21812
+ }
21813
+ const executor = new BulkUpdateExecutor(session, table, rows, options);
21814
+ return executor.execute();
21815
+ }
21816
+ var DEFAULT_BULK_UPDATE_WHERE_CHUNK_SIZE = 500;
21817
+ async function bulkUpdateWhere(session, table, ids, set, options = {}) {
21818
+ if (!ids.length) {
21819
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21820
+ }
21821
+ const {
21822
+ chunkSize = DEFAULT_BULK_UPDATE_WHERE_CHUNK_SIZE,
21823
+ concurrency = "sequential",
21824
+ transactional = true,
21825
+ timing = false,
21826
+ onChunkComplete,
21827
+ by,
21828
+ where: extraWhere,
21829
+ returning
21830
+ } = options;
21831
+ const ctx = createBulkExecutionContext(session);
21832
+ const byColumnName = by ?? findPrimaryKey(table);
21833
+ const byColumn = table.columns[byColumnName];
21834
+ if (!byColumn) {
21835
+ throw new Error(
21836
+ `bulkUpdateWhere: column "${byColumnName}" not found in table "${table.name}"`
21837
+ );
21838
+ }
21839
+ const returningColumns = resolveReturningColumns(ctx, table, returning);
21840
+ const chunks = splitIntoChunks(ids, chunkSize);
21841
+ const totalChunks = chunks.length;
21842
+ const buildTask = (chunk, chunkIndex) => async () => {
21843
+ return runChunk(
21844
+ async () => {
21845
+ const inExpr = inList(byColumn, chunk);
21846
+ const finalWhere = extraWhere ? and(inExpr, extraWhere) : inExpr;
21847
+ let builder = new UpdateQueryBuilder(table).set(set).where(finalWhere);
21848
+ if (returningColumns?.length) {
21849
+ builder = builder.returning(...returningColumns);
21850
+ }
21851
+ const compiled = builder.compile(ctx.dialect);
21852
+ const resultSets = await executeCompiled(ctx, compiled);
21853
+ return {
21854
+ processedRows: chunk.length,
21855
+ returning: returningColumns ? flattenQueryResults(resultSets) : [],
21856
+ elapsedMs: 0
21857
+ };
21858
+ },
21859
+ chunkIndex,
21860
+ totalChunks,
21861
+ chunk.length,
21862
+ timing,
21863
+ onChunkComplete
21864
+ );
21865
+ };
21866
+ const tasks = chunks.map((chunk, i) => buildTask(chunk, i));
21867
+ const outcomes = await maybeTransaction(
21868
+ session,
21869
+ transactional,
21870
+ () => runWithConcurrency(tasks, concurrency)
21871
+ );
21872
+ return timing ? aggregateOutcomesWithTimings(outcomes) : aggregateOutcomes(outcomes);
21873
+ }
21874
+
21875
+ // src/bulk/bulk-delete-executor.ts
21876
+ var BulkDeleteExecutor = class extends BulkBaseExecutor {
21877
+ byColumnName;
21878
+ constructor(session, table, ids, options = {}) {
21879
+ super(session, table, ids, options);
21880
+ this.byColumnName = options.by ?? findPrimaryKey(table);
21881
+ }
21882
+ async executeChunk(chunk, chunkIndex) {
21883
+ const byColumn = this.table.columns[this.byColumnName];
21884
+ if (!byColumn) {
21885
+ throw new Error(
21886
+ `bulkDelete: column "${this.byColumnName}" not found in table "${this.table.name}"`
21887
+ );
21888
+ }
21889
+ const extraWhere = this.options.where;
21890
+ const inExpr = inList(byColumn, chunk);
21891
+ const finalWhere = extraWhere ? and(inExpr, extraWhere) : inExpr;
21892
+ const builder = new DeleteQueryBuilder(this.table).where(finalWhere);
21893
+ const compiled = builder.compile(this.ctx.dialect);
21894
+ await executeCompiled(this.ctx, compiled);
21895
+ return {
21896
+ processedRows: chunk.length,
21897
+ returning: [],
21898
+ elapsedMs: 0
21899
+ };
21900
+ }
21901
+ };
21902
+ async function bulkDelete(session, table, ids, options = {}) {
21903
+ if (!ids.length) {
21904
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21905
+ }
21906
+ const executor = new BulkDeleteExecutor(session, table, ids, options);
21907
+ return executor.execute();
21908
+ }
21909
+ async function bulkDeleteWhere(session, table, where, options = {}) {
21910
+ const { transactional = false } = options;
21911
+ const ctx = createBulkExecutionContext(session);
21912
+ const builder = new DeleteQueryBuilder(table).where(where);
21913
+ const compiled = builder.compile(ctx.dialect);
21914
+ const execute = async () => {
21915
+ await executeCompiled(ctx, compiled);
21916
+ return { processedRows: 0, chunksExecuted: 1, returning: [] };
21917
+ };
21918
+ return maybeTransaction(session, transactional, execute);
21919
+ }
21920
+
21921
+ // src/bulk/bulk-upsert-executor.ts
21922
+ var DEFAULT_CHUNK_SIZE2 = 500;
21923
+ var BulkUpsertExecutor = class extends BulkBaseExecutor {
21924
+ conflictTargetNodes;
21925
+ updateColumns;
21926
+ constructor(session, table, rows, options = {}) {
21927
+ super(session, table, rows, { ...options, chunkSize: options.chunkSize ?? DEFAULT_CHUNK_SIZE2 });
21928
+ const pkName = findPrimaryKey(table);
21929
+ const conflictTargetNames = options.conflictColumns ?? [pkName];
21930
+ this.conflictTargetNodes = conflictTargetNames.map((name) => ({
21931
+ type: "Column",
21932
+ table: table.name,
21933
+ name
21934
+ }));
21935
+ const conflictSet = new Set(conflictTargetNames);
21936
+ this.updateColumns = options.updateColumns ?? Object.keys(rows[0] ?? {}).filter((col2) => !conflictSet.has(col2) && col2 in table.columns);
21937
+ }
21938
+ async executeChunk(chunk, chunkIndex) {
21939
+ const returningColumns = resolveReturningColumns(this.ctx, this.table, this.options.returning);
21940
+ const set = {};
21941
+ for (const col2 of this.updateColumns) {
21942
+ set[col2] = { type: "ExcludedColumn", name: col2 };
21943
+ }
21944
+ let builder;
21945
+ if (this.updateColumns.length === 0) {
21946
+ builder = new InsertQueryBuilder(this.table).values(chunk).onConflict(this.conflictTargetNodes).doNothing();
21947
+ } else {
21948
+ builder = new InsertQueryBuilder(this.table).values(chunk).onConflict(this.conflictTargetNodes).doUpdate(set);
21949
+ }
21950
+ const finalBuilder = builder;
21951
+ if (returningColumns?.length) {
21952
+ finalBuilder.returning(...returningColumns);
21953
+ }
21954
+ const compiled = finalBuilder.compile(this.ctx.dialect);
21955
+ const resultSets = await executeCompiled(this.ctx, compiled);
21956
+ return {
21957
+ processedRows: chunk.length,
21958
+ returning: returningColumns ? flattenQueryResults(resultSets) : [],
21959
+ elapsedMs: 0
21960
+ };
21961
+ }
21962
+ };
21963
+ async function bulkUpsert(session, table, rows, options = {}) {
21964
+ if (!rows.length) {
21965
+ return { processedRows: 0, chunksExecuted: 0, returning: [] };
21966
+ }
21967
+ const executor = new BulkUpsertExecutor(session, table, rows, options);
21968
+ return executor.execute();
21969
+ }
20090
21970
  // Annotate the CommonJS export names for ESM import in node:
20091
21971
  0 && (module.exports = {
20092
21972
  Alphanumeric,
@@ -20096,6 +21976,10 @@ var TagIndex = class {
20096
21976
  BigIntTypeStrategy,
20097
21977
  BinaryTypeStrategy,
20098
21978
  BooleanTypeStrategy,
21979
+ BulkDeleteExecutor,
21980
+ BulkInsertExecutor,
21981
+ BulkUpdateExecutor,
21982
+ BulkUpsertExecutor,
20099
21983
  CEP,
20100
21984
  CNPJ,
20101
21985
  CPF,
@@ -20111,6 +21995,9 @@ var TagIndex = class {
20111
21995
  DefaultEntityMaterializer,
20112
21996
  DefaultHasManyCollection,
20113
21997
  DefaultManyToManyCollection,
21998
+ DefaultMorphManyCollection,
21999
+ DefaultMorphOneReference,
22000
+ DefaultMorphToReference,
20114
22001
  DefaultTypeStrategy,
20115
22002
  DeleteQueryBuilder,
20116
22003
  DomainEventBus,
@@ -20126,6 +22013,9 @@ var TagIndex = class {
20126
22013
  Length,
20127
22014
  Lower,
20128
22015
  MemoryCacheAdapter,
22016
+ MorphMany,
22017
+ MorphOne,
22018
+ MorphTo,
20129
22019
  MySqlDialect,
20130
22020
  NestedSetStrategy,
20131
22021
  Orm,
@@ -20184,6 +22074,12 @@ var TagIndex = class {
20184
22074
  bootstrapEntities,
20185
22075
  buildFilterExpression,
20186
22076
  buildScopeConditions,
22077
+ bulkDelete,
22078
+ bulkDeleteWhere,
22079
+ bulkInsert,
22080
+ bulkUpdate,
22081
+ bulkUpdateWhere,
22082
+ bulkUpsert,
20187
22083
  calculateRowDepths,
20188
22084
  calculateTotalPages,
20189
22085
  callProcedure,
@@ -20216,6 +22112,7 @@ var TagIndex = class {
20216
22112
  count,
20217
22113
  countAll,
20218
22114
  createApiComponentsSection,
22115
+ createBetterSqlite3Executor,
20219
22116
  createDeterministicNamingState,
20220
22117
  createDtoToOpenApiSchema,
20221
22118
  createEntityFromRow,
@@ -20317,12 +22214,16 @@ var TagIndex = class {
20317
22214
  isCastExpressionNode,
20318
22215
  isCollateExpressionNode,
20319
22216
  isComponentReference,
22217
+ isDistinctFrom,
20320
22218
  isExpressionSelectionNode,
20321
22219
  isFunctionNode,
22220
+ isMorphRelation,
22221
+ isNotDistinctFrom,
20322
22222
  isNotNull,
20323
22223
  isNull,
20324
22224
  isNullableColumn,
20325
22225
  isOperandNode,
22226
+ isSingleTargetRelation,
20326
22227
  isTableDef,
20327
22228
  isTreeConfig,
20328
22229
  isValidDuration,
@@ -20346,6 +22247,9 @@ var TagIndex = class {
20346
22247
  loadBelongsToRelation,
20347
22248
  loadHasManyRelation,
20348
22249
  loadHasOneRelation,
22250
+ loadMorphManyRelation,
22251
+ loadMorphOneRelation,
22252
+ loadMorphToRelation,
20349
22253
  localTime,
20350
22254
  localTimestamp,
20351
22255
  locate,
@@ -20367,11 +22271,15 @@ var TagIndex = class {
20367
22271
  minute,
20368
22272
  mod,
20369
22273
  month,
22274
+ morphMany,
22275
+ morphOne,
22276
+ morphTo,
20370
22277
  mul,
20371
22278
  neq,
20372
22279
  nestedDtoToOpenApiSchema,
20373
22280
  nestedWhereInputToOpenApiSchema,
20374
22281
  normalizeColumnType,
22282
+ not,
20375
22283
  notBetween,
20376
22284
  notExists,
20377
22285
  notInList,