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.js CHANGED
@@ -237,10 +237,13 @@ var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressi
237
237
 
238
238
  // src/core/ast/expression-builders.ts
239
239
  var valueToOperand = (value) => {
240
- if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
241
- return { type: "Literal", value: value === void 0 ? null : value };
240
+ if (isOperandNode(value)) {
241
+ return value;
242
242
  }
243
- return value;
243
+ return {
244
+ type: "Literal",
245
+ value
246
+ };
244
247
  };
245
248
  var toNode = (col2) => {
246
249
  if (isOperandNode(col2)) return col2;
@@ -251,10 +254,11 @@ var toLiteralNode = (value) => ({
251
254
  type: "Literal",
252
255
  value
253
256
  });
257
+ var isLiteralValue = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
258
+ var isValueOperandInput = (value) => isOperandNode(value) || isLiteralValue(value);
254
259
  var toOperand = (val) => {
255
- if (val === null) return { type: "Literal", value: null };
256
- if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
257
- return { type: "Literal", value: val };
260
+ if (isLiteralValue(val)) {
261
+ return valueToOperand(val);
258
262
  }
259
263
  return toNode(val);
260
264
  };
@@ -400,6 +404,62 @@ var windowFunction = (name, args = [], partitionBy, orderBy) => {
400
404
  return buildWindowFunction(name, nodeArgs, partitionNodes, orderNodes);
401
405
  };
402
406
 
407
+ // src/core/sql/sql.ts
408
+ var SQL_OPERATORS = {
409
+ /** Equality operator */
410
+ EQUALS: "=",
411
+ /** Not equals operator */
412
+ NOT_EQUALS: "!=",
413
+ /** Greater than operator */
414
+ GREATER_THAN: ">",
415
+ /** Greater than or equal operator */
416
+ GREATER_OR_EQUAL: ">=",
417
+ /** Less than operator */
418
+ LESS_THAN: "<",
419
+ /** Less than or equal operator */
420
+ LESS_OR_EQUAL: "<=",
421
+ /** LIKE pattern matching operator */
422
+ LIKE: "LIKE",
423
+ /** NOT LIKE pattern matching operator */
424
+ NOT_LIKE: "NOT LIKE",
425
+ /** IN membership operator */
426
+ IN: "IN",
427
+ /** NOT IN membership operator */
428
+ NOT_IN: "NOT IN",
429
+ /** BETWEEN range operator */
430
+ BETWEEN: "BETWEEN",
431
+ /** NOT BETWEEN range operator */
432
+ NOT_BETWEEN: "NOT BETWEEN",
433
+ /** IS NULL null check operator */
434
+ IS_NULL: "IS NULL",
435
+ /** IS NOT NULL null check operator */
436
+ IS_NOT_NULL: "IS NOT NULL",
437
+ /** Logical AND operator */
438
+ AND: "AND",
439
+ /** Logical OR operator */
440
+ OR: "OR",
441
+ /** EXISTS operator */
442
+ EXISTS: "EXISTS",
443
+ /** NOT EXISTS operator */
444
+ NOT_EXISTS: "NOT EXISTS"
445
+ };
446
+ var JOIN_KINDS = {
447
+ /** INNER JOIN type */
448
+ INNER: "INNER",
449
+ /** LEFT JOIN type */
450
+ LEFT: "LEFT",
451
+ /** RIGHT JOIN type */
452
+ RIGHT: "RIGHT",
453
+ /** CROSS JOIN type */
454
+ CROSS: "CROSS"
455
+ };
456
+ var ORDER_DIRECTIONS = {
457
+ /** Ascending order */
458
+ ASC: "ASC",
459
+ /** Descending order */
460
+ DESC: "DESC"
461
+ };
462
+
403
463
  // src/core/ast/aggregate-functions.ts
404
464
  var buildAggregate = (name) => (col2) => ({
405
465
  type: "Function",
@@ -409,6 +469,20 @@ var buildAggregate = (name) => (col2) => ({
409
469
  var count = buildAggregate("COUNT");
410
470
  var sum = buildAggregate("SUM");
411
471
  var avg = buildAggregate("AVG");
472
+ var min = buildAggregate("MIN");
473
+ var max = buildAggregate("MAX");
474
+ var toOrderByNode = (order) => ({
475
+ type: "OrderBy",
476
+ column: columnOperand(order.column),
477
+ direction: order.direction ?? ORDER_DIRECTIONS.ASC
478
+ });
479
+ var groupConcat = (col2, options) => ({
480
+ type: "Function",
481
+ name: "GROUP_CONCAT",
482
+ args: [columnOperand(col2)],
483
+ orderBy: options?.orderBy?.map(toOrderByNode),
484
+ separator: options?.separator !== void 0 ? valueToOperand(options.separator) : void 0
485
+ });
412
486
 
413
487
  // src/core/ast/expression-visitor.ts
414
488
  var expressionDispatchers = /* @__PURE__ */ new Map();
@@ -500,12 +574,17 @@ var toTableRef = (table) => ({
500
574
  });
501
575
 
502
576
  // src/core/functions/standard-strategy.ts
503
- var StandardFunctionStrategy = class {
577
+ var StandardFunctionStrategy = class _StandardFunctionStrategy {
504
578
  constructor() {
505
579
  this.renderers = /* @__PURE__ */ new Map();
506
580
  this.registerStandard();
507
581
  }
508
582
  registerStandard() {
583
+ this.add("COUNT", ({ compiledArgs }) => `COUNT(${compiledArgs.join(", ")})`);
584
+ this.add("SUM", ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
585
+ this.add("AVG", ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
586
+ this.add("MIN", ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
587
+ this.add("MAX", ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
509
588
  this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
510
589
  this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
511
590
  this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
@@ -532,6 +611,7 @@ var StandardFunctionStrategy = class {
532
611
  this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
533
612
  this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
534
613
  this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
614
+ this.add("GROUP_CONCAT", (ctx) => this.renderGroupConcat(ctx));
535
615
  }
536
616
  add(name, renderer) {
537
617
  this.renderers.set(name, renderer);
@@ -539,10 +619,40 @@ var StandardFunctionStrategy = class {
539
619
  getRenderer(name) {
540
620
  return this.renderers.get(name);
541
621
  }
622
+ renderGroupConcat(ctx) {
623
+ const arg = ctx.compiledArgs[0];
624
+ const orderClause = this.buildOrderByExpression(ctx);
625
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
626
+ const separatorClause = this.formatGroupConcatSeparator(ctx);
627
+ return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
628
+ }
629
+ buildOrderByExpression(ctx) {
630
+ const orderBy = ctx.node.orderBy;
631
+ if (!orderBy || orderBy.length === 0) {
632
+ return "";
633
+ }
634
+ const parts = orderBy.map((order) => `${ctx.compileOperand(order.column)} ${order.direction}`);
635
+ return `ORDER BY ${parts.join(", ")}`;
636
+ }
637
+ formatGroupConcatSeparator(ctx) {
638
+ if (!ctx.node.separator) {
639
+ return "";
640
+ }
641
+ return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
642
+ }
643
+ getGroupConcatSeparatorOperand(ctx) {
644
+ return ctx.node.separator ?? _StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
645
+ }
646
+ static {
647
+ this.DEFAULT_GROUP_CONCAT_SEPARATOR = {
648
+ type: "Literal",
649
+ value: ","
650
+ };
651
+ }
542
652
  };
543
653
 
544
654
  // src/core/dialect/abstract.ts
545
- var Dialect = class {
655
+ var Dialect = class _Dialect {
546
656
  /**
547
657
  * Compiles a SELECT query AST to SQL
548
658
  * @param ast - Query AST to compile
@@ -715,6 +825,35 @@ var Dialect = class {
715
825
  this.registerDefaultOperandCompilers();
716
826
  this.registerDefaultExpressionCompilers();
717
827
  }
828
+ /**
829
+ * Creates a new Dialect instance (for testing purposes)
830
+ * @param functionStrategy - Optional function strategy
831
+ * @returns New Dialect instance
832
+ */
833
+ static create(functionStrategy) {
834
+ class TestDialect extends _Dialect {
835
+ constructor() {
836
+ super(...arguments);
837
+ this.dialect = "sqlite";
838
+ }
839
+ quoteIdentifier(id) {
840
+ return `"${id}"`;
841
+ }
842
+ compileSelectAst() {
843
+ throw new Error("Not implemented");
844
+ }
845
+ compileInsertAst() {
846
+ throw new Error("Not implemented");
847
+ }
848
+ compileUpdateAst() {
849
+ throw new Error("Not implemented");
850
+ }
851
+ compileDeleteAst() {
852
+ throw new Error("Not implemented");
853
+ }
854
+ }
855
+ return new TestDialect(functionStrategy);
856
+ }
718
857
  /**
719
858
  * Registers an expression compiler for a specific node type
720
859
  * @param type - Expression node type
@@ -856,7 +995,11 @@ var Dialect = class {
856
995
  const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
857
996
  const renderer = this.functionStrategy.getRenderer(fnNode.name);
858
997
  if (renderer) {
859
- return renderer({ node: fnNode, compiledArgs });
998
+ return renderer({
999
+ node: fnNode,
1000
+ compiledArgs,
1001
+ compileOperand: (operand) => this.compileOperand(operand, ctx)
1002
+ });
860
1003
  }
861
1004
  return `${fnNode.name}(${compiledArgs.join(", ")})`;
862
1005
  }
@@ -1279,6 +1422,14 @@ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1279
1422
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1280
1423
  return `DATE_TRUNC('${partClean}', ${date})`;
1281
1424
  });
1425
+ this.add("GROUP_CONCAT", (ctx) => {
1426
+ const arg = ctx.compiledArgs[0];
1427
+ const orderClause = this.buildOrderByExpression(ctx);
1428
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
1429
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1430
+ const separator = ctx.compileOperand(separatorOperand);
1431
+ return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
1432
+ });
1282
1433
  }
1283
1434
  };
1284
1435
 
@@ -1523,6 +1674,12 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1523
1674
  }
1524
1675
  return `date(${date}, 'start of ${partClean}')`;
1525
1676
  });
1677
+ this.add("GROUP_CONCAT", (ctx) => {
1678
+ const arg = ctx.compiledArgs[0];
1679
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1680
+ const separator = ctx.compileOperand(separatorOperand);
1681
+ return `GROUP_CONCAT(${arg}, ${separator})`;
1682
+ });
1526
1683
  }
1527
1684
  };
1528
1685
 
@@ -1639,6 +1796,14 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1639
1796
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1640
1797
  return `DATETRUNC(${partClean}, ${date})`;
1641
1798
  });
1799
+ this.add("GROUP_CONCAT", (ctx) => {
1800
+ const arg = ctx.compiledArgs[0];
1801
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1802
+ const separator = ctx.compileOperand(separatorOperand);
1803
+ const orderClause = this.buildOrderByExpression(ctx);
1804
+ const withinGroup = orderClause ? ` WITHIN GROUP (${orderClause})` : "";
1805
+ return `STRING_AGG(${arg}, ${separator})${withinGroup}`;
1806
+ });
1642
1807
  }
1643
1808
  };
1644
1809
 
@@ -2008,62 +2173,6 @@ var createJoinNode = (kind, tableName, condition, relationName) => ({
2008
2173
  meta: relationName ? { relationName } : void 0
2009
2174
  });
2010
2175
 
2011
- // src/core/sql/sql.ts
2012
- var SQL_OPERATORS = {
2013
- /** Equality operator */
2014
- EQUALS: "=",
2015
- /** Not equals operator */
2016
- NOT_EQUALS: "!=",
2017
- /** Greater than operator */
2018
- GREATER_THAN: ">",
2019
- /** Greater than or equal operator */
2020
- GREATER_OR_EQUAL: ">=",
2021
- /** Less than operator */
2022
- LESS_THAN: "<",
2023
- /** Less than or equal operator */
2024
- LESS_OR_EQUAL: "<=",
2025
- /** LIKE pattern matching operator */
2026
- LIKE: "LIKE",
2027
- /** NOT LIKE pattern matching operator */
2028
- NOT_LIKE: "NOT LIKE",
2029
- /** IN membership operator */
2030
- IN: "IN",
2031
- /** NOT IN membership operator */
2032
- NOT_IN: "NOT IN",
2033
- /** BETWEEN range operator */
2034
- BETWEEN: "BETWEEN",
2035
- /** NOT BETWEEN range operator */
2036
- NOT_BETWEEN: "NOT BETWEEN",
2037
- /** IS NULL null check operator */
2038
- IS_NULL: "IS NULL",
2039
- /** IS NOT NULL null check operator */
2040
- IS_NOT_NULL: "IS NOT NULL",
2041
- /** Logical AND operator */
2042
- AND: "AND",
2043
- /** Logical OR operator */
2044
- OR: "OR",
2045
- /** EXISTS operator */
2046
- EXISTS: "EXISTS",
2047
- /** NOT EXISTS operator */
2048
- NOT_EXISTS: "NOT EXISTS"
2049
- };
2050
- var JOIN_KINDS = {
2051
- /** INNER JOIN type */
2052
- INNER: "INNER",
2053
- /** LEFT JOIN type */
2054
- LEFT: "LEFT",
2055
- /** RIGHT JOIN type */
2056
- RIGHT: "RIGHT",
2057
- /** CROSS JOIN type */
2058
- CROSS: "CROSS"
2059
- };
2060
- var ORDER_DIRECTIONS = {
2061
- /** Ascending order */
2062
- ASC: "ASC",
2063
- /** Descending order */
2064
- DESC: "DESC"
2065
- };
2066
-
2067
2176
  // src/query-builder/hydration-manager.ts
2068
2177
  var HydrationManager = class _HydrationManager {
2069
2178
  /**
@@ -4107,31 +4216,43 @@ var flattenResults = (results) => {
4107
4216
  }
4108
4217
  return rows;
4109
4218
  };
4110
- async function executeHydrated(ctx, qb) {
4219
+ var executeWithEntityContext = async (entityCtx, qb) => {
4111
4220
  const ast = qb.getAST();
4112
- const compiled = ctx.dialect.compileSelect(ast);
4113
- const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
4221
+ const compiled = entityCtx.dialect.compileSelect(ast);
4222
+ const executed = await entityCtx.executor.executeSql(compiled.sql, compiled.params);
4114
4223
  const rows = flattenResults(executed);
4115
4224
  if (ast.setOps && ast.setOps.length > 0) {
4116
- return rows.map(
4117
- (row) => createEntityProxy(ctx, qb.getTable(), row, qb.getLazyRelations())
4118
- );
4225
+ return rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
4119
4226
  }
4120
4227
  const hydrated = hydrateRows(rows, qb.getHydrationPlan());
4121
- return hydrated.map(
4122
- (row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
4123
- );
4228
+ return hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
4229
+ };
4230
+ async function executeHydrated(session, qb) {
4231
+ return executeWithEntityContext(session, qb);
4232
+ }
4233
+ async function executeHydratedWithContexts(_execCtx, hydCtx, qb) {
4234
+ const entityCtx = hydCtx.entityContext;
4235
+ if (!entityCtx) {
4236
+ throw new Error("Hydration context is missing an EntityContext");
4237
+ }
4238
+ return executeWithEntityContext(entityCtx, qb);
4124
4239
  }
4125
4240
 
4126
4241
  // src/query-builder/select.ts
4127
4242
  var SelectQueryBuilder = class _SelectQueryBuilder {
4128
4243
  /**
4129
- * Creates a new SelectQueryBuilder instance
4130
- * @param table - Table definition to query
4131
- * @param state - Optional initial query state
4132
- * @param hydration - Optional hydration manager
4133
- * @param dependencies - Optional query builder dependencies
4134
- */
4244
+
4245
+ * Creates a new SelectQueryBuilder instance
4246
+
4247
+ * @param table - Table definition to query
4248
+
4249
+ * @param state - Optional initial query state
4250
+
4251
+ * @param hydration - Optional hydration manager
4252
+
4253
+ * @param dependencies - Optional query builder dependencies
4254
+
4255
+ */
4135
4256
  constructor(table, state, hydration, dependencies, lazyRelations) {
4136
4257
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
4137
4258
  this.env = { table, deps };
@@ -4168,112 +4289,183 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4168
4289
  return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
4169
4290
  }
4170
4291
  /**
4171
- * Selects specific columns for the query
4172
- * @param columns - Record of column definitions, function nodes, case expressions, or window functions
4173
- * @returns New query builder instance with selected columns
4174
- */
4292
+
4293
+ * Selects specific columns for the query
4294
+
4295
+ * @param columns - Record of column definitions, function nodes, case expressions, or window functions
4296
+
4297
+ * @returns New query builder instance with selected columns
4298
+
4299
+ */
4175
4300
  select(columns) {
4176
4301
  return this.clone(this.columnSelector.select(this.context, columns));
4177
4302
  }
4178
4303
  /**
4179
- * Selects raw column expressions
4180
- * @param cols - Column expressions as strings
4181
- * @returns New query builder instance with raw column selections
4304
+ * Selects columns from the root table by name (typed).
4305
+ * @param cols - Column names on the root table
4182
4306
  */
4307
+ selectColumns(...cols) {
4308
+ const selection = {};
4309
+ for (const key of cols) {
4310
+ const col2 = this.env.table.columns[key];
4311
+ if (!col2) {
4312
+ throw new Error(`Column '${key}' not found on table '${this.env.table.name}'`);
4313
+ }
4314
+ selection[key] = col2;
4315
+ }
4316
+ return this.select(selection);
4317
+ }
4318
+ /**
4319
+
4320
+ * Selects raw column expressions
4321
+
4322
+ * @param cols - Column expressions as strings
4323
+
4324
+ * @returns New query builder instance with raw column selections
4325
+
4326
+ */
4183
4327
  selectRaw(...cols) {
4184
4328
  return this.clone(this.columnSelector.selectRaw(this.context, cols));
4185
4329
  }
4186
4330
  /**
4187
- * Adds a Common Table Expression (CTE) to the query
4188
- * @param name - Name of the CTE
4189
- * @param query - Query builder or query node for the CTE
4190
- * @param columns - Optional column names for the CTE
4191
- * @returns New query builder instance with the CTE
4192
- */
4331
+
4332
+ * Adds a Common Table Expression (CTE) to the query
4333
+
4334
+ * @param name - Name of the CTE
4335
+
4336
+ * @param query - Query builder or query node for the CTE
4337
+
4338
+ * @param columns - Optional column names for the CTE
4339
+
4340
+ * @returns New query builder instance with the CTE
4341
+
4342
+ */
4193
4343
  with(name, query, columns) {
4194
4344
  const subAst = this.resolveQueryNode(query);
4195
4345
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, false));
4196
4346
  return this.clone(nextContext);
4197
4347
  }
4198
4348
  /**
4199
- * Adds a recursive Common Table Expression (CTE) to the query
4200
- * @param name - Name of the CTE
4201
- * @param query - Query builder or query node for the CTE
4202
- * @param columns - Optional column names for the CTE
4203
- * @returns New query builder instance with the recursive CTE
4204
- */
4349
+
4350
+ * Adds a recursive Common Table Expression (CTE) to the query
4351
+
4352
+ * @param name - Name of the CTE
4353
+
4354
+ * @param query - Query builder or query node for the CTE
4355
+
4356
+ * @param columns - Optional column names for the CTE
4357
+
4358
+ * @returns New query builder instance with the recursive CTE
4359
+
4360
+ */
4205
4361
  withRecursive(name, query, columns) {
4206
4362
  const subAst = this.resolveQueryNode(query);
4207
4363
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
4208
4364
  return this.clone(nextContext);
4209
4365
  }
4210
4366
  /**
4211
- * Selects a subquery as a column
4212
- * @param alias - Alias for the subquery column
4213
- * @param sub - Query builder or query node for the subquery
4214
- * @returns New query builder instance with the subquery selection
4215
- */
4367
+
4368
+ * Selects a subquery as a column
4369
+
4370
+ * @param alias - Alias for the subquery column
4371
+
4372
+ * @param sub - Query builder or query node for the subquery
4373
+
4374
+ * @returns New query builder instance with the subquery selection
4375
+
4376
+ */
4216
4377
  selectSubquery(alias, sub) {
4217
4378
  const query = this.resolveQueryNode(sub);
4218
4379
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
4219
4380
  }
4220
4381
  /**
4221
- * Adds an INNER JOIN to the query
4222
- * @param table - Table to join
4223
- * @param condition - Join condition expression
4224
- * @returns New query builder instance with the INNER JOIN
4225
- */
4382
+
4383
+ * Adds an INNER JOIN to the query
4384
+
4385
+ * @param table - Table to join
4386
+
4387
+ * @param condition - Join condition expression
4388
+
4389
+ * @returns New query builder instance with the INNER JOIN
4390
+
4391
+ */
4226
4392
  innerJoin(table, condition) {
4227
4393
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
4228
4394
  return this.clone(nextContext);
4229
4395
  }
4230
4396
  /**
4231
- * Adds a LEFT JOIN to the query
4232
- * @param table - Table to join
4233
- * @param condition - Join condition expression
4234
- * @returns New query builder instance with the LEFT JOIN
4235
- */
4397
+
4398
+ * Adds a LEFT JOIN to the query
4399
+
4400
+ * @param table - Table to join
4401
+
4402
+ * @param condition - Join condition expression
4403
+
4404
+ * @returns New query builder instance with the LEFT JOIN
4405
+
4406
+ */
4236
4407
  leftJoin(table, condition) {
4237
4408
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
4238
4409
  return this.clone(nextContext);
4239
4410
  }
4240
4411
  /**
4241
- * Adds a RIGHT JOIN to the query
4242
- * @param table - Table to join
4243
- * @param condition - Join condition expression
4244
- * @returns New query builder instance with the RIGHT JOIN
4245
- */
4412
+
4413
+ * Adds a RIGHT JOIN to the query
4414
+
4415
+ * @param table - Table to join
4416
+
4417
+ * @param condition - Join condition expression
4418
+
4419
+ * @returns New query builder instance with the RIGHT JOIN
4420
+
4421
+ */
4246
4422
  rightJoin(table, condition) {
4247
4423
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
4248
4424
  return this.clone(nextContext);
4249
4425
  }
4250
4426
  /**
4251
- * Matches records based on a relationship
4252
- * @param relationName - Name of the relationship to match
4253
- * @param predicate - Optional predicate expression
4254
- * @returns New query builder instance with the relationship match
4255
- */
4427
+
4428
+ * Matches records based on a relationship
4429
+
4430
+ * @param relationName - Name of the relationship to match
4431
+
4432
+ * @param predicate - Optional predicate expression
4433
+
4434
+ * @returns New query builder instance with the relationship match
4435
+
4436
+ */
4256
4437
  match(relationName, predicate) {
4257
4438
  const nextContext = this.relationManager.match(this.context, relationName, predicate);
4258
4439
  return this.clone(nextContext);
4259
4440
  }
4260
4441
  /**
4261
- * Joins a related table
4262
- * @param relationName - Name of the relationship to join
4263
- * @param joinKind - Type of join (defaults to INNER)
4264
- * @param extraCondition - Optional additional join condition
4265
- * @returns New query builder instance with the relationship join
4266
- */
4442
+
4443
+ * Joins a related table
4444
+
4445
+ * @param relationName - Name of the relationship to join
4446
+
4447
+ * @param joinKind - Type of join (defaults to INNER)
4448
+
4449
+ * @param extraCondition - Optional additional join condition
4450
+
4451
+ * @returns New query builder instance with the relationship join
4452
+
4453
+ */
4267
4454
  joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
4268
4455
  const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
4269
4456
  return this.clone(nextContext);
4270
4457
  }
4271
4458
  /**
4272
- * Includes related data in the query results
4273
- * @param relationName - Name of the relationship to include
4274
- * @param options - Optional include options
4275
- * @returns New query builder instance with the relationship inclusion
4276
- */
4459
+
4460
+ * Includes related data in the query results
4461
+
4462
+ * @param relationName - Name of the relationship to include
4463
+
4464
+ * @param options - Optional include options
4465
+
4466
+ * @returns New query builder instance with the relationship inclusion
4467
+
4468
+ */
4277
4469
  include(relationName, options) {
4278
4470
  const nextContext = this.relationManager.include(this.context, relationName, options);
4279
4471
  return this.clone(nextContext);
@@ -4283,6 +4475,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4283
4475
  nextLazy.add(relationName);
4284
4476
  return this.clone(this.context, nextLazy);
4285
4477
  }
4478
+ /**
4479
+ * Selects columns for a related table in a single hop.
4480
+ */
4481
+ selectRelationColumns(relationName, ...cols) {
4482
+ const relation = this.env.table.relations[relationName];
4483
+ if (!relation) {
4484
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4485
+ }
4486
+ const target = relation.target;
4487
+ for (const col2 of cols) {
4488
+ if (!target.columns[col2]) {
4489
+ throw new Error(
4490
+ `Column '${col2}' not found on related table '${target.name}' for relation '${relationName}'`
4491
+ );
4492
+ }
4493
+ }
4494
+ return this.include(relationName, { columns: cols });
4495
+ }
4496
+ /**
4497
+ * Convenience alias for selecting specific columns from a relation.
4498
+ */
4499
+ includePick(relationName, cols) {
4500
+ return this.selectRelationColumns(relationName, ...cols);
4501
+ }
4502
+ /**
4503
+ * Selects columns for the root table and relations from a single config object.
4504
+ */
4505
+ selectColumnsDeep(config) {
4506
+ let qb = this;
4507
+ if (config.root?.length) {
4508
+ qb = qb.selectColumns(...config.root);
4509
+ }
4510
+ for (const key of Object.keys(config)) {
4511
+ if (key === "root") continue;
4512
+ const relName = key;
4513
+ const cols = config[relName];
4514
+ if (!cols || !cols.length) continue;
4515
+ qb = qb.selectRelationColumns(relName, ...cols);
4516
+ }
4517
+ return qb;
4518
+ }
4286
4519
  getLazyRelations() {
4287
4520
  return Array.from(this.lazyRelations);
4288
4521
  }
@@ -4292,125 +4525,186 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4292
4525
  async execute(ctx) {
4293
4526
  return executeHydrated(ctx, this);
4294
4527
  }
4528
+ async executeWithContexts(execCtx, hydCtx) {
4529
+ return executeHydratedWithContexts(execCtx, hydCtx, this);
4530
+ }
4295
4531
  /**
4296
- * Adds a WHERE condition to the query
4297
- * @param expr - Expression for the WHERE clause
4298
- * @returns New query builder instance with the WHERE condition
4299
- */
4532
+
4533
+ * Adds a WHERE condition to the query
4534
+
4535
+ * @param expr - Expression for the WHERE clause
4536
+
4537
+ * @returns New query builder instance with the WHERE condition
4538
+
4539
+ */
4300
4540
  where(expr) {
4301
4541
  const nextContext = this.applyAst(this.context, (service) => service.withWhere(expr));
4302
4542
  return this.clone(nextContext);
4303
4543
  }
4304
4544
  /**
4305
- * Adds a GROUP BY clause to the query
4306
- * @param col - Column definition or column node to group by
4307
- * @returns New query builder instance with the GROUP BY clause
4308
- */
4545
+
4546
+ * Adds a GROUP BY clause to the query
4547
+
4548
+ * @param col - Column definition or column node to group by
4549
+
4550
+ * @returns New query builder instance with the GROUP BY clause
4551
+
4552
+ */
4309
4553
  groupBy(col2) {
4310
4554
  const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col2));
4311
4555
  return this.clone(nextContext);
4312
4556
  }
4313
4557
  /**
4314
- * Adds a HAVING condition to the query
4315
- * @param expr - Expression for the HAVING clause
4316
- * @returns New query builder instance with the HAVING condition
4317
- */
4558
+
4559
+ * Adds a HAVING condition to the query
4560
+
4561
+ * @param expr - Expression for the HAVING clause
4562
+
4563
+ * @returns New query builder instance with the HAVING condition
4564
+
4565
+ */
4318
4566
  having(expr) {
4319
4567
  const nextContext = this.applyAst(this.context, (service) => service.withHaving(expr));
4320
4568
  return this.clone(nextContext);
4321
4569
  }
4322
4570
  /**
4323
- * Adds an ORDER BY clause to the query
4324
- * @param col - Column definition or column node to order by
4325
- * @param direction - Order direction (defaults to ASC)
4326
- * @returns New query builder instance with the ORDER BY clause
4327
- */
4571
+
4572
+ * Adds an ORDER BY clause to the query
4573
+
4574
+ * @param col - Column definition or column node to order by
4575
+
4576
+ * @param direction - Order direction (defaults to ASC)
4577
+
4578
+ * @returns New query builder instance with the ORDER BY clause
4579
+
4580
+ */
4328
4581
  orderBy(col2, direction = ORDER_DIRECTIONS.ASC) {
4329
4582
  const nextContext = this.applyAst(this.context, (service) => service.withOrderBy(col2, direction));
4330
4583
  return this.clone(nextContext);
4331
4584
  }
4332
4585
  /**
4333
- * Adds a DISTINCT clause to the query
4334
- * @param cols - Columns to make distinct
4335
- * @returns New query builder instance with the DISTINCT clause
4336
- */
4586
+
4587
+ * Adds a DISTINCT clause to the query
4588
+
4589
+ * @param cols - Columns to make distinct
4590
+
4591
+ * @returns New query builder instance with the DISTINCT clause
4592
+
4593
+ */
4337
4594
  distinct(...cols) {
4338
4595
  return this.clone(this.columnSelector.distinct(this.context, cols));
4339
4596
  }
4340
4597
  /**
4341
- * Adds a LIMIT clause to the query
4342
- * @param n - Maximum number of rows to return
4343
- * @returns New query builder instance with the LIMIT clause
4344
- */
4598
+
4599
+ * Adds a LIMIT clause to the query
4600
+
4601
+ * @param n - Maximum number of rows to return
4602
+
4603
+ * @returns New query builder instance with the LIMIT clause
4604
+
4605
+ */
4345
4606
  limit(n) {
4346
4607
  const nextContext = this.applyAst(this.context, (service) => service.withLimit(n));
4347
4608
  return this.clone(nextContext);
4348
4609
  }
4349
4610
  /**
4350
- * Adds an OFFSET clause to the query
4351
- * @param n - Number of rows to skip
4352
- * @returns New query builder instance with the OFFSET clause
4353
- */
4611
+
4612
+ * Adds an OFFSET clause to the query
4613
+
4614
+ * @param n - Number of rows to skip
4615
+
4616
+ * @returns New query builder instance with the OFFSET clause
4617
+
4618
+ */
4354
4619
  offset(n) {
4355
4620
  const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
4356
4621
  return this.clone(nextContext);
4357
4622
  }
4358
4623
  /**
4359
- * Combines this query with another using UNION
4360
- * @param query - Query to union with
4361
- * @returns New query builder instance with the set operation
4362
- */
4624
+
4625
+ * Combines this query with another using UNION
4626
+
4627
+ * @param query - Query to union with
4628
+
4629
+ * @returns New query builder instance with the set operation
4630
+
4631
+ */
4363
4632
  union(query) {
4364
4633
  return this.clone(this.applySetOperation("UNION", query));
4365
4634
  }
4366
4635
  /**
4367
- * Combines this query with another using UNION ALL
4368
- * @param query - Query to union with
4369
- * @returns New query builder instance with the set operation
4370
- */
4636
+
4637
+ * Combines this query with another using UNION ALL
4638
+
4639
+ * @param query - Query to union with
4640
+
4641
+ * @returns New query builder instance with the set operation
4642
+
4643
+ */
4371
4644
  unionAll(query) {
4372
4645
  return this.clone(this.applySetOperation("UNION ALL", query));
4373
4646
  }
4374
4647
  /**
4375
- * Combines this query with another using INTERSECT
4376
- * @param query - Query to intersect with
4377
- * @returns New query builder instance with the set operation
4378
- */
4648
+
4649
+ * Combines this query with another using INTERSECT
4650
+
4651
+ * @param query - Query to intersect with
4652
+
4653
+ * @returns New query builder instance with the set operation
4654
+
4655
+ */
4379
4656
  intersect(query) {
4380
4657
  return this.clone(this.applySetOperation("INTERSECT", query));
4381
4658
  }
4382
4659
  /**
4383
- * Combines this query with another using EXCEPT
4384
- * @param query - Query to subtract
4385
- * @returns New query builder instance with the set operation
4386
- */
4660
+
4661
+ * Combines this query with another using EXCEPT
4662
+
4663
+ * @param query - Query to subtract
4664
+
4665
+ * @returns New query builder instance with the set operation
4666
+
4667
+ */
4387
4668
  except(query) {
4388
4669
  return this.clone(this.applySetOperation("EXCEPT", query));
4389
4670
  }
4390
4671
  /**
4391
- * Adds a WHERE EXISTS condition to the query
4392
- * @param subquery - Subquery to check for existence
4393
- * @returns New query builder instance with the WHERE EXISTS condition
4394
- */
4672
+
4673
+ * Adds a WHERE EXISTS condition to the query
4674
+
4675
+ * @param subquery - Subquery to check for existence
4676
+
4677
+ * @returns New query builder instance with the WHERE EXISTS condition
4678
+
4679
+ */
4395
4680
  whereExists(subquery) {
4396
4681
  const subAst = this.resolveQueryNode(subquery);
4397
4682
  return this.where(exists(subAst));
4398
4683
  }
4399
4684
  /**
4400
- * Adds a WHERE NOT EXISTS condition to the query
4401
- * @param subquery - Subquery to check for non-existence
4402
- * @returns New query builder instance with the WHERE NOT EXISTS condition
4403
- */
4685
+
4686
+ * Adds a WHERE NOT EXISTS condition to the query
4687
+
4688
+ * @param subquery - Subquery to check for non-existence
4689
+
4690
+ * @returns New query builder instance with the WHERE NOT EXISTS condition
4691
+
4692
+ */
4404
4693
  whereNotExists(subquery) {
4405
4694
  const subAst = this.resolveQueryNode(subquery);
4406
4695
  return this.where(notExists(subAst));
4407
4696
  }
4408
4697
  /**
4409
- * Adds a WHERE EXISTS condition based on a relationship
4410
- * @param relationName - Name of the relationship to check
4411
- * @param callback - Optional callback to modify the relationship query
4412
- * @returns New query builder instance with the relationship existence check
4413
- */
4698
+
4699
+ * Adds a WHERE EXISTS condition based on a relationship
4700
+
4701
+ * @param relationName - Name of the relationship to check
4702
+
4703
+ * @param callback - Optional callback to modify the relationship query
4704
+
4705
+ * @returns New query builder instance with the relationship existence check
4706
+
4707
+ */
4414
4708
  whereHas(relationName, callback) {
4415
4709
  const relation = this.env.table.relations[relationName];
4416
4710
  if (!relation) {
@@ -4425,11 +4719,16 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4425
4719
  return this.where(exists(finalSubAst));
4426
4720
  }
4427
4721
  /**
4428
- * Adds a WHERE NOT EXISTS condition based on a relationship
4429
- * @param relationName - Name of the relationship to check
4430
- * @param callback - Optional callback to modify the relationship query
4431
- * @returns New query builder instance with the relationship non-existence check
4432
- */
4722
+
4723
+ * Adds a WHERE NOT EXISTS condition based on a relationship
4724
+
4725
+ * @param relationName - Name of the relationship to check
4726
+
4727
+ * @param callback - Optional callback to modify the relationship query
4728
+
4729
+ * @returns New query builder instance with the relationship non-existence check
4730
+
4731
+ */
4433
4732
  whereHasNot(relationName, callback) {
4434
4733
  const relation = this.env.table.relations[relationName];
4435
4734
  if (!relation) {
@@ -4444,33 +4743,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4444
4743
  return this.where(notExists(finalSubAst));
4445
4744
  }
4446
4745
  /**
4447
- * Compiles the query to SQL for a specific dialect
4448
- * @param dialect - Database dialect to compile for
4449
- * @returns Compiled query with SQL and parameters
4450
- */
4746
+
4747
+ * Compiles the query to SQL for a specific dialect
4748
+
4749
+ * @param dialect - Database dialect to compile for
4750
+
4751
+ * @returns Compiled query with SQL and parameters
4752
+
4753
+ */
4451
4754
  compile(dialect) {
4452
4755
  const resolved = resolveDialectInput(dialect);
4453
4756
  return resolved.compileSelect(this.context.state.ast);
4454
4757
  }
4455
4758
  /**
4456
- * Converts the query to SQL string for a specific dialect
4457
- * @param dialect - Database dialect to generate SQL for
4458
- * @returns SQL string representation of the query
4459
- */
4759
+
4760
+ * Converts the query to SQL string for a specific dialect
4761
+
4762
+ * @param dialect - Database dialect to generate SQL for
4763
+
4764
+ * @returns SQL string representation of the query
4765
+
4766
+ */
4460
4767
  toSql(dialect) {
4461
4768
  return this.compile(dialect).sql;
4462
4769
  }
4463
4770
  /**
4464
- * Gets the hydration plan for the query
4465
- * @returns Hydration plan or undefined if none exists
4466
- */
4771
+
4772
+ * Gets the hydration plan for the query
4773
+
4774
+ * @returns Hydration plan or undefined if none exists
4775
+
4776
+ */
4467
4777
  getHydrationPlan() {
4468
4778
  return this.context.hydration.getPlan();
4469
4779
  }
4470
4780
  /**
4471
- * Gets the Abstract Syntax Tree (AST) representation of the query
4472
- * @returns Query AST with hydration applied
4473
- */
4781
+
4782
+ * Gets the Abstract Syntax Tree (AST) representation of the query
4783
+
4784
+ * @returns Query AST with hydration applied
4785
+
4786
+ */
4474
4787
  getAST() {
4475
4788
  return this.context.hydration.applyToAst(this.context.state.ast);
4476
4789
  }
@@ -4478,6 +4791,54 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4478
4791
  var createColumn = (table, name) => ({ type: "Column", table, name });
4479
4792
  var createLiteral = (val) => ({ type: "Literal", value: val });
4480
4793
 
4794
+ // src/orm/entity-metadata.ts
4795
+ var metadataMap = /* @__PURE__ */ new Map();
4796
+ var getEntityMetadata = (target) => {
4797
+ return metadataMap.get(target);
4798
+ };
4799
+
4800
+ // src/decorators/bootstrap.ts
4801
+ var getTableDefFromEntity = (ctor) => {
4802
+ const meta = getEntityMetadata(ctor);
4803
+ if (!meta) return void 0;
4804
+ return meta.table;
4805
+ };
4806
+ var selectFromEntity = (ctor) => {
4807
+ const table = getTableDefFromEntity(ctor);
4808
+ if (!table) {
4809
+ throw new Error("Entity metadata has not been bootstrapped");
4810
+ }
4811
+ return new SelectQueryBuilder(table);
4812
+ };
4813
+
4814
+ // src/query-builder/select-helpers.ts
4815
+ function sel(table, ...cols) {
4816
+ const selection = {};
4817
+ for (const col2 of cols) {
4818
+ const def = table.columns[col2];
4819
+ if (!def) {
4820
+ throw new Error(`Column '${col2}' not found on table '${table.name}'`);
4821
+ }
4822
+ selection[col2] = def;
4823
+ }
4824
+ return selection;
4825
+ }
4826
+ function esel(entity, ...props) {
4827
+ const table = getTableDefFromEntity(entity);
4828
+ if (!table) {
4829
+ throw new Error(`No table definition registered for entity '${entity.name}'`);
4830
+ }
4831
+ const selection = {};
4832
+ for (const prop of props) {
4833
+ const col2 = table.columns[prop];
4834
+ if (!col2) {
4835
+ throw new Error(`No column '${prop}' found for entity '${entity.name}'`);
4836
+ }
4837
+ selection[prop] = col2;
4838
+ }
4839
+ return selection;
4840
+ }
4841
+
4481
4842
  // src/query-builder/insert-query-state.ts
4482
4843
  var InsertQueryState = class _InsertQueryState {
4483
4844
  constructor(table, ast) {
@@ -4496,7 +4857,15 @@ var InsertQueryState = class _InsertQueryState {
4496
4857
  if (!rows.length) return this;
4497
4858
  const definedColumns = this.ast.columns.length ? this.ast.columns : buildColumnNodes(this.table, Object.keys(rows[0]));
4498
4859
  const newRows = rows.map(
4499
- (row) => definedColumns.map((column) => valueToOperand(row[column.name]))
4860
+ (row, rowIndex) => definedColumns.map((column) => {
4861
+ const rawValue = row[column.name];
4862
+ if (!isValueOperandInput(rawValue)) {
4863
+ throw new Error(
4864
+ `Invalid insert value for column "${column.name}" in row ${rowIndex}: only primitives, null, or OperandNodes are allowed`
4865
+ );
4866
+ }
4867
+ return valueToOperand(rawValue);
4868
+ })
4500
4869
  );
4501
4870
  return this.clone({
4502
4871
  ...this.ast,
@@ -4547,6 +4916,17 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
4547
4916
  };
4548
4917
 
4549
4918
  // src/query-builder/update-query-state.ts
4919
+ var isUpdateValue = (value) => {
4920
+ if (value === null) return true;
4921
+ switch (typeof value) {
4922
+ case "string":
4923
+ case "number":
4924
+ case "boolean":
4925
+ return true;
4926
+ default:
4927
+ return isOperandNode(value);
4928
+ }
4929
+ };
4550
4930
  var UpdateQueryState = class _UpdateQueryState {
4551
4931
  constructor(table, ast) {
4552
4932
  this.table = table;
@@ -4560,14 +4940,21 @@ var UpdateQueryState = class _UpdateQueryState {
4560
4940
  return new _UpdateQueryState(this.table, nextAst);
4561
4941
  }
4562
4942
  withSet(values) {
4563
- const assignments = Object.entries(values).map(([column, value]) => ({
4564
- column: {
4565
- type: "Column",
4566
- table: this.table.name,
4567
- name: column
4568
- },
4569
- value: valueToOperand(value)
4570
- }));
4943
+ const assignments = Object.entries(values).map(([column, rawValue]) => {
4944
+ if (!isUpdateValue(rawValue)) {
4945
+ throw new Error(
4946
+ `Invalid update value for column "${column}": only primitives, null, or OperandNodes are allowed`
4947
+ );
4948
+ }
4949
+ return {
4950
+ column: {
4951
+ type: "Column",
4952
+ table: this.table.name,
4953
+ name: column
4954
+ },
4955
+ value: valueToOperand(rawValue)
4956
+ };
4957
+ });
4571
4958
  return this.clone({
4572
4959
  ...this.ast,
4573
4960
  set: assignments
@@ -5768,20 +6155,55 @@ var SQL_OPERATOR_REGISTRY = {
5768
6155
  [SQL_OPERATORS.NOT_EXISTS]: { sql: SQL_OPERATORS.NOT_EXISTS, tsName: "notExists" }
5769
6156
  };
5770
6157
 
5771
- // src/codegen/typescript.ts
5772
- var capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
5773
- var assertNever2 = (value) => {
5774
- throw new Error(`Unhandled SQL operator: ${value}`);
5775
- };
5776
- var TypeScriptGenerator = class {
6158
+ // src/codegen/naming-strategy.ts
6159
+ var DefaultNamingStrategy = class {
5777
6160
  /**
5778
- * Generates TypeScript code from a query AST
5779
- * @param ast - Query AST to generate code from
5780
- * @returns Generated TypeScript code
6161
+ * Converts table names to TypeScript symbols
6162
+ * @param table - Table node, function table node, or string name
6163
+ * @returns Capitalized table name (handles schema-qualified names)
5781
6164
  */
5782
- generate(ast) {
5783
- const chainLines = this.buildSelectLines(ast);
5784
- const lines = chainLines.map((line, index) => index === 0 ? `const query = ${line}` : line);
6165
+ tableToSymbol(table) {
6166
+ const tableName = typeof table === "string" ? table : table.name;
6167
+ if (tableName.includes(".")) {
6168
+ return tableName.split(".").map((part) => this.capitalize(part)).join("");
6169
+ }
6170
+ return this.capitalize(tableName);
6171
+ }
6172
+ /**
6173
+ * Converts column references to property names
6174
+ * @param column - Column node
6175
+ * @returns Column name as-is (for backward compatibility)
6176
+ */
6177
+ columnToProperty(column) {
6178
+ return column.name;
6179
+ }
6180
+ /**
6181
+ * Capitalizes the first letter of a string
6182
+ * @param s - String to capitalize
6183
+ * @returns Capitalized string
6184
+ */
6185
+ capitalize(s) {
6186
+ if (!s) return s;
6187
+ return s.charAt(0).toUpperCase() + s.slice(1);
6188
+ }
6189
+ };
6190
+
6191
+ // src/codegen/typescript.ts
6192
+ var assertNever2 = (value) => {
6193
+ throw new Error(`Unhandled SQL operator: ${value}`);
6194
+ };
6195
+ var TypeScriptGenerator = class {
6196
+ constructor(namingStrategy = new DefaultNamingStrategy()) {
6197
+ this.namingStrategy = namingStrategy;
6198
+ }
6199
+ /**
6200
+ * Generates TypeScript code from a query AST
6201
+ * @param ast - Query AST to generate code from
6202
+ * @returns Generated TypeScript code
6203
+ */
6204
+ generate(ast) {
6205
+ const chainLines = this.buildSelectLines(ast);
6206
+ const lines = chainLines.map((line, index) => index === 0 ? `const query = ${line}` : line);
5785
6207
  lines.push(";", "", "await query.execute();");
5786
6208
  return lines.join("\n");
5787
6209
  }
@@ -5800,13 +6222,13 @@ var TypeScriptGenerator = class {
5800
6222
  return `${key}: ${this.printOperand(operand)}`;
5801
6223
  });
5802
6224
  lines.push(`db.select({`);
5803
- selections.forEach((sel, index) => {
5804
- lines.push(` ${sel}${index < selections.length - 1 ? "," : ""}`);
6225
+ selections.forEach((sel2, index) => {
6226
+ lines.push(` ${sel2}${index < selections.length - 1 ? "," : ""}`);
5805
6227
  });
5806
6228
  lines.push(`})`);
5807
- lines.push(`.from(${capitalize(ast.from.name)})`);
6229
+ lines.push(`.from(${this.namingStrategy.tableToSymbol(ast.from)})`);
5808
6230
  if (ast.distinct && ast.distinct.length) {
5809
- const cols = ast.distinct.map((c) => `${capitalize(c.table)}.${c.name}`).join(", ");
6231
+ const cols = ast.distinct.map((c) => `${this.namingStrategy.tableToSymbol(c.table)}.${c.name}`).join(", ");
5810
6232
  lines.push(`.distinct(${cols})`);
5811
6233
  }
5812
6234
  ast.joins.forEach((join) => {
@@ -5821,7 +6243,7 @@ var TypeScriptGenerator = class {
5821
6243
  lines.push(`.joinRelation('${relationName}', '${join.kind}')`);
5822
6244
  }
5823
6245
  } else {
5824
- const table = capitalize(join.table.name);
6246
+ const table = this.namingStrategy.tableToSymbol(join.table);
5825
6247
  const cond = this.printExpression(join.condition);
5826
6248
  let method = "innerJoin";
5827
6249
  if (join.kind === "LEFT") method = "leftJoin";
@@ -5842,7 +6264,7 @@ var TypeScriptGenerator = class {
5842
6264
  lines.push(`.where(${this.printExpression(ast.where)})`);
5843
6265
  }
5844
6266
  if (ast.groupBy && ast.groupBy.length) {
5845
- const cols = ast.groupBy.map((c) => `${capitalize(c.table)}.${c.name}`).join(", ");
6267
+ const cols = ast.groupBy.map((c) => `${this.namingStrategy.tableToSymbol(c.table)}.${c.name}`).join(", ");
5846
6268
  lines.push(`.groupBy(${cols})`);
5847
6269
  }
5848
6270
  if (ast.having) {
@@ -5850,7 +6272,7 @@ var TypeScriptGenerator = class {
5850
6272
  }
5851
6273
  if (ast.orderBy && ast.orderBy.length) {
5852
6274
  ast.orderBy.forEach((o) => {
5853
- lines.push(`.orderBy(${capitalize(o.column.table)}.${o.column.name}, '${o.direction}')`);
6275
+ lines.push(`.orderBy(${this.namingStrategy.tableToSymbol(o.column.table)}.${o.column.name}, '${o.direction}')`);
5854
6276
  });
5855
6277
  }
5856
6278
  if (ast.limit) lines.push(`.limit(${ast.limit})`);
@@ -5989,7 +6411,7 @@ var TypeScriptGenerator = class {
5989
6411
  * @returns TypeScript code representation
5990
6412
  */
5991
6413
  printColumnOperand(column) {
5992
- return `${capitalize(column.table)}.${column.name}`;
6414
+ return `${this.namingStrategy.tableToSymbol(column.table)}.${column.name}`;
5993
6415
  }
5994
6416
  /**
5995
6417
  * Prints a literal operand to TypeScript code
@@ -6015,7 +6437,7 @@ var TypeScriptGenerator = class {
6015
6437
  * @returns TypeScript code representation
6016
6438
  */
6017
6439
  printJsonPathOperand(json) {
6018
- return `jsonPath(${capitalize(json.column.table)}.${json.column.name}, '${json.path}')`;
6440
+ return `jsonPath(${this.namingStrategy.tableToSymbol(json.column.table)}.${json.column.name}, '${json.path}')`;
6019
6441
  }
6020
6442
  /**
6021
6443
  * Prints a scalar subquery operand to TypeScript code
@@ -6051,11 +6473,11 @@ var TypeScriptGenerator = class {
6051
6473
  result += ") OVER (";
6052
6474
  const parts = [];
6053
6475
  if (node.partitionBy && node.partitionBy.length > 0) {
6054
- const partitionClause = "PARTITION BY " + node.partitionBy.map((col2) => `${capitalize(col2.table)}.${col2.name}`).join(", ");
6476
+ const partitionClause = "PARTITION BY " + node.partitionBy.map((col2) => `${this.namingStrategy.tableToSymbol(col2.table)}.${col2.name}`).join(", ");
6055
6477
  parts.push(partitionClause);
6056
6478
  }
6057
6479
  if (node.orderBy && node.orderBy.length > 0) {
6058
- const orderClause = "ORDER BY " + node.orderBy.map((o) => `${capitalize(o.column.table)}.${o.column.name} ${o.direction}`).join(", ");
6480
+ const orderClause = "ORDER BY " + node.orderBy.map((o) => `${this.namingStrategy.tableToSymbol(o.column.table)}.${o.column.name} ${o.direction}`).join(", ");
6059
6481
  parts.push(orderClause);
6060
6482
  }
6061
6483
  result += parts.join(" ");
@@ -6084,48 +6506,6 @@ var TypeScriptGenerator = class {
6084
6506
  }
6085
6507
  };
6086
6508
 
6087
- // src/orm/domain-event-bus.ts
6088
- var DomainEventBus = class {
6089
- constructor(initialHandlers) {
6090
- this.handlers = /* @__PURE__ */ new Map();
6091
- const handlers = initialHandlers ?? {};
6092
- Object.entries(handlers).forEach(([name, list]) => {
6093
- this.handlers.set(name, [...list]);
6094
- });
6095
- }
6096
- register(name, handler) {
6097
- const existing = this.handlers.get(name) ?? [];
6098
- existing.push(handler);
6099
- this.handlers.set(name, existing);
6100
- }
6101
- async dispatch(trackedEntities, ctx) {
6102
- for (const tracked of trackedEntities) {
6103
- const entity = tracked.entity;
6104
- if (!entity.domainEvents || !entity.domainEvents.length) continue;
6105
- for (const event of entity.domainEvents) {
6106
- const eventName = this.getEventName(event);
6107
- const handlers = this.handlers.get(eventName);
6108
- if (!handlers) continue;
6109
- for (const handler of handlers) {
6110
- await handler(event, ctx);
6111
- }
6112
- }
6113
- entity.domainEvents = [];
6114
- }
6115
- }
6116
- getEventName(event) {
6117
- if (!event) return "Unknown";
6118
- if (typeof event === "string") return event;
6119
- return event.constructor?.name ?? "Unknown";
6120
- }
6121
- };
6122
- var addDomainEvent = (entity, event) => {
6123
- if (!entity.domainEvents) {
6124
- entity.domainEvents = [];
6125
- }
6126
- entity.domainEvents.push(event);
6127
- };
6128
-
6129
6509
  // src/orm/identity-map.ts
6130
6510
  var IdentityMap = class {
6131
6511
  constructor() {
@@ -6153,169 +6533,14 @@ var IdentityMap = class {
6153
6533
  const bucket = this.buckets.get(table.name);
6154
6534
  return bucket ? Array.from(bucket.values()) : [];
6155
6535
  }
6536
+ clear() {
6537
+ this.buckets.clear();
6538
+ }
6156
6539
  toIdentityKey(pk) {
6157
6540
  return String(pk);
6158
6541
  }
6159
6542
  };
6160
6543
 
6161
- // src/orm/relation-change-processor.ts
6162
- var RelationChangeProcessor = class {
6163
- constructor(unitOfWork, dialect, executor) {
6164
- this.unitOfWork = unitOfWork;
6165
- this.dialect = dialect;
6166
- this.executor = executor;
6167
- this.relationChanges = [];
6168
- }
6169
- registerChange(entry) {
6170
- this.relationChanges.push(entry);
6171
- }
6172
- async process() {
6173
- if (!this.relationChanges.length) return;
6174
- const entries = [...this.relationChanges];
6175
- this.relationChanges.length = 0;
6176
- for (const entry of entries) {
6177
- switch (entry.relation.type) {
6178
- case RelationKinds.HasMany:
6179
- await this.handleHasManyChange(entry);
6180
- break;
6181
- case RelationKinds.HasOne:
6182
- await this.handleHasOneChange(entry);
6183
- break;
6184
- case RelationKinds.BelongsToMany:
6185
- await this.handleBelongsToManyChange(entry);
6186
- break;
6187
- case RelationKinds.BelongsTo:
6188
- await this.handleBelongsToChange(entry);
6189
- break;
6190
- }
6191
- }
6192
- }
6193
- async handleHasManyChange(entry) {
6194
- const relation = entry.relation;
6195
- const target = entry.change.entity;
6196
- if (!target) return;
6197
- const tracked = this.unitOfWork.findTracked(target);
6198
- if (!tracked) return;
6199
- const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
6200
- const rootValue = entry.root[localKey];
6201
- if (rootValue === void 0 || rootValue === null) return;
6202
- if (entry.change.kind === "add" || entry.change.kind === "attach") {
6203
- this.assignHasManyForeignKey(tracked.entity, relation, rootValue);
6204
- this.unitOfWork.markDirty(tracked.entity);
6205
- return;
6206
- }
6207
- if (entry.change.kind === "remove") {
6208
- this.detachHasManyChild(tracked.entity, relation);
6209
- }
6210
- }
6211
- async handleHasOneChange(entry) {
6212
- const relation = entry.relation;
6213
- const target = entry.change.entity;
6214
- if (!target) return;
6215
- const tracked = this.unitOfWork.findTracked(target);
6216
- if (!tracked) return;
6217
- const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
6218
- const rootValue = entry.root[localKey];
6219
- if (rootValue === void 0 || rootValue === null) return;
6220
- if (entry.change.kind === "attach" || entry.change.kind === "add") {
6221
- this.assignHasOneForeignKey(tracked.entity, relation, rootValue);
6222
- this.unitOfWork.markDirty(tracked.entity);
6223
- return;
6224
- }
6225
- if (entry.change.kind === "remove") {
6226
- this.detachHasOneChild(tracked.entity, relation);
6227
- }
6228
- }
6229
- async handleBelongsToChange(_entry) {
6230
- }
6231
- async handleBelongsToManyChange(entry) {
6232
- const relation = entry.relation;
6233
- const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
6234
- const rootId = entry.root[rootKey];
6235
- if (rootId === void 0 || rootId === null) return;
6236
- const targetId = this.resolvePrimaryKeyValue(entry.change.entity, relation.target);
6237
- if (targetId === null) return;
6238
- if (entry.change.kind === "attach" || entry.change.kind === "add") {
6239
- await this.insertPivotRow(relation, rootId, targetId);
6240
- return;
6241
- }
6242
- if (entry.change.kind === "detach" || entry.change.kind === "remove") {
6243
- await this.deletePivotRow(relation, rootId, targetId);
6244
- if (relation.cascade === "all" || relation.cascade === "remove") {
6245
- this.unitOfWork.markRemoved(entry.change.entity);
6246
- }
6247
- }
6248
- }
6249
- assignHasManyForeignKey(child, relation, rootValue) {
6250
- const current = child[relation.foreignKey];
6251
- if (current === rootValue) return;
6252
- child[relation.foreignKey] = rootValue;
6253
- }
6254
- detachHasManyChild(child, relation) {
6255
- if (relation.cascade === "all" || relation.cascade === "remove") {
6256
- this.unitOfWork.markRemoved(child);
6257
- return;
6258
- }
6259
- child[relation.foreignKey] = null;
6260
- this.unitOfWork.markDirty(child);
6261
- }
6262
- assignHasOneForeignKey(child, relation, rootValue) {
6263
- const current = child[relation.foreignKey];
6264
- if (current === rootValue) return;
6265
- child[relation.foreignKey] = rootValue;
6266
- }
6267
- detachHasOneChild(child, relation) {
6268
- if (relation.cascade === "all" || relation.cascade === "remove") {
6269
- this.unitOfWork.markRemoved(child);
6270
- return;
6271
- }
6272
- child[relation.foreignKey] = null;
6273
- this.unitOfWork.markDirty(child);
6274
- }
6275
- async insertPivotRow(relation, rootId, targetId) {
6276
- const payload = {
6277
- [relation.pivotForeignKeyToRoot]: rootId,
6278
- [relation.pivotForeignKeyToTarget]: targetId
6279
- };
6280
- const builder = new InsertQueryBuilder(relation.pivotTable).values(payload);
6281
- const compiled = builder.compile(this.dialect);
6282
- await this.executor.executeSql(compiled.sql, compiled.params);
6283
- }
6284
- async deletePivotRow(relation, rootId, targetId) {
6285
- const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
6286
- const targetCol = relation.pivotTable.columns[relation.pivotForeignKeyToTarget];
6287
- if (!rootCol || !targetCol) return;
6288
- const builder = new DeleteQueryBuilder(relation.pivotTable).where(
6289
- and(eq(rootCol, rootId), eq(targetCol, targetId))
6290
- );
6291
- const compiled = builder.compile(this.dialect);
6292
- await this.executor.executeSql(compiled.sql, compiled.params);
6293
- }
6294
- resolvePrimaryKeyValue(entity, table) {
6295
- if (!entity) return null;
6296
- const key = findPrimaryKey(table);
6297
- const value = entity[key];
6298
- if (value === void 0 || value === null) return null;
6299
- return value;
6300
- }
6301
- };
6302
-
6303
- // src/orm/transaction-runner.ts
6304
- var runInTransaction = async (executor, action) => {
6305
- if (!executor.beginTransaction) {
6306
- await action();
6307
- return;
6308
- }
6309
- await executor.beginTransaction();
6310
- try {
6311
- await action();
6312
- await executor.commitTransaction?.();
6313
- } catch (error) {
6314
- await executor.rollbackTransaction?.();
6315
- throw error;
6316
- }
6317
- };
6318
-
6319
6544
  // src/orm/runtime-types.ts
6320
6545
  var EntityStatus = /* @__PURE__ */ ((EntityStatus2) => {
6321
6546
  EntityStatus2["New"] = "new";
@@ -6420,6 +6645,10 @@ var UnitOfWork = class {
6420
6645
  }
6421
6646
  }
6422
6647
  }
6648
+ reset() {
6649
+ this.trackedEntities.clear();
6650
+ this.identityMap.clear();
6651
+ }
6423
6652
  async flushInsert(tracked) {
6424
6653
  await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
6425
6654
  const payload = this.extractColumns(tracked.table, tracked.entity);
@@ -6489,6 +6718,7 @@ var UnitOfWork = class {
6489
6718
  extractColumns(table, entity) {
6490
6719
  const payload = {};
6491
6720
  for (const column of Object.keys(table.columns)) {
6721
+ if (entity[column] === void 0) continue;
6492
6722
  payload[column] = entity[column];
6493
6723
  }
6494
6724
  return payload;
@@ -6539,6 +6769,194 @@ var UnitOfWork = class {
6539
6769
  }
6540
6770
  };
6541
6771
 
6772
+ // src/orm/domain-event-bus.ts
6773
+ var DomainEventBus = class {
6774
+ constructor(initialHandlers) {
6775
+ this.handlers = /* @__PURE__ */ new Map();
6776
+ if (initialHandlers) {
6777
+ for (const key in initialHandlers) {
6778
+ const type = key;
6779
+ const list = initialHandlers[type] ?? [];
6780
+ this.handlers.set(type, [...list]);
6781
+ }
6782
+ }
6783
+ }
6784
+ on(type, handler) {
6785
+ const key = type;
6786
+ const existing = this.handlers.get(key) ?? [];
6787
+ existing.push(handler);
6788
+ this.handlers.set(key, existing);
6789
+ }
6790
+ register(type, handler) {
6791
+ this.on(type, handler);
6792
+ }
6793
+ async dispatch(trackedEntities, ctx) {
6794
+ for (const tracked of trackedEntities) {
6795
+ const entity = tracked.entity;
6796
+ if (!entity.domainEvents?.length) continue;
6797
+ for (const event of entity.domainEvents) {
6798
+ const handlers = this.handlers.get(event.type);
6799
+ if (!handlers?.length) continue;
6800
+ for (const handler of handlers) {
6801
+ await handler(event, ctx);
6802
+ }
6803
+ }
6804
+ entity.domainEvents = [];
6805
+ }
6806
+ }
6807
+ };
6808
+ var addDomainEvent = (entity, event) => {
6809
+ if (!entity.domainEvents) {
6810
+ entity.domainEvents = [];
6811
+ }
6812
+ entity.domainEvents.push(event);
6813
+ };
6814
+
6815
+ // src/orm/relation-change-processor.ts
6816
+ var RelationChangeProcessor = class {
6817
+ constructor(unitOfWork, dialect, executor) {
6818
+ this.unitOfWork = unitOfWork;
6819
+ this.dialect = dialect;
6820
+ this.executor = executor;
6821
+ this.relationChanges = [];
6822
+ }
6823
+ registerChange(entry) {
6824
+ this.relationChanges.push(entry);
6825
+ }
6826
+ reset() {
6827
+ this.relationChanges.length = 0;
6828
+ }
6829
+ async process() {
6830
+ if (!this.relationChanges.length) return;
6831
+ const entries = [...this.relationChanges];
6832
+ this.relationChanges.length = 0;
6833
+ for (const entry of entries) {
6834
+ switch (entry.relation.type) {
6835
+ case RelationKinds.HasMany:
6836
+ await this.handleHasManyChange(entry);
6837
+ break;
6838
+ case RelationKinds.HasOne:
6839
+ await this.handleHasOneChange(entry);
6840
+ break;
6841
+ case RelationKinds.BelongsToMany:
6842
+ await this.handleBelongsToManyChange(entry);
6843
+ break;
6844
+ case RelationKinds.BelongsTo:
6845
+ await this.handleBelongsToChange(entry);
6846
+ break;
6847
+ }
6848
+ }
6849
+ }
6850
+ async handleHasManyChange(entry) {
6851
+ const relation = entry.relation;
6852
+ const target = entry.change.entity;
6853
+ if (!target) return;
6854
+ const tracked = this.unitOfWork.findTracked(target);
6855
+ if (!tracked) return;
6856
+ const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
6857
+ const rootValue = entry.root[localKey];
6858
+ if (rootValue === void 0 || rootValue === null) return;
6859
+ if (entry.change.kind === "add" || entry.change.kind === "attach") {
6860
+ this.assignHasManyForeignKey(tracked.entity, relation, rootValue);
6861
+ this.unitOfWork.markDirty(tracked.entity);
6862
+ return;
6863
+ }
6864
+ if (entry.change.kind === "remove") {
6865
+ this.detachHasManyChild(tracked.entity, relation);
6866
+ }
6867
+ }
6868
+ async handleHasOneChange(entry) {
6869
+ const relation = entry.relation;
6870
+ const target = entry.change.entity;
6871
+ if (!target) return;
6872
+ const tracked = this.unitOfWork.findTracked(target);
6873
+ if (!tracked) return;
6874
+ const localKey = relation.localKey || findPrimaryKey(entry.rootTable);
6875
+ const rootValue = entry.root[localKey];
6876
+ if (rootValue === void 0 || rootValue === null) return;
6877
+ if (entry.change.kind === "attach" || entry.change.kind === "add") {
6878
+ this.assignHasOneForeignKey(tracked.entity, relation, rootValue);
6879
+ this.unitOfWork.markDirty(tracked.entity);
6880
+ return;
6881
+ }
6882
+ if (entry.change.kind === "remove") {
6883
+ this.detachHasOneChild(tracked.entity, relation);
6884
+ }
6885
+ }
6886
+ async handleBelongsToChange(_entry) {
6887
+ }
6888
+ async handleBelongsToManyChange(entry) {
6889
+ const relation = entry.relation;
6890
+ const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
6891
+ const rootId = entry.root[rootKey];
6892
+ if (rootId === void 0 || rootId === null) return;
6893
+ const targetId = this.resolvePrimaryKeyValue(entry.change.entity, relation.target);
6894
+ if (targetId === null) return;
6895
+ if (entry.change.kind === "attach" || entry.change.kind === "add") {
6896
+ await this.insertPivotRow(relation, rootId, targetId);
6897
+ return;
6898
+ }
6899
+ if (entry.change.kind === "detach" || entry.change.kind === "remove") {
6900
+ await this.deletePivotRow(relation, rootId, targetId);
6901
+ if (relation.cascade === "all" || relation.cascade === "remove") {
6902
+ this.unitOfWork.markRemoved(entry.change.entity);
6903
+ }
6904
+ }
6905
+ }
6906
+ assignHasManyForeignKey(child, relation, rootValue) {
6907
+ const current = child[relation.foreignKey];
6908
+ if (current === rootValue) return;
6909
+ child[relation.foreignKey] = rootValue;
6910
+ }
6911
+ detachHasManyChild(child, relation) {
6912
+ if (relation.cascade === "all" || relation.cascade === "remove") {
6913
+ this.unitOfWork.markRemoved(child);
6914
+ return;
6915
+ }
6916
+ child[relation.foreignKey] = null;
6917
+ this.unitOfWork.markDirty(child);
6918
+ }
6919
+ assignHasOneForeignKey(child, relation, rootValue) {
6920
+ const current = child[relation.foreignKey];
6921
+ if (current === rootValue) return;
6922
+ child[relation.foreignKey] = rootValue;
6923
+ }
6924
+ detachHasOneChild(child, relation) {
6925
+ if (relation.cascade === "all" || relation.cascade === "remove") {
6926
+ this.unitOfWork.markRemoved(child);
6927
+ return;
6928
+ }
6929
+ child[relation.foreignKey] = null;
6930
+ this.unitOfWork.markDirty(child);
6931
+ }
6932
+ async insertPivotRow(relation, rootId, targetId) {
6933
+ const payload = {
6934
+ [relation.pivotForeignKeyToRoot]: rootId,
6935
+ [relation.pivotForeignKeyToTarget]: targetId
6936
+ };
6937
+ const builder = new InsertQueryBuilder(relation.pivotTable).values(payload);
6938
+ const compiled = builder.compile(this.dialect);
6939
+ await this.executor.executeSql(compiled.sql, compiled.params);
6940
+ }
6941
+ async deletePivotRow(relation, rootId, targetId) {
6942
+ const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
6943
+ const targetCol = relation.pivotTable.columns[relation.pivotForeignKeyToTarget];
6944
+ if (!rootCol || !targetCol) return;
6945
+ const builder = new DeleteQueryBuilder(relation.pivotTable).where(
6946
+ and(eq(rootCol, rootId), eq(targetCol, targetId))
6947
+ );
6948
+ const compiled = builder.compile(this.dialect);
6949
+ await this.executor.executeSql(compiled.sql, compiled.params);
6950
+ }
6951
+ resolvePrimaryKeyValue(entity, table) {
6952
+ if (!entity) return null;
6953
+ const key = findPrimaryKey(table);
6954
+ const value = entity[key];
6955
+ if (value === void 0 || value === null) return null;
6956
+ return value;
6957
+ }
6958
+ };
6959
+
6542
6960
  // src/orm/query-logger.ts
6543
6961
  var createQueryLoggingExecutor = (executor, logger) => {
6544
6962
  if (!logger) {
@@ -6562,31 +6980,40 @@ var createQueryLoggingExecutor = (executor, logger) => {
6562
6980
  return wrapped;
6563
6981
  };
6564
6982
 
6565
- // src/orm/orm-context.ts
6566
- var OrmContext = class {
6567
- constructor(options) {
6568
- this.options = options;
6983
+ // src/orm/transaction-runner.ts
6984
+ var runInTransaction = async (executor, action) => {
6985
+ if (!executor.beginTransaction) {
6986
+ await action();
6987
+ return;
6988
+ }
6989
+ await executor.beginTransaction();
6990
+ try {
6991
+ await action();
6992
+ await executor.commitTransaction?.();
6993
+ } catch (error) {
6994
+ await executor.rollbackTransaction?.();
6995
+ throw error;
6996
+ }
6997
+ };
6998
+
6999
+ // src/orm/orm-session.ts
7000
+ var OrmSession = class {
7001
+ constructor(opts) {
7002
+ this.registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
7003
+ this.relationChanges.registerChange(
7004
+ buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
7005
+ );
7006
+ };
7007
+ this.orm = opts.orm;
7008
+ this.executor = createQueryLoggingExecutor(opts.executor, opts.queryLogger);
7009
+ this.interceptors = [...opts.interceptors ?? []];
6569
7010
  this.identityMap = new IdentityMap();
6570
- this.interceptors = [...options.interceptors ?? []];
6571
- this.executorWithLogging = createQueryLoggingExecutor(options.executor, options.queryLogger);
6572
- this.unitOfWork = new UnitOfWork(
6573
- options.dialect,
6574
- this.executorWithLogging,
6575
- this.identityMap,
6576
- () => this
6577
- );
6578
- this.relationChanges = new RelationChangeProcessor(
6579
- this.unitOfWork,
6580
- options.dialect,
6581
- this.executorWithLogging
6582
- );
6583
- this.domainEvents = new DomainEventBus(options.domainEventHandlers);
7011
+ this.unitOfWork = new UnitOfWork(this.orm.dialect, this.executor, this.identityMap, () => this);
7012
+ this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
7013
+ this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
6584
7014
  }
6585
7015
  get dialect() {
6586
- return this.options.dialect;
6587
- }
6588
- get executor() {
6589
- return this.executorWithLogging;
7016
+ return this.orm.dialect;
6590
7017
  }
6591
7018
  get identityBuckets() {
6592
7019
  return this.unitOfWork.identityBuckets;
@@ -6612,24 +7039,64 @@ var OrmContext = class {
6612
7039
  markRemoved(entity) {
6613
7040
  this.unitOfWork.markRemoved(entity);
6614
7041
  }
6615
- registerRelationChange(root, relationKey, rootTable, relationName, relation, change) {
6616
- const entry = {
6617
- root,
6618
- relationKey,
6619
- rootTable,
6620
- relationName,
6621
- relation,
6622
- change
6623
- };
6624
- this.relationChanges.registerChange(entry);
7042
+ getEntitiesForTable(table) {
7043
+ return this.unitOfWork.getEntitiesForTable(table);
6625
7044
  }
6626
7045
  registerInterceptor(interceptor) {
6627
7046
  this.interceptors.push(interceptor);
6628
7047
  }
6629
- registerDomainEventHandler(name, handler) {
6630
- this.domainEvents.register(name, handler);
7048
+ registerDomainEventHandler(type, handler) {
7049
+ this.domainEvents.on(type, handler);
7050
+ }
7051
+ async find(entityClass, id) {
7052
+ const table = getTableDefFromEntity(entityClass);
7053
+ if (!table) {
7054
+ throw new Error("Entity metadata has not been bootstrapped");
7055
+ }
7056
+ const primaryKey = findPrimaryKey(table);
7057
+ const column = table.columns[primaryKey];
7058
+ if (!column) {
7059
+ throw new Error("Entity table does not expose a primary key");
7060
+ }
7061
+ const columnSelections = Object.values(table.columns).reduce((acc, col2) => {
7062
+ acc[col2.name] = col2;
7063
+ return acc;
7064
+ }, {});
7065
+ const qb = selectFromEntity(entityClass).select(columnSelections).where(eq(column, id)).limit(1);
7066
+ const rows = await executeHydrated(this, qb);
7067
+ return rows[0] ?? null;
7068
+ }
7069
+ async findOne(qb) {
7070
+ const limited = qb.limit(1);
7071
+ const rows = await executeHydrated(this, limited);
7072
+ return rows[0] ?? null;
7073
+ }
7074
+ async findMany(qb) {
7075
+ return executeHydrated(this, qb);
7076
+ }
7077
+ async persist(entity) {
7078
+ if (this.unitOfWork.findTracked(entity)) {
7079
+ return;
7080
+ }
7081
+ const table = getTableDefFromEntity(entity.constructor);
7082
+ if (!table) {
7083
+ throw new Error("Entity metadata has not been bootstrapped");
7084
+ }
7085
+ const primaryKey = findPrimaryKey(table);
7086
+ const pkValue = entity[primaryKey];
7087
+ if (pkValue !== void 0 && pkValue !== null) {
7088
+ this.trackManaged(table, pkValue, entity);
7089
+ } else {
7090
+ this.trackNew(table, entity);
7091
+ }
7092
+ }
7093
+ async remove(entity) {
7094
+ this.markRemoved(entity);
7095
+ }
7096
+ async flush() {
7097
+ await this.unitOfWork.flush();
6631
7098
  }
6632
- async saveChanges() {
7099
+ async commit() {
6633
7100
  await runInTransaction(this.executor, async () => {
6634
7101
  for (const interceptor of this.interceptors) {
6635
7102
  await interceptor.beforeFlush?.(this);
@@ -6643,8 +7110,82 @@ var OrmContext = class {
6643
7110
  });
6644
7111
  await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
6645
7112
  }
6646
- getEntitiesForTable(table) {
6647
- return this.unitOfWork.getEntitiesForTable(table);
7113
+ async rollback() {
7114
+ await this.executor.rollbackTransaction?.();
7115
+ this.unitOfWork.reset();
7116
+ this.relationChanges.reset();
7117
+ }
7118
+ getExecutionContext() {
7119
+ return {
7120
+ dialect: this.orm.dialect,
7121
+ executor: this.executor,
7122
+ interceptors: this.orm.interceptors
7123
+ };
7124
+ }
7125
+ getHydrationContext() {
7126
+ return {
7127
+ identityMap: this.identityMap,
7128
+ unitOfWork: this.unitOfWork,
7129
+ domainEvents: this.domainEvents,
7130
+ relationChanges: this.relationChanges,
7131
+ entityContext: this
7132
+ };
7133
+ }
7134
+ };
7135
+ var buildRelationChangeEntry = (root, relationKey, rootTable, relationName, relation, change) => ({
7136
+ root,
7137
+ relationKey,
7138
+ rootTable,
7139
+ relationName,
7140
+ relation,
7141
+ change
7142
+ });
7143
+
7144
+ // src/orm/interceptor-pipeline.ts
7145
+ var InterceptorPipeline = class {
7146
+ constructor() {
7147
+ this.interceptors = [];
7148
+ }
7149
+ use(interceptor) {
7150
+ this.interceptors.push(interceptor);
7151
+ }
7152
+ async run(ctx, executor) {
7153
+ let i = 0;
7154
+ const dispatch = async () => {
7155
+ const interceptor = this.interceptors[i++];
7156
+ if (!interceptor) {
7157
+ return executor.executeSql(ctx.sql, ctx.params);
7158
+ }
7159
+ return interceptor(ctx, dispatch);
7160
+ };
7161
+ return dispatch();
7162
+ }
7163
+ };
7164
+
7165
+ // src/orm/orm.ts
7166
+ var Orm = class {
7167
+ constructor(opts) {
7168
+ this.dialect = opts.dialect;
7169
+ this.interceptors = opts.interceptors ?? new InterceptorPipeline();
7170
+ this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
7171
+ this.executorFactory = opts.executorFactory;
7172
+ }
7173
+ createSession(options) {
7174
+ const executor = this.executorFactory.createExecutor(options?.tx);
7175
+ return new OrmSession({ orm: this, executor });
7176
+ }
7177
+ async transaction(fn4) {
7178
+ const executor = this.executorFactory.createTransactionalExecutor();
7179
+ const session = new OrmSession({ orm: this, executor });
7180
+ try {
7181
+ const result = await fn4(session);
7182
+ await session.commit();
7183
+ return result;
7184
+ } catch (err) {
7185
+ await session.rollback();
7186
+ throw err;
7187
+ } finally {
7188
+ }
6648
7189
  }
6649
7190
  };
6650
7191
 
@@ -6759,10 +7300,12 @@ export {
6759
7300
  DefaultHasManyCollection,
6760
7301
  DefaultManyToManyCollection,
6761
7302
  DeleteQueryBuilder,
7303
+ DomainEventBus,
6762
7304
  EntityStatus,
6763
7305
  InsertQueryBuilder,
6764
7306
  MySqlDialect,
6765
- OrmContext,
7307
+ Orm,
7308
+ OrmSession,
6766
7309
  PostgresDialect,
6767
7310
  RelationKinds,
6768
7311
  SelectQueryBuilder,
@@ -6804,6 +7347,7 @@ export {
6804
7347
  createMssqlExecutor,
6805
7348
  createMysqlExecutor,
6806
7349
  createPostgresExecutor,
7350
+ createQueryLoggingExecutor,
6807
7351
  createSqliteExecutor,
6808
7352
  currentDate,
6809
7353
  currentTime,
@@ -6820,7 +7364,9 @@ export {
6820
7364
  diffSchema,
6821
7365
  endOfMonth,
6822
7366
  eq,
7367
+ esel,
6823
7368
  executeHydrated,
7369
+ executeHydratedWithContexts,
6824
7370
  exists,
6825
7371
  exp,
6826
7372
  extract,
@@ -6830,6 +7376,7 @@ export {
6830
7376
  generateCreateTableSql,
6831
7377
  generateSchemaSql,
6832
7378
  getSchemaIntrospector,
7379
+ groupConcat,
6833
7380
  gt,
6834
7381
  gte,
6835
7382
  hasMany,
@@ -6844,6 +7391,7 @@ export {
6844
7391
  isNotNull,
6845
7392
  isNull,
6846
7393
  isOperandNode,
7394
+ isValueOperandInput,
6847
7395
  isWindowFunctionNode,
6848
7396
  jsonPath,
6849
7397
  lag,
@@ -6866,6 +7414,8 @@ export {
6866
7414
  lt,
6867
7415
  lte,
6868
7416
  ltrim,
7417
+ max,
7418
+ min,
6869
7419
  mod,
6870
7420
  month,
6871
7421
  neq,
@@ -6896,6 +7446,7 @@ export {
6896
7446
  rowsToQueryResult,
6897
7447
  rpad,
6898
7448
  rtrim,
7449
+ sel,
6899
7450
  sign,
6900
7451
  sin,
6901
7452
  space,