metal-orm 1.0.15 → 1.0.17

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 (63) hide show
  1. package/README.md +64 -61
  2. package/dist/decorators/index.cjs +490 -175
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +1 -5
  5. package/dist/decorators/index.d.ts +1 -5
  6. package/dist/decorators/index.js +490 -175
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +1044 -483
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +67 -15
  11. package/dist/index.d.ts +67 -15
  12. package/dist/index.js +1033 -482
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-Bkv8g8u_.d.cts → select-BPCn6MOH.d.cts} +486 -32
  15. package/dist/{select-Bkv8g8u_.d.ts → select-BPCn6MOH.d.ts} +486 -32
  16. package/package.json +2 -1
  17. package/src/codegen/naming-strategy.ts +64 -0
  18. package/src/codegen/typescript.ts +48 -53
  19. package/src/core/ast/aggregate-functions.ts +50 -4
  20. package/src/core/ast/expression-builders.ts +22 -15
  21. package/src/core/ast/expression-nodes.ts +6 -0
  22. package/src/core/ddl/introspect/functions/postgres.ts +2 -6
  23. package/src/core/ddl/schema-generator.ts +3 -2
  24. package/src/core/ddl/schema-introspect.ts +1 -1
  25. package/src/core/dialect/abstract.ts +40 -8
  26. package/src/core/dialect/mssql/functions.ts +24 -15
  27. package/src/core/dialect/postgres/functions.ts +33 -24
  28. package/src/core/dialect/sqlite/functions.ts +19 -12
  29. package/src/core/functions/datetime.ts +2 -1
  30. package/src/core/functions/numeric.ts +2 -1
  31. package/src/core/functions/standard-strategy.ts +52 -12
  32. package/src/core/functions/text.ts +2 -1
  33. package/src/core/functions/types.ts +8 -8
  34. package/src/decorators/column.ts +13 -4
  35. package/src/index.ts +13 -5
  36. package/src/orm/domain-event-bus.ts +43 -25
  37. package/src/orm/entity-context.ts +30 -0
  38. package/src/orm/entity-meta.ts +42 -2
  39. package/src/orm/entity-metadata.ts +1 -6
  40. package/src/orm/entity.ts +88 -88
  41. package/src/orm/execute.ts +42 -25
  42. package/src/orm/execution-context.ts +18 -0
  43. package/src/orm/hydration-context.ts +16 -0
  44. package/src/orm/identity-map.ts +4 -0
  45. package/src/orm/interceptor-pipeline.ts +29 -0
  46. package/src/orm/lazy-batch.ts +6 -6
  47. package/src/orm/orm-session.ts +245 -0
  48. package/src/orm/orm.ts +58 -0
  49. package/src/orm/query-logger.ts +15 -0
  50. package/src/orm/relation-change-processor.ts +5 -1
  51. package/src/orm/relations/belongs-to.ts +45 -44
  52. package/src/orm/relations/has-many.ts +44 -43
  53. package/src/orm/relations/has-one.ts +140 -139
  54. package/src/orm/relations/many-to-many.ts +46 -45
  55. package/src/orm/runtime-types.ts +60 -2
  56. package/src/orm/transaction-runner.ts +7 -0
  57. package/src/orm/unit-of-work.ts +7 -1
  58. package/src/query-builder/insert-query-state.ts +13 -3
  59. package/src/query-builder/select-helpers.ts +50 -0
  60. package/src/query-builder/select.ts +616 -18
  61. package/src/query-builder/update-query-state.ts +31 -9
  62. package/src/schema/types.ts +16 -6
  63. package/src/orm/orm-context.ts +0 -159
package/dist/index.cjs CHANGED
@@ -48,10 +48,12 @@ __export(index_exports, {
48
48
  DefaultHasManyCollection: () => DefaultHasManyCollection,
49
49
  DefaultManyToManyCollection: () => DefaultManyToManyCollection,
50
50
  DeleteQueryBuilder: () => DeleteQueryBuilder,
51
+ DomainEventBus: () => DomainEventBus,
51
52
  EntityStatus: () => EntityStatus,
52
53
  InsertQueryBuilder: () => InsertQueryBuilder,
53
54
  MySqlDialect: () => MySqlDialect,
54
- OrmContext: () => OrmContext,
55
+ Orm: () => Orm,
56
+ OrmSession: () => OrmSession,
55
57
  PostgresDialect: () => PostgresDialect,
56
58
  RelationKinds: () => RelationKinds,
57
59
  SelectQueryBuilder: () => SelectQueryBuilder,
@@ -93,6 +95,7 @@ __export(index_exports, {
93
95
  createMssqlExecutor: () => createMssqlExecutor,
94
96
  createMysqlExecutor: () => createMysqlExecutor,
95
97
  createPostgresExecutor: () => createPostgresExecutor,
98
+ createQueryLoggingExecutor: () => createQueryLoggingExecutor,
96
99
  createSqliteExecutor: () => createSqliteExecutor,
97
100
  currentDate: () => currentDate,
98
101
  currentTime: () => currentTime,
@@ -109,7 +112,9 @@ __export(index_exports, {
109
112
  diffSchema: () => diffSchema,
110
113
  endOfMonth: () => endOfMonth,
111
114
  eq: () => eq,
115
+ esel: () => esel,
112
116
  executeHydrated: () => executeHydrated,
117
+ executeHydratedWithContexts: () => executeHydratedWithContexts,
113
118
  exists: () => exists,
114
119
  exp: () => exp,
115
120
  extract: () => extract,
@@ -119,6 +124,7 @@ __export(index_exports, {
119
124
  generateCreateTableSql: () => generateCreateTableSql,
120
125
  generateSchemaSql: () => generateSchemaSql,
121
126
  getSchemaIntrospector: () => getSchemaIntrospector,
127
+ groupConcat: () => groupConcat,
122
128
  gt: () => gt,
123
129
  gte: () => gte,
124
130
  hasMany: () => hasMany,
@@ -133,6 +139,7 @@ __export(index_exports, {
133
139
  isNotNull: () => isNotNull,
134
140
  isNull: () => isNull,
135
141
  isOperandNode: () => isOperandNode,
142
+ isValueOperandInput: () => isValueOperandInput,
136
143
  isWindowFunctionNode: () => isWindowFunctionNode,
137
144
  jsonPath: () => jsonPath,
138
145
  lag: () => lag,
@@ -155,6 +162,8 @@ __export(index_exports, {
155
162
  lt: () => lt,
156
163
  lte: () => lte,
157
164
  ltrim: () => ltrim,
165
+ max: () => max,
166
+ min: () => min,
158
167
  mod: () => mod,
159
168
  month: () => month,
160
169
  neq: () => neq,
@@ -185,6 +194,7 @@ __export(index_exports, {
185
194
  rowsToQueryResult: () => rowsToQueryResult,
186
195
  rpad: () => rpad,
187
196
  rtrim: () => rtrim,
197
+ sel: () => sel,
188
198
  sign: () => sign,
189
199
  sin: () => sin,
190
200
  space: () => space,
@@ -418,10 +428,13 @@ var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressi
418
428
 
419
429
  // src/core/ast/expression-builders.ts
420
430
  var valueToOperand = (value) => {
421
- if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
422
- return { type: "Literal", value: value === void 0 ? null : value };
431
+ if (isOperandNode(value)) {
432
+ return value;
423
433
  }
424
- return value;
434
+ return {
435
+ type: "Literal",
436
+ value
437
+ };
425
438
  };
426
439
  var toNode = (col2) => {
427
440
  if (isOperandNode(col2)) return col2;
@@ -432,10 +445,11 @@ var toLiteralNode = (value) => ({
432
445
  type: "Literal",
433
446
  value
434
447
  });
448
+ var isLiteralValue = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
449
+ var isValueOperandInput = (value) => isOperandNode(value) || isLiteralValue(value);
435
450
  var toOperand = (val) => {
436
- if (val === null) return { type: "Literal", value: null };
437
- if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
438
- return { type: "Literal", value: val };
451
+ if (isLiteralValue(val)) {
452
+ return valueToOperand(val);
439
453
  }
440
454
  return toNode(val);
441
455
  };
@@ -581,6 +595,62 @@ var windowFunction = (name, args = [], partitionBy, orderBy) => {
581
595
  return buildWindowFunction(name, nodeArgs, partitionNodes, orderNodes);
582
596
  };
583
597
 
598
+ // src/core/sql/sql.ts
599
+ var SQL_OPERATORS = {
600
+ /** Equality operator */
601
+ EQUALS: "=",
602
+ /** Not equals operator */
603
+ NOT_EQUALS: "!=",
604
+ /** Greater than operator */
605
+ GREATER_THAN: ">",
606
+ /** Greater than or equal operator */
607
+ GREATER_OR_EQUAL: ">=",
608
+ /** Less than operator */
609
+ LESS_THAN: "<",
610
+ /** Less than or equal operator */
611
+ LESS_OR_EQUAL: "<=",
612
+ /** LIKE pattern matching operator */
613
+ LIKE: "LIKE",
614
+ /** NOT LIKE pattern matching operator */
615
+ NOT_LIKE: "NOT LIKE",
616
+ /** IN membership operator */
617
+ IN: "IN",
618
+ /** NOT IN membership operator */
619
+ NOT_IN: "NOT IN",
620
+ /** BETWEEN range operator */
621
+ BETWEEN: "BETWEEN",
622
+ /** NOT BETWEEN range operator */
623
+ NOT_BETWEEN: "NOT BETWEEN",
624
+ /** IS NULL null check operator */
625
+ IS_NULL: "IS NULL",
626
+ /** IS NOT NULL null check operator */
627
+ IS_NOT_NULL: "IS NOT NULL",
628
+ /** Logical AND operator */
629
+ AND: "AND",
630
+ /** Logical OR operator */
631
+ OR: "OR",
632
+ /** EXISTS operator */
633
+ EXISTS: "EXISTS",
634
+ /** NOT EXISTS operator */
635
+ NOT_EXISTS: "NOT EXISTS"
636
+ };
637
+ var JOIN_KINDS = {
638
+ /** INNER JOIN type */
639
+ INNER: "INNER",
640
+ /** LEFT JOIN type */
641
+ LEFT: "LEFT",
642
+ /** RIGHT JOIN type */
643
+ RIGHT: "RIGHT",
644
+ /** CROSS JOIN type */
645
+ CROSS: "CROSS"
646
+ };
647
+ var ORDER_DIRECTIONS = {
648
+ /** Ascending order */
649
+ ASC: "ASC",
650
+ /** Descending order */
651
+ DESC: "DESC"
652
+ };
653
+
584
654
  // src/core/ast/aggregate-functions.ts
585
655
  var buildAggregate = (name) => (col2) => ({
586
656
  type: "Function",
@@ -590,6 +660,20 @@ var buildAggregate = (name) => (col2) => ({
590
660
  var count = buildAggregate("COUNT");
591
661
  var sum = buildAggregate("SUM");
592
662
  var avg = buildAggregate("AVG");
663
+ var min = buildAggregate("MIN");
664
+ var max = buildAggregate("MAX");
665
+ var toOrderByNode = (order) => ({
666
+ type: "OrderBy",
667
+ column: columnOperand(order.column),
668
+ direction: order.direction ?? ORDER_DIRECTIONS.ASC
669
+ });
670
+ var groupConcat = (col2, options) => ({
671
+ type: "Function",
672
+ name: "GROUP_CONCAT",
673
+ args: [columnOperand(col2)],
674
+ orderBy: options?.orderBy?.map(toOrderByNode),
675
+ separator: options?.separator !== void 0 ? valueToOperand(options.separator) : void 0
676
+ });
593
677
 
594
678
  // src/core/ast/expression-visitor.ts
595
679
  var expressionDispatchers = /* @__PURE__ */ new Map();
@@ -681,12 +765,17 @@ var toTableRef = (table) => ({
681
765
  });
682
766
 
683
767
  // src/core/functions/standard-strategy.ts
684
- var StandardFunctionStrategy = class {
768
+ var StandardFunctionStrategy = class _StandardFunctionStrategy {
685
769
  constructor() {
686
770
  this.renderers = /* @__PURE__ */ new Map();
687
771
  this.registerStandard();
688
772
  }
689
773
  registerStandard() {
774
+ this.add("COUNT", ({ compiledArgs }) => `COUNT(${compiledArgs.join(", ")})`);
775
+ this.add("SUM", ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
776
+ this.add("AVG", ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
777
+ this.add("MIN", ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
778
+ this.add("MAX", ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
690
779
  this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
691
780
  this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
692
781
  this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
@@ -713,6 +802,7 @@ var StandardFunctionStrategy = class {
713
802
  this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
714
803
  this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
715
804
  this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
805
+ this.add("GROUP_CONCAT", (ctx) => this.renderGroupConcat(ctx));
716
806
  }
717
807
  add(name, renderer) {
718
808
  this.renderers.set(name, renderer);
@@ -720,10 +810,40 @@ var StandardFunctionStrategy = class {
720
810
  getRenderer(name) {
721
811
  return this.renderers.get(name);
722
812
  }
813
+ renderGroupConcat(ctx) {
814
+ const arg = ctx.compiledArgs[0];
815
+ const orderClause = this.buildOrderByExpression(ctx);
816
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
817
+ const separatorClause = this.formatGroupConcatSeparator(ctx);
818
+ return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
819
+ }
820
+ buildOrderByExpression(ctx) {
821
+ const orderBy = ctx.node.orderBy;
822
+ if (!orderBy || orderBy.length === 0) {
823
+ return "";
824
+ }
825
+ const parts = orderBy.map((order) => `${ctx.compileOperand(order.column)} ${order.direction}`);
826
+ return `ORDER BY ${parts.join(", ")}`;
827
+ }
828
+ formatGroupConcatSeparator(ctx) {
829
+ if (!ctx.node.separator) {
830
+ return "";
831
+ }
832
+ return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
833
+ }
834
+ getGroupConcatSeparatorOperand(ctx) {
835
+ return ctx.node.separator ?? _StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
836
+ }
837
+ static {
838
+ this.DEFAULT_GROUP_CONCAT_SEPARATOR = {
839
+ type: "Literal",
840
+ value: ","
841
+ };
842
+ }
723
843
  };
724
844
 
725
845
  // src/core/dialect/abstract.ts
726
- var Dialect = class {
846
+ var Dialect = class _Dialect {
727
847
  /**
728
848
  * Compiles a SELECT query AST to SQL
729
849
  * @param ast - Query AST to compile
@@ -896,6 +1016,35 @@ var Dialect = class {
896
1016
  this.registerDefaultOperandCompilers();
897
1017
  this.registerDefaultExpressionCompilers();
898
1018
  }
1019
+ /**
1020
+ * Creates a new Dialect instance (for testing purposes)
1021
+ * @param functionStrategy - Optional function strategy
1022
+ * @returns New Dialect instance
1023
+ */
1024
+ static create(functionStrategy) {
1025
+ class TestDialect extends _Dialect {
1026
+ constructor() {
1027
+ super(...arguments);
1028
+ this.dialect = "sqlite";
1029
+ }
1030
+ quoteIdentifier(id) {
1031
+ return `"${id}"`;
1032
+ }
1033
+ compileSelectAst() {
1034
+ throw new Error("Not implemented");
1035
+ }
1036
+ compileInsertAst() {
1037
+ throw new Error("Not implemented");
1038
+ }
1039
+ compileUpdateAst() {
1040
+ throw new Error("Not implemented");
1041
+ }
1042
+ compileDeleteAst() {
1043
+ throw new Error("Not implemented");
1044
+ }
1045
+ }
1046
+ return new TestDialect(functionStrategy);
1047
+ }
899
1048
  /**
900
1049
  * Registers an expression compiler for a specific node type
901
1050
  * @param type - Expression node type
@@ -1037,7 +1186,11 @@ var Dialect = class {
1037
1186
  const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
1038
1187
  const renderer = this.functionStrategy.getRenderer(fnNode.name);
1039
1188
  if (renderer) {
1040
- return renderer({ node: fnNode, compiledArgs });
1189
+ return renderer({
1190
+ node: fnNode,
1191
+ compiledArgs,
1192
+ compileOperand: (operand) => this.compileOperand(operand, ctx)
1193
+ });
1041
1194
  }
1042
1195
  return `${fnNode.name}(${compiledArgs.join(", ")})`;
1043
1196
  }
@@ -1460,6 +1613,14 @@ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1460
1613
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1461
1614
  return `DATE_TRUNC('${partClean}', ${date})`;
1462
1615
  });
1616
+ this.add("GROUP_CONCAT", (ctx) => {
1617
+ const arg = ctx.compiledArgs[0];
1618
+ const orderClause = this.buildOrderByExpression(ctx);
1619
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
1620
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1621
+ const separator = ctx.compileOperand(separatorOperand);
1622
+ return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
1623
+ });
1463
1624
  }
1464
1625
  };
1465
1626
 
@@ -1704,6 +1865,12 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1704
1865
  }
1705
1866
  return `date(${date}, 'start of ${partClean}')`;
1706
1867
  });
1868
+ this.add("GROUP_CONCAT", (ctx) => {
1869
+ const arg = ctx.compiledArgs[0];
1870
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1871
+ const separator = ctx.compileOperand(separatorOperand);
1872
+ return `GROUP_CONCAT(${arg}, ${separator})`;
1873
+ });
1707
1874
  }
1708
1875
  };
1709
1876
 
@@ -1820,6 +1987,14 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1820
1987
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1821
1988
  return `DATETRUNC(${partClean}, ${date})`;
1822
1989
  });
1990
+ this.add("GROUP_CONCAT", (ctx) => {
1991
+ const arg = ctx.compiledArgs[0];
1992
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1993
+ const separator = ctx.compileOperand(separatorOperand);
1994
+ const orderClause = this.buildOrderByExpression(ctx);
1995
+ const withinGroup = orderClause ? ` WITHIN GROUP (${orderClause})` : "";
1996
+ return `STRING_AGG(${arg}, ${separator})${withinGroup}`;
1997
+ });
1823
1998
  }
1824
1999
  };
1825
2000
 
@@ -2189,62 +2364,6 @@ var createJoinNode = (kind, tableName, condition, relationName) => ({
2189
2364
  meta: relationName ? { relationName } : void 0
2190
2365
  });
2191
2366
 
2192
- // src/core/sql/sql.ts
2193
- var SQL_OPERATORS = {
2194
- /** Equality operator */
2195
- EQUALS: "=",
2196
- /** Not equals operator */
2197
- NOT_EQUALS: "!=",
2198
- /** Greater than operator */
2199
- GREATER_THAN: ">",
2200
- /** Greater than or equal operator */
2201
- GREATER_OR_EQUAL: ">=",
2202
- /** Less than operator */
2203
- LESS_THAN: "<",
2204
- /** Less than or equal operator */
2205
- LESS_OR_EQUAL: "<=",
2206
- /** LIKE pattern matching operator */
2207
- LIKE: "LIKE",
2208
- /** NOT LIKE pattern matching operator */
2209
- NOT_LIKE: "NOT LIKE",
2210
- /** IN membership operator */
2211
- IN: "IN",
2212
- /** NOT IN membership operator */
2213
- NOT_IN: "NOT IN",
2214
- /** BETWEEN range operator */
2215
- BETWEEN: "BETWEEN",
2216
- /** NOT BETWEEN range operator */
2217
- NOT_BETWEEN: "NOT BETWEEN",
2218
- /** IS NULL null check operator */
2219
- IS_NULL: "IS NULL",
2220
- /** IS NOT NULL null check operator */
2221
- IS_NOT_NULL: "IS NOT NULL",
2222
- /** Logical AND operator */
2223
- AND: "AND",
2224
- /** Logical OR operator */
2225
- OR: "OR",
2226
- /** EXISTS operator */
2227
- EXISTS: "EXISTS",
2228
- /** NOT EXISTS operator */
2229
- NOT_EXISTS: "NOT EXISTS"
2230
- };
2231
- var JOIN_KINDS = {
2232
- /** INNER JOIN type */
2233
- INNER: "INNER",
2234
- /** LEFT JOIN type */
2235
- LEFT: "LEFT",
2236
- /** RIGHT JOIN type */
2237
- RIGHT: "RIGHT",
2238
- /** CROSS JOIN type */
2239
- CROSS: "CROSS"
2240
- };
2241
- var ORDER_DIRECTIONS = {
2242
- /** Ascending order */
2243
- ASC: "ASC",
2244
- /** Descending order */
2245
- DESC: "DESC"
2246
- };
2247
-
2248
2367
  // src/query-builder/hydration-manager.ts
2249
2368
  var HydrationManager = class _HydrationManager {
2250
2369
  /**
@@ -4288,31 +4407,43 @@ var flattenResults = (results) => {
4288
4407
  }
4289
4408
  return rows;
4290
4409
  };
4291
- async function executeHydrated(ctx, qb) {
4410
+ var executeWithEntityContext = async (entityCtx, qb) => {
4292
4411
  const ast = qb.getAST();
4293
- const compiled = ctx.dialect.compileSelect(ast);
4294
- const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
4412
+ const compiled = entityCtx.dialect.compileSelect(ast);
4413
+ const executed = await entityCtx.executor.executeSql(compiled.sql, compiled.params);
4295
4414
  const rows = flattenResults(executed);
4296
4415
  if (ast.setOps && ast.setOps.length > 0) {
4297
- return rows.map(
4298
- (row) => createEntityProxy(ctx, qb.getTable(), row, qb.getLazyRelations())
4299
- );
4416
+ return rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
4300
4417
  }
4301
4418
  const hydrated = hydrateRows(rows, qb.getHydrationPlan());
4302
- return hydrated.map(
4303
- (row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
4304
- );
4419
+ return hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
4420
+ };
4421
+ async function executeHydrated(session, qb) {
4422
+ return executeWithEntityContext(session, qb);
4423
+ }
4424
+ async function executeHydratedWithContexts(_execCtx, hydCtx, qb) {
4425
+ const entityCtx = hydCtx.entityContext;
4426
+ if (!entityCtx) {
4427
+ throw new Error("Hydration context is missing an EntityContext");
4428
+ }
4429
+ return executeWithEntityContext(entityCtx, qb);
4305
4430
  }
4306
4431
 
4307
4432
  // src/query-builder/select.ts
4308
4433
  var SelectQueryBuilder = class _SelectQueryBuilder {
4309
4434
  /**
4310
- * Creates a new SelectQueryBuilder instance
4311
- * @param table - Table definition to query
4312
- * @param state - Optional initial query state
4313
- * @param hydration - Optional hydration manager
4314
- * @param dependencies - Optional query builder dependencies
4315
- */
4435
+
4436
+ * Creates a new SelectQueryBuilder instance
4437
+
4438
+ * @param table - Table definition to query
4439
+
4440
+ * @param state - Optional initial query state
4441
+
4442
+ * @param hydration - Optional hydration manager
4443
+
4444
+ * @param dependencies - Optional query builder dependencies
4445
+
4446
+ */
4316
4447
  constructor(table, state, hydration, dependencies, lazyRelations) {
4317
4448
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
4318
4449
  this.env = { table, deps };
@@ -4349,112 +4480,183 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4349
4480
  return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
4350
4481
  }
4351
4482
  /**
4352
- * Selects specific columns for the query
4353
- * @param columns - Record of column definitions, function nodes, case expressions, or window functions
4354
- * @returns New query builder instance with selected columns
4355
- */
4483
+
4484
+ * Selects specific columns for the query
4485
+
4486
+ * @param columns - Record of column definitions, function nodes, case expressions, or window functions
4487
+
4488
+ * @returns New query builder instance with selected columns
4489
+
4490
+ */
4356
4491
  select(columns) {
4357
4492
  return this.clone(this.columnSelector.select(this.context, columns));
4358
4493
  }
4359
4494
  /**
4360
- * Selects raw column expressions
4361
- * @param cols - Column expressions as strings
4362
- * @returns New query builder instance with raw column selections
4495
+ * Selects columns from the root table by name (typed).
4496
+ * @param cols - Column names on the root table
4363
4497
  */
4498
+ selectColumns(...cols) {
4499
+ const selection = {};
4500
+ for (const key of cols) {
4501
+ const col2 = this.env.table.columns[key];
4502
+ if (!col2) {
4503
+ throw new Error(`Column '${key}' not found on table '${this.env.table.name}'`);
4504
+ }
4505
+ selection[key] = col2;
4506
+ }
4507
+ return this.select(selection);
4508
+ }
4509
+ /**
4510
+
4511
+ * Selects raw column expressions
4512
+
4513
+ * @param cols - Column expressions as strings
4514
+
4515
+ * @returns New query builder instance with raw column selections
4516
+
4517
+ */
4364
4518
  selectRaw(...cols) {
4365
4519
  return this.clone(this.columnSelector.selectRaw(this.context, cols));
4366
4520
  }
4367
4521
  /**
4368
- * Adds a Common Table Expression (CTE) to the query
4369
- * @param name - Name of the CTE
4370
- * @param query - Query builder or query node for the CTE
4371
- * @param columns - Optional column names for the CTE
4372
- * @returns New query builder instance with the CTE
4373
- */
4522
+
4523
+ * Adds a Common Table Expression (CTE) to the query
4524
+
4525
+ * @param name - Name of the CTE
4526
+
4527
+ * @param query - Query builder or query node for the CTE
4528
+
4529
+ * @param columns - Optional column names for the CTE
4530
+
4531
+ * @returns New query builder instance with the CTE
4532
+
4533
+ */
4374
4534
  with(name, query, columns) {
4375
4535
  const subAst = this.resolveQueryNode(query);
4376
4536
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, false));
4377
4537
  return this.clone(nextContext);
4378
4538
  }
4379
4539
  /**
4380
- * Adds a recursive Common Table Expression (CTE) to the query
4381
- * @param name - Name of the CTE
4382
- * @param query - Query builder or query node for the CTE
4383
- * @param columns - Optional column names for the CTE
4384
- * @returns New query builder instance with the recursive CTE
4385
- */
4540
+
4541
+ * Adds a recursive Common Table Expression (CTE) to the query
4542
+
4543
+ * @param name - Name of the CTE
4544
+
4545
+ * @param query - Query builder or query node for the CTE
4546
+
4547
+ * @param columns - Optional column names for the CTE
4548
+
4549
+ * @returns New query builder instance with the recursive CTE
4550
+
4551
+ */
4386
4552
  withRecursive(name, query, columns) {
4387
4553
  const subAst = this.resolveQueryNode(query);
4388
4554
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
4389
4555
  return this.clone(nextContext);
4390
4556
  }
4391
4557
  /**
4392
- * Selects a subquery as a column
4393
- * @param alias - Alias for the subquery column
4394
- * @param sub - Query builder or query node for the subquery
4395
- * @returns New query builder instance with the subquery selection
4396
- */
4558
+
4559
+ * Selects a subquery as a column
4560
+
4561
+ * @param alias - Alias for the subquery column
4562
+
4563
+ * @param sub - Query builder or query node for the subquery
4564
+
4565
+ * @returns New query builder instance with the subquery selection
4566
+
4567
+ */
4397
4568
  selectSubquery(alias, sub) {
4398
4569
  const query = this.resolveQueryNode(sub);
4399
4570
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
4400
4571
  }
4401
4572
  /**
4402
- * Adds an INNER JOIN to the query
4403
- * @param table - Table to join
4404
- * @param condition - Join condition expression
4405
- * @returns New query builder instance with the INNER JOIN
4406
- */
4573
+
4574
+ * Adds an INNER JOIN to the query
4575
+
4576
+ * @param table - Table to join
4577
+
4578
+ * @param condition - Join condition expression
4579
+
4580
+ * @returns New query builder instance with the INNER JOIN
4581
+
4582
+ */
4407
4583
  innerJoin(table, condition) {
4408
4584
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
4409
4585
  return this.clone(nextContext);
4410
4586
  }
4411
4587
  /**
4412
- * Adds a LEFT JOIN to the query
4413
- * @param table - Table to join
4414
- * @param condition - Join condition expression
4415
- * @returns New query builder instance with the LEFT JOIN
4416
- */
4588
+
4589
+ * Adds a LEFT JOIN to the query
4590
+
4591
+ * @param table - Table to join
4592
+
4593
+ * @param condition - Join condition expression
4594
+
4595
+ * @returns New query builder instance with the LEFT JOIN
4596
+
4597
+ */
4417
4598
  leftJoin(table, condition) {
4418
4599
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
4419
4600
  return this.clone(nextContext);
4420
4601
  }
4421
4602
  /**
4422
- * Adds a RIGHT JOIN to the query
4423
- * @param table - Table to join
4424
- * @param condition - Join condition expression
4425
- * @returns New query builder instance with the RIGHT JOIN
4426
- */
4603
+
4604
+ * Adds a RIGHT JOIN to the query
4605
+
4606
+ * @param table - Table to join
4607
+
4608
+ * @param condition - Join condition expression
4609
+
4610
+ * @returns New query builder instance with the RIGHT JOIN
4611
+
4612
+ */
4427
4613
  rightJoin(table, condition) {
4428
4614
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
4429
4615
  return this.clone(nextContext);
4430
4616
  }
4431
4617
  /**
4432
- * Matches records based on a relationship
4433
- * @param relationName - Name of the relationship to match
4434
- * @param predicate - Optional predicate expression
4435
- * @returns New query builder instance with the relationship match
4436
- */
4618
+
4619
+ * Matches records based on a relationship
4620
+
4621
+ * @param relationName - Name of the relationship to match
4622
+
4623
+ * @param predicate - Optional predicate expression
4624
+
4625
+ * @returns New query builder instance with the relationship match
4626
+
4627
+ */
4437
4628
  match(relationName, predicate) {
4438
4629
  const nextContext = this.relationManager.match(this.context, relationName, predicate);
4439
4630
  return this.clone(nextContext);
4440
4631
  }
4441
4632
  /**
4442
- * Joins a related table
4443
- * @param relationName - Name of the relationship to join
4444
- * @param joinKind - Type of join (defaults to INNER)
4445
- * @param extraCondition - Optional additional join condition
4446
- * @returns New query builder instance with the relationship join
4447
- */
4633
+
4634
+ * Joins a related table
4635
+
4636
+ * @param relationName - Name of the relationship to join
4637
+
4638
+ * @param joinKind - Type of join (defaults to INNER)
4639
+
4640
+ * @param extraCondition - Optional additional join condition
4641
+
4642
+ * @returns New query builder instance with the relationship join
4643
+
4644
+ */
4448
4645
  joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
4449
4646
  const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
4450
4647
  return this.clone(nextContext);
4451
4648
  }
4452
4649
  /**
4453
- * Includes related data in the query results
4454
- * @param relationName - Name of the relationship to include
4455
- * @param options - Optional include options
4456
- * @returns New query builder instance with the relationship inclusion
4457
- */
4650
+
4651
+ * Includes related data in the query results
4652
+
4653
+ * @param relationName - Name of the relationship to include
4654
+
4655
+ * @param options - Optional include options
4656
+
4657
+ * @returns New query builder instance with the relationship inclusion
4658
+
4659
+ */
4458
4660
  include(relationName, options) {
4459
4661
  const nextContext = this.relationManager.include(this.context, relationName, options);
4460
4662
  return this.clone(nextContext);
@@ -4464,6 +4666,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4464
4666
  nextLazy.add(relationName);
4465
4667
  return this.clone(this.context, nextLazy);
4466
4668
  }
4669
+ /**
4670
+ * Selects columns for a related table in a single hop.
4671
+ */
4672
+ selectRelationColumns(relationName, ...cols) {
4673
+ const relation = this.env.table.relations[relationName];
4674
+ if (!relation) {
4675
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4676
+ }
4677
+ const target = relation.target;
4678
+ for (const col2 of cols) {
4679
+ if (!target.columns[col2]) {
4680
+ throw new Error(
4681
+ `Column '${col2}' not found on related table '${target.name}' for relation '${relationName}'`
4682
+ );
4683
+ }
4684
+ }
4685
+ return this.include(relationName, { columns: cols });
4686
+ }
4687
+ /**
4688
+ * Convenience alias for selecting specific columns from a relation.
4689
+ */
4690
+ includePick(relationName, cols) {
4691
+ return this.selectRelationColumns(relationName, ...cols);
4692
+ }
4693
+ /**
4694
+ * Selects columns for the root table and relations from a single config object.
4695
+ */
4696
+ selectColumnsDeep(config) {
4697
+ let qb = this;
4698
+ if (config.root?.length) {
4699
+ qb = qb.selectColumns(...config.root);
4700
+ }
4701
+ for (const key of Object.keys(config)) {
4702
+ if (key === "root") continue;
4703
+ const relName = key;
4704
+ const cols = config[relName];
4705
+ if (!cols || !cols.length) continue;
4706
+ qb = qb.selectRelationColumns(relName, ...cols);
4707
+ }
4708
+ return qb;
4709
+ }
4467
4710
  getLazyRelations() {
4468
4711
  return Array.from(this.lazyRelations);
4469
4712
  }
@@ -4473,125 +4716,186 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4473
4716
  async execute(ctx) {
4474
4717
  return executeHydrated(ctx, this);
4475
4718
  }
4719
+ async executeWithContexts(execCtx, hydCtx) {
4720
+ return executeHydratedWithContexts(execCtx, hydCtx, this);
4721
+ }
4476
4722
  /**
4477
- * Adds a WHERE condition to the query
4478
- * @param expr - Expression for the WHERE clause
4479
- * @returns New query builder instance with the WHERE condition
4480
- */
4723
+
4724
+ * Adds a WHERE condition to the query
4725
+
4726
+ * @param expr - Expression for the WHERE clause
4727
+
4728
+ * @returns New query builder instance with the WHERE condition
4729
+
4730
+ */
4481
4731
  where(expr) {
4482
4732
  const nextContext = this.applyAst(this.context, (service) => service.withWhere(expr));
4483
4733
  return this.clone(nextContext);
4484
4734
  }
4485
4735
  /**
4486
- * Adds a GROUP BY clause to the query
4487
- * @param col - Column definition or column node to group by
4488
- * @returns New query builder instance with the GROUP BY clause
4489
- */
4736
+
4737
+ * Adds a GROUP BY clause to the query
4738
+
4739
+ * @param col - Column definition or column node to group by
4740
+
4741
+ * @returns New query builder instance with the GROUP BY clause
4742
+
4743
+ */
4490
4744
  groupBy(col2) {
4491
4745
  const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col2));
4492
4746
  return this.clone(nextContext);
4493
4747
  }
4494
4748
  /**
4495
- * Adds a HAVING condition to the query
4496
- * @param expr - Expression for the HAVING clause
4497
- * @returns New query builder instance with the HAVING condition
4498
- */
4749
+
4750
+ * Adds a HAVING condition to the query
4751
+
4752
+ * @param expr - Expression for the HAVING clause
4753
+
4754
+ * @returns New query builder instance with the HAVING condition
4755
+
4756
+ */
4499
4757
  having(expr) {
4500
4758
  const nextContext = this.applyAst(this.context, (service) => service.withHaving(expr));
4501
4759
  return this.clone(nextContext);
4502
4760
  }
4503
4761
  /**
4504
- * Adds an ORDER BY clause to the query
4505
- * @param col - Column definition or column node to order by
4506
- * @param direction - Order direction (defaults to ASC)
4507
- * @returns New query builder instance with the ORDER BY clause
4508
- */
4762
+
4763
+ * Adds an ORDER BY clause to the query
4764
+
4765
+ * @param col - Column definition or column node to order by
4766
+
4767
+ * @param direction - Order direction (defaults to ASC)
4768
+
4769
+ * @returns New query builder instance with the ORDER BY clause
4770
+
4771
+ */
4509
4772
  orderBy(col2, direction = ORDER_DIRECTIONS.ASC) {
4510
4773
  const nextContext = this.applyAst(this.context, (service) => service.withOrderBy(col2, direction));
4511
4774
  return this.clone(nextContext);
4512
4775
  }
4513
4776
  /**
4514
- * Adds a DISTINCT clause to the query
4515
- * @param cols - Columns to make distinct
4516
- * @returns New query builder instance with the DISTINCT clause
4517
- */
4777
+
4778
+ * Adds a DISTINCT clause to the query
4779
+
4780
+ * @param cols - Columns to make distinct
4781
+
4782
+ * @returns New query builder instance with the DISTINCT clause
4783
+
4784
+ */
4518
4785
  distinct(...cols) {
4519
4786
  return this.clone(this.columnSelector.distinct(this.context, cols));
4520
4787
  }
4521
4788
  /**
4522
- * Adds a LIMIT clause to the query
4523
- * @param n - Maximum number of rows to return
4524
- * @returns New query builder instance with the LIMIT clause
4525
- */
4789
+
4790
+ * Adds a LIMIT clause to the query
4791
+
4792
+ * @param n - Maximum number of rows to return
4793
+
4794
+ * @returns New query builder instance with the LIMIT clause
4795
+
4796
+ */
4526
4797
  limit(n) {
4527
4798
  const nextContext = this.applyAst(this.context, (service) => service.withLimit(n));
4528
4799
  return this.clone(nextContext);
4529
4800
  }
4530
4801
  /**
4531
- * Adds an OFFSET clause to the query
4532
- * @param n - Number of rows to skip
4533
- * @returns New query builder instance with the OFFSET clause
4534
- */
4802
+
4803
+ * Adds an OFFSET clause to the query
4804
+
4805
+ * @param n - Number of rows to skip
4806
+
4807
+ * @returns New query builder instance with the OFFSET clause
4808
+
4809
+ */
4535
4810
  offset(n) {
4536
4811
  const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
4537
4812
  return this.clone(nextContext);
4538
4813
  }
4539
4814
  /**
4540
- * Combines this query with another using UNION
4541
- * @param query - Query to union with
4542
- * @returns New query builder instance with the set operation
4543
- */
4815
+
4816
+ * Combines this query with another using UNION
4817
+
4818
+ * @param query - Query to union with
4819
+
4820
+ * @returns New query builder instance with the set operation
4821
+
4822
+ */
4544
4823
  union(query) {
4545
4824
  return this.clone(this.applySetOperation("UNION", query));
4546
4825
  }
4547
4826
  /**
4548
- * Combines this query with another using UNION ALL
4549
- * @param query - Query to union with
4550
- * @returns New query builder instance with the set operation
4551
- */
4827
+
4828
+ * Combines this query with another using UNION ALL
4829
+
4830
+ * @param query - Query to union with
4831
+
4832
+ * @returns New query builder instance with the set operation
4833
+
4834
+ */
4552
4835
  unionAll(query) {
4553
4836
  return this.clone(this.applySetOperation("UNION ALL", query));
4554
4837
  }
4555
4838
  /**
4556
- * Combines this query with another using INTERSECT
4557
- * @param query - Query to intersect with
4558
- * @returns New query builder instance with the set operation
4559
- */
4839
+
4840
+ * Combines this query with another using INTERSECT
4841
+
4842
+ * @param query - Query to intersect with
4843
+
4844
+ * @returns New query builder instance with the set operation
4845
+
4846
+ */
4560
4847
  intersect(query) {
4561
4848
  return this.clone(this.applySetOperation("INTERSECT", query));
4562
4849
  }
4563
4850
  /**
4564
- * Combines this query with another using EXCEPT
4565
- * @param query - Query to subtract
4566
- * @returns New query builder instance with the set operation
4567
- */
4851
+
4852
+ * Combines this query with another using EXCEPT
4853
+
4854
+ * @param query - Query to subtract
4855
+
4856
+ * @returns New query builder instance with the set operation
4857
+
4858
+ */
4568
4859
  except(query) {
4569
4860
  return this.clone(this.applySetOperation("EXCEPT", query));
4570
4861
  }
4571
4862
  /**
4572
- * Adds a WHERE EXISTS condition to the query
4573
- * @param subquery - Subquery to check for existence
4574
- * @returns New query builder instance with the WHERE EXISTS condition
4575
- */
4863
+
4864
+ * Adds a WHERE EXISTS condition to the query
4865
+
4866
+ * @param subquery - Subquery to check for existence
4867
+
4868
+ * @returns New query builder instance with the WHERE EXISTS condition
4869
+
4870
+ */
4576
4871
  whereExists(subquery) {
4577
4872
  const subAst = this.resolveQueryNode(subquery);
4578
4873
  return this.where(exists(subAst));
4579
4874
  }
4580
4875
  /**
4581
- * Adds a WHERE NOT EXISTS condition to the query
4582
- * @param subquery - Subquery to check for non-existence
4583
- * @returns New query builder instance with the WHERE NOT EXISTS condition
4584
- */
4876
+
4877
+ * Adds a WHERE NOT EXISTS condition to the query
4878
+
4879
+ * @param subquery - Subquery to check for non-existence
4880
+
4881
+ * @returns New query builder instance with the WHERE NOT EXISTS condition
4882
+
4883
+ */
4585
4884
  whereNotExists(subquery) {
4586
4885
  const subAst = this.resolveQueryNode(subquery);
4587
4886
  return this.where(notExists(subAst));
4588
4887
  }
4589
4888
  /**
4590
- * Adds a WHERE EXISTS condition based on a relationship
4591
- * @param relationName - Name of the relationship to check
4592
- * @param callback - Optional callback to modify the relationship query
4593
- * @returns New query builder instance with the relationship existence check
4594
- */
4889
+
4890
+ * Adds a WHERE EXISTS condition based on a relationship
4891
+
4892
+ * @param relationName - Name of the relationship to check
4893
+
4894
+ * @param callback - Optional callback to modify the relationship query
4895
+
4896
+ * @returns New query builder instance with the relationship existence check
4897
+
4898
+ */
4595
4899
  whereHas(relationName, callback) {
4596
4900
  const relation = this.env.table.relations[relationName];
4597
4901
  if (!relation) {
@@ -4606,11 +4910,16 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4606
4910
  return this.where(exists(finalSubAst));
4607
4911
  }
4608
4912
  /**
4609
- * Adds a WHERE NOT EXISTS condition based on a relationship
4610
- * @param relationName - Name of the relationship to check
4611
- * @param callback - Optional callback to modify the relationship query
4612
- * @returns New query builder instance with the relationship non-existence check
4613
- */
4913
+
4914
+ * Adds a WHERE NOT EXISTS condition based on a relationship
4915
+
4916
+ * @param relationName - Name of the relationship to check
4917
+
4918
+ * @param callback - Optional callback to modify the relationship query
4919
+
4920
+ * @returns New query builder instance with the relationship non-existence check
4921
+
4922
+ */
4614
4923
  whereHasNot(relationName, callback) {
4615
4924
  const relation = this.env.table.relations[relationName];
4616
4925
  if (!relation) {
@@ -4625,33 +4934,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4625
4934
  return this.where(notExists(finalSubAst));
4626
4935
  }
4627
4936
  /**
4628
- * Compiles the query to SQL for a specific dialect
4629
- * @param dialect - Database dialect to compile for
4630
- * @returns Compiled query with SQL and parameters
4631
- */
4937
+
4938
+ * Compiles the query to SQL for a specific dialect
4939
+
4940
+ * @param dialect - Database dialect to compile for
4941
+
4942
+ * @returns Compiled query with SQL and parameters
4943
+
4944
+ */
4632
4945
  compile(dialect) {
4633
4946
  const resolved = resolveDialectInput(dialect);
4634
4947
  return resolved.compileSelect(this.context.state.ast);
4635
4948
  }
4636
4949
  /**
4637
- * Converts the query to SQL string for a specific dialect
4638
- * @param dialect - Database dialect to generate SQL for
4639
- * @returns SQL string representation of the query
4640
- */
4950
+
4951
+ * Converts the query to SQL string for a specific dialect
4952
+
4953
+ * @param dialect - Database dialect to generate SQL for
4954
+
4955
+ * @returns SQL string representation of the query
4956
+
4957
+ */
4641
4958
  toSql(dialect) {
4642
4959
  return this.compile(dialect).sql;
4643
4960
  }
4644
4961
  /**
4645
- * Gets the hydration plan for the query
4646
- * @returns Hydration plan or undefined if none exists
4647
- */
4962
+
4963
+ * Gets the hydration plan for the query
4964
+
4965
+ * @returns Hydration plan or undefined if none exists
4966
+
4967
+ */
4648
4968
  getHydrationPlan() {
4649
4969
  return this.context.hydration.getPlan();
4650
4970
  }
4651
4971
  /**
4652
- * Gets the Abstract Syntax Tree (AST) representation of the query
4653
- * @returns Query AST with hydration applied
4654
- */
4972
+
4973
+ * Gets the Abstract Syntax Tree (AST) representation of the query
4974
+
4975
+ * @returns Query AST with hydration applied
4976
+
4977
+ */
4655
4978
  getAST() {
4656
4979
  return this.context.hydration.applyToAst(this.context.state.ast);
4657
4980
  }
@@ -4659,6 +4982,54 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4659
4982
  var createColumn = (table, name) => ({ type: "Column", table, name });
4660
4983
  var createLiteral = (val) => ({ type: "Literal", value: val });
4661
4984
 
4985
+ // src/orm/entity-metadata.ts
4986
+ var metadataMap = /* @__PURE__ */ new Map();
4987
+ var getEntityMetadata = (target) => {
4988
+ return metadataMap.get(target);
4989
+ };
4990
+
4991
+ // src/decorators/bootstrap.ts
4992
+ var getTableDefFromEntity = (ctor) => {
4993
+ const meta = getEntityMetadata(ctor);
4994
+ if (!meta) return void 0;
4995
+ return meta.table;
4996
+ };
4997
+ var selectFromEntity = (ctor) => {
4998
+ const table = getTableDefFromEntity(ctor);
4999
+ if (!table) {
5000
+ throw new Error("Entity metadata has not been bootstrapped");
5001
+ }
5002
+ return new SelectQueryBuilder(table);
5003
+ };
5004
+
5005
+ // src/query-builder/select-helpers.ts
5006
+ function sel(table, ...cols) {
5007
+ const selection = {};
5008
+ for (const col2 of cols) {
5009
+ const def = table.columns[col2];
5010
+ if (!def) {
5011
+ throw new Error(`Column '${col2}' not found on table '${table.name}'`);
5012
+ }
5013
+ selection[col2] = def;
5014
+ }
5015
+ return selection;
5016
+ }
5017
+ function esel(entity, ...props) {
5018
+ const table = getTableDefFromEntity(entity);
5019
+ if (!table) {
5020
+ throw new Error(`No table definition registered for entity '${entity.name}'`);
5021
+ }
5022
+ const selection = {};
5023
+ for (const prop of props) {
5024
+ const col2 = table.columns[prop];
5025
+ if (!col2) {
5026
+ throw new Error(`No column '${prop}' found for entity '${entity.name}'`);
5027
+ }
5028
+ selection[prop] = col2;
5029
+ }
5030
+ return selection;
5031
+ }
5032
+
4662
5033
  // src/query-builder/insert-query-state.ts
4663
5034
  var InsertQueryState = class _InsertQueryState {
4664
5035
  constructor(table, ast) {
@@ -4677,7 +5048,15 @@ var InsertQueryState = class _InsertQueryState {
4677
5048
  if (!rows.length) return this;
4678
5049
  const definedColumns = this.ast.columns.length ? this.ast.columns : buildColumnNodes(this.table, Object.keys(rows[0]));
4679
5050
  const newRows = rows.map(
4680
- (row) => definedColumns.map((column) => valueToOperand(row[column.name]))
5051
+ (row, rowIndex) => definedColumns.map((column) => {
5052
+ const rawValue = row[column.name];
5053
+ if (!isValueOperandInput(rawValue)) {
5054
+ throw new Error(
5055
+ `Invalid insert value for column "${column.name}" in row ${rowIndex}: only primitives, null, or OperandNodes are allowed`
5056
+ );
5057
+ }
5058
+ return valueToOperand(rawValue);
5059
+ })
4681
5060
  );
4682
5061
  return this.clone({
4683
5062
  ...this.ast,
@@ -4728,6 +5107,17 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
4728
5107
  };
4729
5108
 
4730
5109
  // src/query-builder/update-query-state.ts
5110
+ var isUpdateValue = (value) => {
5111
+ if (value === null) return true;
5112
+ switch (typeof value) {
5113
+ case "string":
5114
+ case "number":
5115
+ case "boolean":
5116
+ return true;
5117
+ default:
5118
+ return isOperandNode(value);
5119
+ }
5120
+ };
4731
5121
  var UpdateQueryState = class _UpdateQueryState {
4732
5122
  constructor(table, ast) {
4733
5123
  this.table = table;
@@ -4741,14 +5131,21 @@ var UpdateQueryState = class _UpdateQueryState {
4741
5131
  return new _UpdateQueryState(this.table, nextAst);
4742
5132
  }
4743
5133
  withSet(values) {
4744
- const assignments = Object.entries(values).map(([column, value]) => ({
4745
- column: {
4746
- type: "Column",
4747
- table: this.table.name,
4748
- name: column
4749
- },
4750
- value: valueToOperand(value)
4751
- }));
5134
+ const assignments = Object.entries(values).map(([column, rawValue]) => {
5135
+ if (!isUpdateValue(rawValue)) {
5136
+ throw new Error(
5137
+ `Invalid update value for column "${column}": only primitives, null, or OperandNodes are allowed`
5138
+ );
5139
+ }
5140
+ return {
5141
+ column: {
5142
+ type: "Column",
5143
+ table: this.table.name,
5144
+ name: column
5145
+ },
5146
+ value: valueToOperand(rawValue)
5147
+ };
5148
+ });
4752
5149
  return this.clone({
4753
5150
  ...this.ast,
4754
5151
  set: assignments
@@ -5949,20 +6346,55 @@ var SQL_OPERATOR_REGISTRY = {
5949
6346
  [SQL_OPERATORS.NOT_EXISTS]: { sql: SQL_OPERATORS.NOT_EXISTS, tsName: "notExists" }
5950
6347
  };
5951
6348
 
5952
- // src/codegen/typescript.ts
5953
- var capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
5954
- var assertNever2 = (value) => {
5955
- throw new Error(`Unhandled SQL operator: ${value}`);
5956
- };
5957
- var TypeScriptGenerator = class {
6349
+ // src/codegen/naming-strategy.ts
6350
+ var DefaultNamingStrategy = class {
5958
6351
  /**
5959
- * Generates TypeScript code from a query AST
5960
- * @param ast - Query AST to generate code from
5961
- * @returns Generated TypeScript code
6352
+ * Converts table names to TypeScript symbols
6353
+ * @param table - Table node, function table node, or string name
6354
+ * @returns Capitalized table name (handles schema-qualified names)
5962
6355
  */
5963
- generate(ast) {
5964
- const chainLines = this.buildSelectLines(ast);
5965
- const lines = chainLines.map((line, index) => index === 0 ? `const query = ${line}` : line);
6356
+ tableToSymbol(table) {
6357
+ const tableName = typeof table === "string" ? table : table.name;
6358
+ if (tableName.includes(".")) {
6359
+ return tableName.split(".").map((part) => this.capitalize(part)).join("");
6360
+ }
6361
+ return this.capitalize(tableName);
6362
+ }
6363
+ /**
6364
+ * Converts column references to property names
6365
+ * @param column - Column node
6366
+ * @returns Column name as-is (for backward compatibility)
6367
+ */
6368
+ columnToProperty(column) {
6369
+ return column.name;
6370
+ }
6371
+ /**
6372
+ * Capitalizes the first letter of a string
6373
+ * @param s - String to capitalize
6374
+ * @returns Capitalized string
6375
+ */
6376
+ capitalize(s) {
6377
+ if (!s) return s;
6378
+ return s.charAt(0).toUpperCase() + s.slice(1);
6379
+ }
6380
+ };
6381
+
6382
+ // src/codegen/typescript.ts
6383
+ var assertNever2 = (value) => {
6384
+ throw new Error(`Unhandled SQL operator: ${value}`);
6385
+ };
6386
+ var TypeScriptGenerator = class {
6387
+ constructor(namingStrategy = new DefaultNamingStrategy()) {
6388
+ this.namingStrategy = namingStrategy;
6389
+ }
6390
+ /**
6391
+ * Generates TypeScript code from a query AST
6392
+ * @param ast - Query AST to generate code from
6393
+ * @returns Generated TypeScript code
6394
+ */
6395
+ generate(ast) {
6396
+ const chainLines = this.buildSelectLines(ast);
6397
+ const lines = chainLines.map((line, index) => index === 0 ? `const query = ${line}` : line);
5966
6398
  lines.push(";", "", "await query.execute();");
5967
6399
  return lines.join("\n");
5968
6400
  }
@@ -5981,13 +6413,13 @@ var TypeScriptGenerator = class {
5981
6413
  return `${key}: ${this.printOperand(operand)}`;
5982
6414
  });
5983
6415
  lines.push(`db.select({`);
5984
- selections.forEach((sel, index) => {
5985
- lines.push(` ${sel}${index < selections.length - 1 ? "," : ""}`);
6416
+ selections.forEach((sel2, index) => {
6417
+ lines.push(` ${sel2}${index < selections.length - 1 ? "," : ""}`);
5986
6418
  });
5987
6419
  lines.push(`})`);
5988
- lines.push(`.from(${capitalize(ast.from.name)})`);
6420
+ lines.push(`.from(${this.namingStrategy.tableToSymbol(ast.from)})`);
5989
6421
  if (ast.distinct && ast.distinct.length) {
5990
- const cols = ast.distinct.map((c) => `${capitalize(c.table)}.${c.name}`).join(", ");
6422
+ const cols = ast.distinct.map((c) => `${this.namingStrategy.tableToSymbol(c.table)}.${c.name}`).join(", ");
5991
6423
  lines.push(`.distinct(${cols})`);
5992
6424
  }
5993
6425
  ast.joins.forEach((join) => {
@@ -6002,7 +6434,7 @@ var TypeScriptGenerator = class {
6002
6434
  lines.push(`.joinRelation('${relationName}', '${join.kind}')`);
6003
6435
  }
6004
6436
  } else {
6005
- const table = capitalize(join.table.name);
6437
+ const table = this.namingStrategy.tableToSymbol(join.table);
6006
6438
  const cond = this.printExpression(join.condition);
6007
6439
  let method = "innerJoin";
6008
6440
  if (join.kind === "LEFT") method = "leftJoin";
@@ -6023,7 +6455,7 @@ var TypeScriptGenerator = class {
6023
6455
  lines.push(`.where(${this.printExpression(ast.where)})`);
6024
6456
  }
6025
6457
  if (ast.groupBy && ast.groupBy.length) {
6026
- const cols = ast.groupBy.map((c) => `${capitalize(c.table)}.${c.name}`).join(", ");
6458
+ const cols = ast.groupBy.map((c) => `${this.namingStrategy.tableToSymbol(c.table)}.${c.name}`).join(", ");
6027
6459
  lines.push(`.groupBy(${cols})`);
6028
6460
  }
6029
6461
  if (ast.having) {
@@ -6031,7 +6463,7 @@ var TypeScriptGenerator = class {
6031
6463
  }
6032
6464
  if (ast.orderBy && ast.orderBy.length) {
6033
6465
  ast.orderBy.forEach((o) => {
6034
- lines.push(`.orderBy(${capitalize(o.column.table)}.${o.column.name}, '${o.direction}')`);
6466
+ lines.push(`.orderBy(${this.namingStrategy.tableToSymbol(o.column.table)}.${o.column.name}, '${o.direction}')`);
6035
6467
  });
6036
6468
  }
6037
6469
  if (ast.limit) lines.push(`.limit(${ast.limit})`);
@@ -6170,7 +6602,7 @@ var TypeScriptGenerator = class {
6170
6602
  * @returns TypeScript code representation
6171
6603
  */
6172
6604
  printColumnOperand(column) {
6173
- return `${capitalize(column.table)}.${column.name}`;
6605
+ return `${this.namingStrategy.tableToSymbol(column.table)}.${column.name}`;
6174
6606
  }
6175
6607
  /**
6176
6608
  * Prints a literal operand to TypeScript code
@@ -6196,7 +6628,7 @@ var TypeScriptGenerator = class {
6196
6628
  * @returns TypeScript code representation
6197
6629
  */
6198
6630
  printJsonPathOperand(json) {
6199
- return `jsonPath(${capitalize(json.column.table)}.${json.column.name}, '${json.path}')`;
6631
+ return `jsonPath(${this.namingStrategy.tableToSymbol(json.column.table)}.${json.column.name}, '${json.path}')`;
6200
6632
  }
6201
6633
  /**
6202
6634
  * Prints a scalar subquery operand to TypeScript code
@@ -6232,11 +6664,11 @@ var TypeScriptGenerator = class {
6232
6664
  result += ") OVER (";
6233
6665
  const parts = [];
6234
6666
  if (node.partitionBy && node.partitionBy.length > 0) {
6235
- const partitionClause = "PARTITION BY " + node.partitionBy.map((col2) => `${capitalize(col2.table)}.${col2.name}`).join(", ");
6667
+ const partitionClause = "PARTITION BY " + node.partitionBy.map((col2) => `${this.namingStrategy.tableToSymbol(col2.table)}.${col2.name}`).join(", ");
6236
6668
  parts.push(partitionClause);
6237
6669
  }
6238
6670
  if (node.orderBy && node.orderBy.length > 0) {
6239
- const orderClause = "ORDER BY " + node.orderBy.map((o) => `${capitalize(o.column.table)}.${o.column.name} ${o.direction}`).join(", ");
6671
+ const orderClause = "ORDER BY " + node.orderBy.map((o) => `${this.namingStrategy.tableToSymbol(o.column.table)}.${o.column.name} ${o.direction}`).join(", ");
6240
6672
  parts.push(orderClause);
6241
6673
  }
6242
6674
  result += parts.join(" ");
@@ -6265,48 +6697,6 @@ var TypeScriptGenerator = class {
6265
6697
  }
6266
6698
  };
6267
6699
 
6268
- // src/orm/domain-event-bus.ts
6269
- var DomainEventBus = class {
6270
- constructor(initialHandlers) {
6271
- this.handlers = /* @__PURE__ */ new Map();
6272
- const handlers = initialHandlers ?? {};
6273
- Object.entries(handlers).forEach(([name, list]) => {
6274
- this.handlers.set(name, [...list]);
6275
- });
6276
- }
6277
- register(name, handler) {
6278
- const existing = this.handlers.get(name) ?? [];
6279
- existing.push(handler);
6280
- this.handlers.set(name, existing);
6281
- }
6282
- async dispatch(trackedEntities, ctx) {
6283
- for (const tracked of trackedEntities) {
6284
- const entity = tracked.entity;
6285
- if (!entity.domainEvents || !entity.domainEvents.length) continue;
6286
- for (const event of entity.domainEvents) {
6287
- const eventName = this.getEventName(event);
6288
- const handlers = this.handlers.get(eventName);
6289
- if (!handlers) continue;
6290
- for (const handler of handlers) {
6291
- await handler(event, ctx);
6292
- }
6293
- }
6294
- entity.domainEvents = [];
6295
- }
6296
- }
6297
- getEventName(event) {
6298
- if (!event) return "Unknown";
6299
- if (typeof event === "string") return event;
6300
- return event.constructor?.name ?? "Unknown";
6301
- }
6302
- };
6303
- var addDomainEvent = (entity, event) => {
6304
- if (!entity.domainEvents) {
6305
- entity.domainEvents = [];
6306
- }
6307
- entity.domainEvents.push(event);
6308
- };
6309
-
6310
6700
  // src/orm/identity-map.ts
6311
6701
  var IdentityMap = class {
6312
6702
  constructor() {
@@ -6334,169 +6724,14 @@ var IdentityMap = class {
6334
6724
  const bucket = this.buckets.get(table.name);
6335
6725
  return bucket ? Array.from(bucket.values()) : [];
6336
6726
  }
6727
+ clear() {
6728
+ this.buckets.clear();
6729
+ }
6337
6730
  toIdentityKey(pk) {
6338
6731
  return String(pk);
6339
6732
  }
6340
6733
  };
6341
6734
 
6342
- // src/orm/relation-change-processor.ts
6343
- var RelationChangeProcessor = class {
6344
- constructor(unitOfWork, dialect, executor) {
6345
- this.unitOfWork = unitOfWork;
6346
- this.dialect = dialect;
6347
- this.executor = executor;
6348
- this.relationChanges = [];
6349
- }
6350
- registerChange(entry) {
6351
- this.relationChanges.push(entry);
6352
- }
6353
- async process() {
6354
- if (!this.relationChanges.length) return;
6355
- const entries = [...this.relationChanges];
6356
- this.relationChanges.length = 0;
6357
- for (const entry of entries) {
6358
- switch (entry.relation.type) {
6359
- case RelationKinds.HasMany:
6360
- await this.handleHasManyChange(entry);
6361
- break;
6362
- case RelationKinds.HasOne:
6363
- await this.handleHasOneChange(entry);
6364
- break;
6365
- case RelationKinds.BelongsToMany:
6366
- await this.handleBelongsToManyChange(entry);
6367
- break;
6368
- case RelationKinds.BelongsTo:
6369
- await this.handleBelongsToChange(entry);
6370
- break;
6371
- }
6372
- }
6373
- }
6374
- async handleHasManyChange(entry) {
6375
- const relation = entry.relation;
6376
- const target = entry.change.entity;
6377
- if (!target) return;
6378
- const tracked = this.unitOfWork.findTracked(target);
6379
- if (!tracked) return;
6380
- const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
6381
- const rootValue = entry.root[localKey];
6382
- if (rootValue === void 0 || rootValue === null) return;
6383
- if (entry.change.kind === "add" || entry.change.kind === "attach") {
6384
- this.assignHasManyForeignKey(tracked.entity, relation, rootValue);
6385
- this.unitOfWork.markDirty(tracked.entity);
6386
- return;
6387
- }
6388
- if (entry.change.kind === "remove") {
6389
- this.detachHasManyChild(tracked.entity, relation);
6390
- }
6391
- }
6392
- async handleHasOneChange(entry) {
6393
- const relation = entry.relation;
6394
- const target = entry.change.entity;
6395
- if (!target) return;
6396
- const tracked = this.unitOfWork.findTracked(target);
6397
- if (!tracked) return;
6398
- const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
6399
- const rootValue = entry.root[localKey];
6400
- if (rootValue === void 0 || rootValue === null) return;
6401
- if (entry.change.kind === "attach" || entry.change.kind === "add") {
6402
- this.assignHasOneForeignKey(tracked.entity, relation, rootValue);
6403
- this.unitOfWork.markDirty(tracked.entity);
6404
- return;
6405
- }
6406
- if (entry.change.kind === "remove") {
6407
- this.detachHasOneChild(tracked.entity, relation);
6408
- }
6409
- }
6410
- async handleBelongsToChange(_entry) {
6411
- }
6412
- async handleBelongsToManyChange(entry) {
6413
- const relation = entry.relation;
6414
- const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
6415
- const rootId = entry.root[rootKey];
6416
- if (rootId === void 0 || rootId === null) return;
6417
- const targetId = this.resolvePrimaryKeyValue(entry.change.entity, relation.target);
6418
- if (targetId === null) return;
6419
- if (entry.change.kind === "attach" || entry.change.kind === "add") {
6420
- await this.insertPivotRow(relation, rootId, targetId);
6421
- return;
6422
- }
6423
- if (entry.change.kind === "detach" || entry.change.kind === "remove") {
6424
- await this.deletePivotRow(relation, rootId, targetId);
6425
- if (relation.cascade === "all" || relation.cascade === "remove") {
6426
- this.unitOfWork.markRemoved(entry.change.entity);
6427
- }
6428
- }
6429
- }
6430
- assignHasManyForeignKey(child, relation, rootValue) {
6431
- const current = child[relation.foreignKey];
6432
- if (current === rootValue) return;
6433
- child[relation.foreignKey] = rootValue;
6434
- }
6435
- detachHasManyChild(child, relation) {
6436
- if (relation.cascade === "all" || relation.cascade === "remove") {
6437
- this.unitOfWork.markRemoved(child);
6438
- return;
6439
- }
6440
- child[relation.foreignKey] = null;
6441
- this.unitOfWork.markDirty(child);
6442
- }
6443
- assignHasOneForeignKey(child, relation, rootValue) {
6444
- const current = child[relation.foreignKey];
6445
- if (current === rootValue) return;
6446
- child[relation.foreignKey] = rootValue;
6447
- }
6448
- detachHasOneChild(child, relation) {
6449
- if (relation.cascade === "all" || relation.cascade === "remove") {
6450
- this.unitOfWork.markRemoved(child);
6451
- return;
6452
- }
6453
- child[relation.foreignKey] = null;
6454
- this.unitOfWork.markDirty(child);
6455
- }
6456
- async insertPivotRow(relation, rootId, targetId) {
6457
- const payload = {
6458
- [relation.pivotForeignKeyToRoot]: rootId,
6459
- [relation.pivotForeignKeyToTarget]: targetId
6460
- };
6461
- const builder = new InsertQueryBuilder(relation.pivotTable).values(payload);
6462
- const compiled = builder.compile(this.dialect);
6463
- await this.executor.executeSql(compiled.sql, compiled.params);
6464
- }
6465
- async deletePivotRow(relation, rootId, targetId) {
6466
- const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
6467
- const targetCol = relation.pivotTable.columns[relation.pivotForeignKeyToTarget];
6468
- if (!rootCol || !targetCol) return;
6469
- const builder = new DeleteQueryBuilder(relation.pivotTable).where(
6470
- and(eq(rootCol, rootId), eq(targetCol, targetId))
6471
- );
6472
- const compiled = builder.compile(this.dialect);
6473
- await this.executor.executeSql(compiled.sql, compiled.params);
6474
- }
6475
- resolvePrimaryKeyValue(entity, table) {
6476
- if (!entity) return null;
6477
- const key = findPrimaryKey(table);
6478
- const value = entity[key];
6479
- if (value === void 0 || value === null) return null;
6480
- return value;
6481
- }
6482
- };
6483
-
6484
- // src/orm/transaction-runner.ts
6485
- var runInTransaction = async (executor, action) => {
6486
- if (!executor.beginTransaction) {
6487
- await action();
6488
- return;
6489
- }
6490
- await executor.beginTransaction();
6491
- try {
6492
- await action();
6493
- await executor.commitTransaction?.();
6494
- } catch (error) {
6495
- await executor.rollbackTransaction?.();
6496
- throw error;
6497
- }
6498
- };
6499
-
6500
6735
  // src/orm/runtime-types.ts
6501
6736
  var EntityStatus = /* @__PURE__ */ ((EntityStatus2) => {
6502
6737
  EntityStatus2["New"] = "new";
@@ -6601,6 +6836,10 @@ var UnitOfWork = class {
6601
6836
  }
6602
6837
  }
6603
6838
  }
6839
+ reset() {
6840
+ this.trackedEntities.clear();
6841
+ this.identityMap.clear();
6842
+ }
6604
6843
  async flushInsert(tracked) {
6605
6844
  await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
6606
6845
  const payload = this.extractColumns(tracked.table, tracked.entity);
@@ -6670,6 +6909,7 @@ var UnitOfWork = class {
6670
6909
  extractColumns(table, entity) {
6671
6910
  const payload = {};
6672
6911
  for (const column of Object.keys(table.columns)) {
6912
+ if (entity[column] === void 0) continue;
6673
6913
  payload[column] = entity[column];
6674
6914
  }
6675
6915
  return payload;
@@ -6720,6 +6960,194 @@ var UnitOfWork = class {
6720
6960
  }
6721
6961
  };
6722
6962
 
6963
+ // src/orm/domain-event-bus.ts
6964
+ var DomainEventBus = class {
6965
+ constructor(initialHandlers) {
6966
+ this.handlers = /* @__PURE__ */ new Map();
6967
+ if (initialHandlers) {
6968
+ for (const key in initialHandlers) {
6969
+ const type = key;
6970
+ const list = initialHandlers[type] ?? [];
6971
+ this.handlers.set(type, [...list]);
6972
+ }
6973
+ }
6974
+ }
6975
+ on(type, handler) {
6976
+ const key = type;
6977
+ const existing = this.handlers.get(key) ?? [];
6978
+ existing.push(handler);
6979
+ this.handlers.set(key, existing);
6980
+ }
6981
+ register(type, handler) {
6982
+ this.on(type, handler);
6983
+ }
6984
+ async dispatch(trackedEntities, ctx) {
6985
+ for (const tracked of trackedEntities) {
6986
+ const entity = tracked.entity;
6987
+ if (!entity.domainEvents?.length) continue;
6988
+ for (const event of entity.domainEvents) {
6989
+ const handlers = this.handlers.get(event.type);
6990
+ if (!handlers?.length) continue;
6991
+ for (const handler of handlers) {
6992
+ await handler(event, ctx);
6993
+ }
6994
+ }
6995
+ entity.domainEvents = [];
6996
+ }
6997
+ }
6998
+ };
6999
+ var addDomainEvent = (entity, event) => {
7000
+ if (!entity.domainEvents) {
7001
+ entity.domainEvents = [];
7002
+ }
7003
+ entity.domainEvents.push(event);
7004
+ };
7005
+
7006
+ // src/orm/relation-change-processor.ts
7007
+ var RelationChangeProcessor = class {
7008
+ constructor(unitOfWork, dialect, executor) {
7009
+ this.unitOfWork = unitOfWork;
7010
+ this.dialect = dialect;
7011
+ this.executor = executor;
7012
+ this.relationChanges = [];
7013
+ }
7014
+ registerChange(entry) {
7015
+ this.relationChanges.push(entry);
7016
+ }
7017
+ reset() {
7018
+ this.relationChanges.length = 0;
7019
+ }
7020
+ async process() {
7021
+ if (!this.relationChanges.length) return;
7022
+ const entries = [...this.relationChanges];
7023
+ this.relationChanges.length = 0;
7024
+ for (const entry of entries) {
7025
+ switch (entry.relation.type) {
7026
+ case RelationKinds.HasMany:
7027
+ await this.handleHasManyChange(entry);
7028
+ break;
7029
+ case RelationKinds.HasOne:
7030
+ await this.handleHasOneChange(entry);
7031
+ break;
7032
+ case RelationKinds.BelongsToMany:
7033
+ await this.handleBelongsToManyChange(entry);
7034
+ break;
7035
+ case RelationKinds.BelongsTo:
7036
+ await this.handleBelongsToChange(entry);
7037
+ break;
7038
+ }
7039
+ }
7040
+ }
7041
+ async handleHasManyChange(entry) {
7042
+ const relation = entry.relation;
7043
+ const target = entry.change.entity;
7044
+ if (!target) return;
7045
+ const tracked = this.unitOfWork.findTracked(target);
7046
+ if (!tracked) return;
7047
+ const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
7048
+ const rootValue = entry.root[localKey];
7049
+ if (rootValue === void 0 || rootValue === null) return;
7050
+ if (entry.change.kind === "add" || entry.change.kind === "attach") {
7051
+ this.assignHasManyForeignKey(tracked.entity, relation, rootValue);
7052
+ this.unitOfWork.markDirty(tracked.entity);
7053
+ return;
7054
+ }
7055
+ if (entry.change.kind === "remove") {
7056
+ this.detachHasManyChild(tracked.entity, relation);
7057
+ }
7058
+ }
7059
+ async handleHasOneChange(entry) {
7060
+ const relation = entry.relation;
7061
+ const target = entry.change.entity;
7062
+ if (!target) return;
7063
+ const tracked = this.unitOfWork.findTracked(target);
7064
+ if (!tracked) return;
7065
+ const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
7066
+ const rootValue = entry.root[localKey];
7067
+ if (rootValue === void 0 || rootValue === null) return;
7068
+ if (entry.change.kind === "attach" || entry.change.kind === "add") {
7069
+ this.assignHasOneForeignKey(tracked.entity, relation, rootValue);
7070
+ this.unitOfWork.markDirty(tracked.entity);
7071
+ return;
7072
+ }
7073
+ if (entry.change.kind === "remove") {
7074
+ this.detachHasOneChild(tracked.entity, relation);
7075
+ }
7076
+ }
7077
+ async handleBelongsToChange(_entry) {
7078
+ }
7079
+ async handleBelongsToManyChange(entry) {
7080
+ const relation = entry.relation;
7081
+ const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
7082
+ const rootId = entry.root[rootKey];
7083
+ if (rootId === void 0 || rootId === null) return;
7084
+ const targetId = this.resolvePrimaryKeyValue(entry.change.entity, relation.target);
7085
+ if (targetId === null) return;
7086
+ if (entry.change.kind === "attach" || entry.change.kind === "add") {
7087
+ await this.insertPivotRow(relation, rootId, targetId);
7088
+ return;
7089
+ }
7090
+ if (entry.change.kind === "detach" || entry.change.kind === "remove") {
7091
+ await this.deletePivotRow(relation, rootId, targetId);
7092
+ if (relation.cascade === "all" || relation.cascade === "remove") {
7093
+ this.unitOfWork.markRemoved(entry.change.entity);
7094
+ }
7095
+ }
7096
+ }
7097
+ assignHasManyForeignKey(child, relation, rootValue) {
7098
+ const current = child[relation.foreignKey];
7099
+ if (current === rootValue) return;
7100
+ child[relation.foreignKey] = rootValue;
7101
+ }
7102
+ detachHasManyChild(child, relation) {
7103
+ if (relation.cascade === "all" || relation.cascade === "remove") {
7104
+ this.unitOfWork.markRemoved(child);
7105
+ return;
7106
+ }
7107
+ child[relation.foreignKey] = null;
7108
+ this.unitOfWork.markDirty(child);
7109
+ }
7110
+ assignHasOneForeignKey(child, relation, rootValue) {
7111
+ const current = child[relation.foreignKey];
7112
+ if (current === rootValue) return;
7113
+ child[relation.foreignKey] = rootValue;
7114
+ }
7115
+ detachHasOneChild(child, relation) {
7116
+ if (relation.cascade === "all" || relation.cascade === "remove") {
7117
+ this.unitOfWork.markRemoved(child);
7118
+ return;
7119
+ }
7120
+ child[relation.foreignKey] = null;
7121
+ this.unitOfWork.markDirty(child);
7122
+ }
7123
+ async insertPivotRow(relation, rootId, targetId) {
7124
+ const payload = {
7125
+ [relation.pivotForeignKeyToRoot]: rootId,
7126
+ [relation.pivotForeignKeyToTarget]: targetId
7127
+ };
7128
+ const builder = new InsertQueryBuilder(relation.pivotTable).values(payload);
7129
+ const compiled = builder.compile(this.dialect);
7130
+ await this.executor.executeSql(compiled.sql, compiled.params);
7131
+ }
7132
+ async deletePivotRow(relation, rootId, targetId) {
7133
+ const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
7134
+ const targetCol = relation.pivotTable.columns[relation.pivotForeignKeyToTarget];
7135
+ if (!rootCol || !targetCol) return;
7136
+ const builder = new DeleteQueryBuilder(relation.pivotTable).where(
7137
+ and(eq(rootCol, rootId), eq(targetCol, targetId))
7138
+ );
7139
+ const compiled = builder.compile(this.dialect);
7140
+ await this.executor.executeSql(compiled.sql, compiled.params);
7141
+ }
7142
+ resolvePrimaryKeyValue(entity, table) {
7143
+ if (!entity) return null;
7144
+ const key = findPrimaryKey(table);
7145
+ const value = entity[key];
7146
+ if (value === void 0 || value === null) return null;
7147
+ return value;
7148
+ }
7149
+ };
7150
+
6723
7151
  // src/orm/query-logger.ts
6724
7152
  var createQueryLoggingExecutor = (executor, logger) => {
6725
7153
  if (!logger) {
@@ -6743,31 +7171,40 @@ var createQueryLoggingExecutor = (executor, logger) => {
6743
7171
  return wrapped;
6744
7172
  };
6745
7173
 
6746
- // src/orm/orm-context.ts
6747
- var OrmContext = class {
6748
- constructor(options) {
6749
- this.options = options;
7174
+ // src/orm/transaction-runner.ts
7175
+ var runInTransaction = async (executor, action) => {
7176
+ if (!executor.beginTransaction) {
7177
+ await action();
7178
+ return;
7179
+ }
7180
+ await executor.beginTransaction();
7181
+ try {
7182
+ await action();
7183
+ await executor.commitTransaction?.();
7184
+ } catch (error) {
7185
+ await executor.rollbackTransaction?.();
7186
+ throw error;
7187
+ }
7188
+ };
7189
+
7190
+ // src/orm/orm-session.ts
7191
+ var OrmSession = class {
7192
+ constructor(opts) {
7193
+ this.registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
7194
+ this.relationChanges.registerChange(
7195
+ buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
7196
+ );
7197
+ };
7198
+ this.orm = opts.orm;
7199
+ this.executor = createQueryLoggingExecutor(opts.executor, opts.queryLogger);
7200
+ this.interceptors = [...opts.interceptors ?? []];
6750
7201
  this.identityMap = new IdentityMap();
6751
- this.interceptors = [...options.interceptors ?? []];
6752
- this.executorWithLogging = createQueryLoggingExecutor(options.executor, options.queryLogger);
6753
- this.unitOfWork = new UnitOfWork(
6754
- options.dialect,
6755
- this.executorWithLogging,
6756
- this.identityMap,
6757
- () => this
6758
- );
6759
- this.relationChanges = new RelationChangeProcessor(
6760
- this.unitOfWork,
6761
- options.dialect,
6762
- this.executorWithLogging
6763
- );
6764
- this.domainEvents = new DomainEventBus(options.domainEventHandlers);
7202
+ this.unitOfWork = new UnitOfWork(this.orm.dialect, this.executor, this.identityMap, () => this);
7203
+ this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
7204
+ this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
6765
7205
  }
6766
7206
  get dialect() {
6767
- return this.options.dialect;
6768
- }
6769
- get executor() {
6770
- return this.executorWithLogging;
7207
+ return this.orm.dialect;
6771
7208
  }
6772
7209
  get identityBuckets() {
6773
7210
  return this.unitOfWork.identityBuckets;
@@ -6793,24 +7230,64 @@ var OrmContext = class {
6793
7230
  markRemoved(entity) {
6794
7231
  this.unitOfWork.markRemoved(entity);
6795
7232
  }
6796
- registerRelationChange(root, relationKey, rootTable, relationName, relation, change) {
6797
- const entry = {
6798
- root,
6799
- relationKey,
6800
- rootTable,
6801
- relationName,
6802
- relation,
6803
- change
6804
- };
6805
- this.relationChanges.registerChange(entry);
7233
+ getEntitiesForTable(table) {
7234
+ return this.unitOfWork.getEntitiesForTable(table);
6806
7235
  }
6807
7236
  registerInterceptor(interceptor) {
6808
7237
  this.interceptors.push(interceptor);
6809
7238
  }
6810
- registerDomainEventHandler(name, handler) {
6811
- this.domainEvents.register(name, handler);
7239
+ registerDomainEventHandler(type, handler) {
7240
+ this.domainEvents.on(type, handler);
7241
+ }
7242
+ async find(entityClass, id) {
7243
+ const table = getTableDefFromEntity(entityClass);
7244
+ if (!table) {
7245
+ throw new Error("Entity metadata has not been bootstrapped");
7246
+ }
7247
+ const primaryKey = findPrimaryKey(table);
7248
+ const column = table.columns[primaryKey];
7249
+ if (!column) {
7250
+ throw new Error("Entity table does not expose a primary key");
7251
+ }
7252
+ const columnSelections = Object.values(table.columns).reduce((acc, col2) => {
7253
+ acc[col2.name] = col2;
7254
+ return acc;
7255
+ }, {});
7256
+ const qb = selectFromEntity(entityClass).select(columnSelections).where(eq(column, id)).limit(1);
7257
+ const rows = await executeHydrated(this, qb);
7258
+ return rows[0] ?? null;
7259
+ }
7260
+ async findOne(qb) {
7261
+ const limited = qb.limit(1);
7262
+ const rows = await executeHydrated(this, limited);
7263
+ return rows[0] ?? null;
7264
+ }
7265
+ async findMany(qb) {
7266
+ return executeHydrated(this, qb);
7267
+ }
7268
+ async persist(entity) {
7269
+ if (this.unitOfWork.findTracked(entity)) {
7270
+ return;
7271
+ }
7272
+ const table = getTableDefFromEntity(entity.constructor);
7273
+ if (!table) {
7274
+ throw new Error("Entity metadata has not been bootstrapped");
7275
+ }
7276
+ const primaryKey = findPrimaryKey(table);
7277
+ const pkValue = entity[primaryKey];
7278
+ if (pkValue !== void 0 && pkValue !== null) {
7279
+ this.trackManaged(table, pkValue, entity);
7280
+ } else {
7281
+ this.trackNew(table, entity);
7282
+ }
7283
+ }
7284
+ async remove(entity) {
7285
+ this.markRemoved(entity);
7286
+ }
7287
+ async flush() {
7288
+ await this.unitOfWork.flush();
6812
7289
  }
6813
- async saveChanges() {
7290
+ async commit() {
6814
7291
  await runInTransaction(this.executor, async () => {
6815
7292
  for (const interceptor of this.interceptors) {
6816
7293
  await interceptor.beforeFlush?.(this);
@@ -6824,8 +7301,82 @@ var OrmContext = class {
6824
7301
  });
6825
7302
  await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
6826
7303
  }
6827
- getEntitiesForTable(table) {
6828
- return this.unitOfWork.getEntitiesForTable(table);
7304
+ async rollback() {
7305
+ await this.executor.rollbackTransaction?.();
7306
+ this.unitOfWork.reset();
7307
+ this.relationChanges.reset();
7308
+ }
7309
+ getExecutionContext() {
7310
+ return {
7311
+ dialect: this.orm.dialect,
7312
+ executor: this.executor,
7313
+ interceptors: this.orm.interceptors
7314
+ };
7315
+ }
7316
+ getHydrationContext() {
7317
+ return {
7318
+ identityMap: this.identityMap,
7319
+ unitOfWork: this.unitOfWork,
7320
+ domainEvents: this.domainEvents,
7321
+ relationChanges: this.relationChanges,
7322
+ entityContext: this
7323
+ };
7324
+ }
7325
+ };
7326
+ var buildRelationChangeEntry = (root, relationKey, rootTable, relationName, relation, change) => ({
7327
+ root,
7328
+ relationKey,
7329
+ rootTable,
7330
+ relationName,
7331
+ relation,
7332
+ change
7333
+ });
7334
+
7335
+ // src/orm/interceptor-pipeline.ts
7336
+ var InterceptorPipeline = class {
7337
+ constructor() {
7338
+ this.interceptors = [];
7339
+ }
7340
+ use(interceptor) {
7341
+ this.interceptors.push(interceptor);
7342
+ }
7343
+ async run(ctx, executor) {
7344
+ let i = 0;
7345
+ const dispatch = async () => {
7346
+ const interceptor = this.interceptors[i++];
7347
+ if (!interceptor) {
7348
+ return executor.executeSql(ctx.sql, ctx.params);
7349
+ }
7350
+ return interceptor(ctx, dispatch);
7351
+ };
7352
+ return dispatch();
7353
+ }
7354
+ };
7355
+
7356
+ // src/orm/orm.ts
7357
+ var Orm = class {
7358
+ constructor(opts) {
7359
+ this.dialect = opts.dialect;
7360
+ this.interceptors = opts.interceptors ?? new InterceptorPipeline();
7361
+ this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
7362
+ this.executorFactory = opts.executorFactory;
7363
+ }
7364
+ createSession(options) {
7365
+ const executor = this.executorFactory.createExecutor(options?.tx);
7366
+ return new OrmSession({ orm: this, executor });
7367
+ }
7368
+ async transaction(fn4) {
7369
+ const executor = this.executorFactory.createTransactionalExecutor();
7370
+ const session = new OrmSession({ orm: this, executor });
7371
+ try {
7372
+ const result = await fn4(session);
7373
+ await session.commit();
7374
+ return result;
7375
+ } catch (err) {
7376
+ await session.rollback();
7377
+ throw err;
7378
+ } finally {
7379
+ }
6829
7380
  }
6830
7381
  };
6831
7382
 
@@ -6941,10 +7492,12 @@ function createMssqlExecutor(client) {
6941
7492
  DefaultHasManyCollection,
6942
7493
  DefaultManyToManyCollection,
6943
7494
  DeleteQueryBuilder,
7495
+ DomainEventBus,
6944
7496
  EntityStatus,
6945
7497
  InsertQueryBuilder,
6946
7498
  MySqlDialect,
6947
- OrmContext,
7499
+ Orm,
7500
+ OrmSession,
6948
7501
  PostgresDialect,
6949
7502
  RelationKinds,
6950
7503
  SelectQueryBuilder,
@@ -6986,6 +7539,7 @@ function createMssqlExecutor(client) {
6986
7539
  createMssqlExecutor,
6987
7540
  createMysqlExecutor,
6988
7541
  createPostgresExecutor,
7542
+ createQueryLoggingExecutor,
6989
7543
  createSqliteExecutor,
6990
7544
  currentDate,
6991
7545
  currentTime,
@@ -7002,7 +7556,9 @@ function createMssqlExecutor(client) {
7002
7556
  diffSchema,
7003
7557
  endOfMonth,
7004
7558
  eq,
7559
+ esel,
7005
7560
  executeHydrated,
7561
+ executeHydratedWithContexts,
7006
7562
  exists,
7007
7563
  exp,
7008
7564
  extract,
@@ -7012,6 +7568,7 @@ function createMssqlExecutor(client) {
7012
7568
  generateCreateTableSql,
7013
7569
  generateSchemaSql,
7014
7570
  getSchemaIntrospector,
7571
+ groupConcat,
7015
7572
  gt,
7016
7573
  gte,
7017
7574
  hasMany,
@@ -7026,6 +7583,7 @@ function createMssqlExecutor(client) {
7026
7583
  isNotNull,
7027
7584
  isNull,
7028
7585
  isOperandNode,
7586
+ isValueOperandInput,
7029
7587
  isWindowFunctionNode,
7030
7588
  jsonPath,
7031
7589
  lag,
@@ -7048,6 +7606,8 @@ function createMssqlExecutor(client) {
7048
7606
  lt,
7049
7607
  lte,
7050
7608
  ltrim,
7609
+ max,
7610
+ min,
7051
7611
  mod,
7052
7612
  month,
7053
7613
  neq,
@@ -7078,6 +7638,7 @@ function createMssqlExecutor(client) {
7078
7638
  rowsToQueryResult,
7079
7639
  rpad,
7080
7640
  rtrim,
7641
+ sel,
7081
7642
  sign,
7082
7643
  sin,
7083
7644
  space,