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
@@ -100,11 +100,20 @@ function Entity(options = {}) {
100
100
 
101
101
  // src/decorators/column.ts
102
102
  var normalizeColumnInput = (input) => {
103
+ const asOptions = input;
104
+ const asDefinition = input;
103
105
  const column = {
104
- type: input.type ?? input.type,
105
- args: input.args ?? input.args,
106
- notNull: input.notNull ?? input.notNull,
107
- primary: input.primary ?? input.primary
106
+ type: asOptions.type ?? asDefinition.type,
107
+ args: asOptions.args ?? asDefinition.args,
108
+ notNull: asOptions.notNull ?? asDefinition.notNull,
109
+ primary: asOptions.primary ?? asDefinition.primary,
110
+ unique: asDefinition.unique,
111
+ default: asDefinition.default,
112
+ autoIncrement: asDefinition.autoIncrement,
113
+ generated: asDefinition.generated,
114
+ check: asDefinition.check,
115
+ references: asDefinition.references,
116
+ comment: asDefinition.comment
108
117
  };
109
118
  if (!column.type) {
110
119
  throw new Error("Column decorator requires a column type");
@@ -289,6 +298,15 @@ var isWindowFunctionNode = (node) => node?.type === "WindowFunction";
289
298
  var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
290
299
 
291
300
  // src/core/ast/expression-builders.ts
301
+ var valueToOperand = (value) => {
302
+ if (isOperandNode(value)) {
303
+ return value;
304
+ }
305
+ return {
306
+ type: "Literal",
307
+ value
308
+ };
309
+ };
292
310
  var toNode = (col) => {
293
311
  if (isOperandNode(col)) return col;
294
312
  const def = col;
@@ -298,10 +316,10 @@ var toLiteralNode = (value) => ({
298
316
  type: "Literal",
299
317
  value
300
318
  });
319
+ var isLiteralValue = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
301
320
  var toOperand = (val) => {
302
- if (val === null) return { type: "Literal", value: null };
303
- if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
304
- return { type: "Literal", value: val };
321
+ if (isLiteralValue(val)) {
322
+ return valueToOperand(val);
305
323
  }
306
324
  return toNode(val);
307
325
  };
@@ -342,6 +360,24 @@ var notExists = (subquery) => ({
342
360
  subquery
343
361
  });
344
362
 
363
+ // src/core/sql/sql.ts
364
+ var JOIN_KINDS = {
365
+ /** INNER JOIN type */
366
+ INNER: "INNER",
367
+ /** LEFT JOIN type */
368
+ LEFT: "LEFT",
369
+ /** RIGHT JOIN type */
370
+ RIGHT: "RIGHT",
371
+ /** CROSS JOIN type */
372
+ CROSS: "CROSS"
373
+ };
374
+ var ORDER_DIRECTIONS = {
375
+ /** Ascending order */
376
+ ASC: "ASC",
377
+ /** Descending order */
378
+ DESC: "DESC"
379
+ };
380
+
345
381
  // src/core/ast/aggregate-functions.ts
346
382
  var buildAggregate = (name) => (col) => ({
347
383
  type: "Function",
@@ -351,14 +387,21 @@ var buildAggregate = (name) => (col) => ({
351
387
  var count = buildAggregate("COUNT");
352
388
  var sum = buildAggregate("SUM");
353
389
  var avg = buildAggregate("AVG");
390
+ var min = buildAggregate("MIN");
391
+ var max = buildAggregate("MAX");
354
392
 
355
393
  // src/core/functions/standard-strategy.ts
356
- var StandardFunctionStrategy = class {
394
+ var StandardFunctionStrategy = class _StandardFunctionStrategy {
357
395
  constructor() {
358
396
  this.renderers = /* @__PURE__ */ new Map();
359
397
  this.registerStandard();
360
398
  }
361
399
  registerStandard() {
400
+ this.add("COUNT", ({ compiledArgs }) => `COUNT(${compiledArgs.join(", ")})`);
401
+ this.add("SUM", ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
402
+ this.add("AVG", ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
403
+ this.add("MIN", ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
404
+ this.add("MAX", ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
362
405
  this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
363
406
  this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
364
407
  this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
@@ -385,6 +428,7 @@ var StandardFunctionStrategy = class {
385
428
  this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
386
429
  this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
387
430
  this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
431
+ this.add("GROUP_CONCAT", (ctx) => this.renderGroupConcat(ctx));
388
432
  }
389
433
  add(name, renderer) {
390
434
  this.renderers.set(name, renderer);
@@ -392,10 +436,40 @@ var StandardFunctionStrategy = class {
392
436
  getRenderer(name) {
393
437
  return this.renderers.get(name);
394
438
  }
439
+ renderGroupConcat(ctx) {
440
+ const arg = ctx.compiledArgs[0];
441
+ const orderClause = this.buildOrderByExpression(ctx);
442
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
443
+ const separatorClause = this.formatGroupConcatSeparator(ctx);
444
+ return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
445
+ }
446
+ buildOrderByExpression(ctx) {
447
+ const orderBy = ctx.node.orderBy;
448
+ if (!orderBy || orderBy.length === 0) {
449
+ return "";
450
+ }
451
+ const parts = orderBy.map((order) => `${ctx.compileOperand(order.column)} ${order.direction}`);
452
+ return `ORDER BY ${parts.join(", ")}`;
453
+ }
454
+ formatGroupConcatSeparator(ctx) {
455
+ if (!ctx.node.separator) {
456
+ return "";
457
+ }
458
+ return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
459
+ }
460
+ getGroupConcatSeparatorOperand(ctx) {
461
+ return ctx.node.separator ?? _StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
462
+ }
463
+ static {
464
+ this.DEFAULT_GROUP_CONCAT_SEPARATOR = {
465
+ type: "Literal",
466
+ value: ","
467
+ };
468
+ }
395
469
  };
396
470
 
397
471
  // src/core/dialect/abstract.ts
398
- var Dialect = class {
472
+ var Dialect = class _Dialect {
399
473
  /**
400
474
  * Compiles a SELECT query AST to SQL
401
475
  * @param ast - Query AST to compile
@@ -568,6 +642,35 @@ var Dialect = class {
568
642
  this.registerDefaultOperandCompilers();
569
643
  this.registerDefaultExpressionCompilers();
570
644
  }
645
+ /**
646
+ * Creates a new Dialect instance (for testing purposes)
647
+ * @param functionStrategy - Optional function strategy
648
+ * @returns New Dialect instance
649
+ */
650
+ static create(functionStrategy) {
651
+ class TestDialect extends _Dialect {
652
+ constructor() {
653
+ super(...arguments);
654
+ this.dialect = "sqlite";
655
+ }
656
+ quoteIdentifier(id) {
657
+ return `"${id}"`;
658
+ }
659
+ compileSelectAst() {
660
+ throw new Error("Not implemented");
661
+ }
662
+ compileInsertAst() {
663
+ throw new Error("Not implemented");
664
+ }
665
+ compileUpdateAst() {
666
+ throw new Error("Not implemented");
667
+ }
668
+ compileDeleteAst() {
669
+ throw new Error("Not implemented");
670
+ }
671
+ }
672
+ return new TestDialect(functionStrategy);
673
+ }
571
674
  /**
572
675
  * Registers an expression compiler for a specific node type
573
676
  * @param type - Expression node type
@@ -709,7 +812,11 @@ var Dialect = class {
709
812
  const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
710
813
  const renderer = this.functionStrategy.getRenderer(fnNode.name);
711
814
  if (renderer) {
712
- return renderer({ node: fnNode, compiledArgs });
815
+ return renderer({
816
+ node: fnNode,
817
+ compiledArgs,
818
+ compileOperand: (operand) => this.compileOperand(operand, ctx)
819
+ });
713
820
  }
714
821
  return `${fnNode.name}(${compiledArgs.join(", ")})`;
715
822
  }
@@ -1132,6 +1239,14 @@ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1132
1239
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1133
1240
  return `DATE_TRUNC('${partClean}', ${date})`;
1134
1241
  });
1242
+ this.add("GROUP_CONCAT", (ctx) => {
1243
+ const arg = ctx.compiledArgs[0];
1244
+ const orderClause = this.buildOrderByExpression(ctx);
1245
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
1246
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1247
+ const separator = ctx.compileOperand(separatorOperand);
1248
+ return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
1249
+ });
1135
1250
  }
1136
1251
  };
1137
1252
 
@@ -1376,6 +1491,12 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1376
1491
  }
1377
1492
  return `date(${date}, 'start of ${partClean}')`;
1378
1493
  });
1494
+ this.add("GROUP_CONCAT", (ctx) => {
1495
+ const arg = ctx.compiledArgs[0];
1496
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1497
+ const separator = ctx.compileOperand(separatorOperand);
1498
+ return `GROUP_CONCAT(${arg}, ${separator})`;
1499
+ });
1379
1500
  }
1380
1501
  };
1381
1502
 
@@ -1492,6 +1613,14 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1492
1613
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1493
1614
  return `DATETRUNC(${partClean}, ${date})`;
1494
1615
  });
1616
+ this.add("GROUP_CONCAT", (ctx) => {
1617
+ const arg = ctx.compiledArgs[0];
1618
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1619
+ const separator = ctx.compileOperand(separatorOperand);
1620
+ const orderClause = this.buildOrderByExpression(ctx);
1621
+ const withinGroup = orderClause ? ` WITHIN GROUP (${orderClause})` : "";
1622
+ return `STRING_AGG(${arg}, ${separator})${withinGroup}`;
1623
+ });
1495
1624
  }
1496
1625
  };
1497
1626
 
@@ -1861,24 +1990,6 @@ var createJoinNode = (kind, tableName, condition, relationName) => ({
1861
1990
  meta: relationName ? { relationName } : void 0
1862
1991
  });
1863
1992
 
1864
- // src/core/sql/sql.ts
1865
- var JOIN_KINDS = {
1866
- /** INNER JOIN type */
1867
- INNER: "INNER",
1868
- /** LEFT JOIN type */
1869
- LEFT: "LEFT",
1870
- /** RIGHT JOIN type */
1871
- RIGHT: "RIGHT",
1872
- /** CROSS JOIN type */
1873
- CROSS: "CROSS"
1874
- };
1875
- var ORDER_DIRECTIONS = {
1876
- /** Ascending order */
1877
- ASC: "ASC",
1878
- /** Descending order */
1879
- DESC: "DESC"
1880
- };
1881
-
1882
1993
  // src/query-builder/hydration-manager.ts
1883
1994
  var HydrationManager = class _HydrationManager {
1884
1995
  /**
@@ -3903,31 +4014,43 @@ var flattenResults = (results) => {
3903
4014
  }
3904
4015
  return rows;
3905
4016
  };
3906
- async function executeHydrated(ctx, qb) {
4017
+ var executeWithEntityContext = async (entityCtx, qb) => {
3907
4018
  const ast = qb.getAST();
3908
- const compiled = ctx.dialect.compileSelect(ast);
3909
- const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
4019
+ const compiled = entityCtx.dialect.compileSelect(ast);
4020
+ const executed = await entityCtx.executor.executeSql(compiled.sql, compiled.params);
3910
4021
  const rows = flattenResults(executed);
3911
4022
  if (ast.setOps && ast.setOps.length > 0) {
3912
- return rows.map(
3913
- (row) => createEntityProxy(ctx, qb.getTable(), row, qb.getLazyRelations())
3914
- );
4023
+ return rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
3915
4024
  }
3916
4025
  const hydrated = hydrateRows(rows, qb.getHydrationPlan());
3917
- return hydrated.map(
3918
- (row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
3919
- );
4026
+ return hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
4027
+ };
4028
+ async function executeHydrated(session, qb) {
4029
+ return executeWithEntityContext(session, qb);
4030
+ }
4031
+ async function executeHydratedWithContexts(_execCtx, hydCtx, qb) {
4032
+ const entityCtx = hydCtx.entityContext;
4033
+ if (!entityCtx) {
4034
+ throw new Error("Hydration context is missing an EntityContext");
4035
+ }
4036
+ return executeWithEntityContext(entityCtx, qb);
3920
4037
  }
3921
4038
 
3922
4039
  // src/query-builder/select.ts
3923
4040
  var SelectQueryBuilder = class _SelectQueryBuilder {
3924
4041
  /**
3925
- * Creates a new SelectQueryBuilder instance
3926
- * @param table - Table definition to query
3927
- * @param state - Optional initial query state
3928
- * @param hydration - Optional hydration manager
3929
- * @param dependencies - Optional query builder dependencies
3930
- */
4042
+
4043
+ * Creates a new SelectQueryBuilder instance
4044
+
4045
+ * @param table - Table definition to query
4046
+
4047
+ * @param state - Optional initial query state
4048
+
4049
+ * @param hydration - Optional hydration manager
4050
+
4051
+ * @param dependencies - Optional query builder dependencies
4052
+
4053
+ */
3931
4054
  constructor(table, state, hydration, dependencies, lazyRelations) {
3932
4055
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
3933
4056
  this.env = { table, deps };
@@ -3964,112 +4087,183 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
3964
4087
  return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
3965
4088
  }
3966
4089
  /**
3967
- * Selects specific columns for the query
3968
- * @param columns - Record of column definitions, function nodes, case expressions, or window functions
3969
- * @returns New query builder instance with selected columns
3970
- */
4090
+
4091
+ * Selects specific columns for the query
4092
+
4093
+ * @param columns - Record of column definitions, function nodes, case expressions, or window functions
4094
+
4095
+ * @returns New query builder instance with selected columns
4096
+
4097
+ */
3971
4098
  select(columns) {
3972
4099
  return this.clone(this.columnSelector.select(this.context, columns));
3973
4100
  }
3974
4101
  /**
3975
- * Selects raw column expressions
3976
- * @param cols - Column expressions as strings
3977
- * @returns New query builder instance with raw column selections
4102
+ * Selects columns from the root table by name (typed).
4103
+ * @param cols - Column names on the root table
3978
4104
  */
4105
+ selectColumns(...cols) {
4106
+ const selection = {};
4107
+ for (const key of cols) {
4108
+ const col = this.env.table.columns[key];
4109
+ if (!col) {
4110
+ throw new Error(`Column '${key}' not found on table '${this.env.table.name}'`);
4111
+ }
4112
+ selection[key] = col;
4113
+ }
4114
+ return this.select(selection);
4115
+ }
4116
+ /**
4117
+
4118
+ * Selects raw column expressions
4119
+
4120
+ * @param cols - Column expressions as strings
4121
+
4122
+ * @returns New query builder instance with raw column selections
4123
+
4124
+ */
3979
4125
  selectRaw(...cols) {
3980
4126
  return this.clone(this.columnSelector.selectRaw(this.context, cols));
3981
4127
  }
3982
4128
  /**
3983
- * Adds a Common Table Expression (CTE) to the query
3984
- * @param name - Name of the CTE
3985
- * @param query - Query builder or query node for the CTE
3986
- * @param columns - Optional column names for the CTE
3987
- * @returns New query builder instance with the CTE
3988
- */
4129
+
4130
+ * Adds a Common Table Expression (CTE) to the query
4131
+
4132
+ * @param name - Name of the CTE
4133
+
4134
+ * @param query - Query builder or query node for the CTE
4135
+
4136
+ * @param columns - Optional column names for the CTE
4137
+
4138
+ * @returns New query builder instance with the CTE
4139
+
4140
+ */
3989
4141
  with(name, query, columns) {
3990
4142
  const subAst = this.resolveQueryNode(query);
3991
4143
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, false));
3992
4144
  return this.clone(nextContext);
3993
4145
  }
3994
4146
  /**
3995
- * Adds a recursive Common Table Expression (CTE) to the query
3996
- * @param name - Name of the CTE
3997
- * @param query - Query builder or query node for the CTE
3998
- * @param columns - Optional column names for the CTE
3999
- * @returns New query builder instance with the recursive CTE
4000
- */
4147
+
4148
+ * Adds a recursive Common Table Expression (CTE) to the query
4149
+
4150
+ * @param name - Name of the CTE
4151
+
4152
+ * @param query - Query builder or query node for the CTE
4153
+
4154
+ * @param columns - Optional column names for the CTE
4155
+
4156
+ * @returns New query builder instance with the recursive CTE
4157
+
4158
+ */
4001
4159
  withRecursive(name, query, columns) {
4002
4160
  const subAst = this.resolveQueryNode(query);
4003
4161
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
4004
4162
  return this.clone(nextContext);
4005
4163
  }
4006
4164
  /**
4007
- * Selects a subquery as a column
4008
- * @param alias - Alias for the subquery column
4009
- * @param sub - Query builder or query node for the subquery
4010
- * @returns New query builder instance with the subquery selection
4011
- */
4165
+
4166
+ * Selects a subquery as a column
4167
+
4168
+ * @param alias - Alias for the subquery column
4169
+
4170
+ * @param sub - Query builder or query node for the subquery
4171
+
4172
+ * @returns New query builder instance with the subquery selection
4173
+
4174
+ */
4012
4175
  selectSubquery(alias, sub) {
4013
4176
  const query = this.resolveQueryNode(sub);
4014
4177
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
4015
4178
  }
4016
4179
  /**
4017
- * Adds an INNER JOIN to the query
4018
- * @param table - Table to join
4019
- * @param condition - Join condition expression
4020
- * @returns New query builder instance with the INNER JOIN
4021
- */
4180
+
4181
+ * Adds an INNER JOIN to the query
4182
+
4183
+ * @param table - Table to join
4184
+
4185
+ * @param condition - Join condition expression
4186
+
4187
+ * @returns New query builder instance with the INNER JOIN
4188
+
4189
+ */
4022
4190
  innerJoin(table, condition) {
4023
4191
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
4024
4192
  return this.clone(nextContext);
4025
4193
  }
4026
4194
  /**
4027
- * Adds a LEFT JOIN to the query
4028
- * @param table - Table to join
4029
- * @param condition - Join condition expression
4030
- * @returns New query builder instance with the LEFT JOIN
4031
- */
4195
+
4196
+ * Adds a LEFT JOIN to the query
4197
+
4198
+ * @param table - Table to join
4199
+
4200
+ * @param condition - Join condition expression
4201
+
4202
+ * @returns New query builder instance with the LEFT JOIN
4203
+
4204
+ */
4032
4205
  leftJoin(table, condition) {
4033
4206
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
4034
4207
  return this.clone(nextContext);
4035
4208
  }
4036
4209
  /**
4037
- * Adds a RIGHT JOIN to the query
4038
- * @param table - Table to join
4039
- * @param condition - Join condition expression
4040
- * @returns New query builder instance with the RIGHT JOIN
4041
- */
4210
+
4211
+ * Adds a RIGHT JOIN to the query
4212
+
4213
+ * @param table - Table to join
4214
+
4215
+ * @param condition - Join condition expression
4216
+
4217
+ * @returns New query builder instance with the RIGHT JOIN
4218
+
4219
+ */
4042
4220
  rightJoin(table, condition) {
4043
4221
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
4044
4222
  return this.clone(nextContext);
4045
4223
  }
4046
4224
  /**
4047
- * Matches records based on a relationship
4048
- * @param relationName - Name of the relationship to match
4049
- * @param predicate - Optional predicate expression
4050
- * @returns New query builder instance with the relationship match
4051
- */
4225
+
4226
+ * Matches records based on a relationship
4227
+
4228
+ * @param relationName - Name of the relationship to match
4229
+
4230
+ * @param predicate - Optional predicate expression
4231
+
4232
+ * @returns New query builder instance with the relationship match
4233
+
4234
+ */
4052
4235
  match(relationName, predicate) {
4053
4236
  const nextContext = this.relationManager.match(this.context, relationName, predicate);
4054
4237
  return this.clone(nextContext);
4055
4238
  }
4056
4239
  /**
4057
- * Joins a related table
4058
- * @param relationName - Name of the relationship to join
4059
- * @param joinKind - Type of join (defaults to INNER)
4060
- * @param extraCondition - Optional additional join condition
4061
- * @returns New query builder instance with the relationship join
4062
- */
4240
+
4241
+ * Joins a related table
4242
+
4243
+ * @param relationName - Name of the relationship to join
4244
+
4245
+ * @param joinKind - Type of join (defaults to INNER)
4246
+
4247
+ * @param extraCondition - Optional additional join condition
4248
+
4249
+ * @returns New query builder instance with the relationship join
4250
+
4251
+ */
4063
4252
  joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
4064
4253
  const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
4065
4254
  return this.clone(nextContext);
4066
4255
  }
4067
4256
  /**
4068
- * Includes related data in the query results
4069
- * @param relationName - Name of the relationship to include
4070
- * @param options - Optional include options
4071
- * @returns New query builder instance with the relationship inclusion
4072
- */
4257
+
4258
+ * Includes related data in the query results
4259
+
4260
+ * @param relationName - Name of the relationship to include
4261
+
4262
+ * @param options - Optional include options
4263
+
4264
+ * @returns New query builder instance with the relationship inclusion
4265
+
4266
+ */
4073
4267
  include(relationName, options) {
4074
4268
  const nextContext = this.relationManager.include(this.context, relationName, options);
4075
4269
  return this.clone(nextContext);
@@ -4079,6 +4273,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4079
4273
  nextLazy.add(relationName);
4080
4274
  return this.clone(this.context, nextLazy);
4081
4275
  }
4276
+ /**
4277
+ * Selects columns for a related table in a single hop.
4278
+ */
4279
+ selectRelationColumns(relationName, ...cols) {
4280
+ const relation = this.env.table.relations[relationName];
4281
+ if (!relation) {
4282
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4283
+ }
4284
+ const target = relation.target;
4285
+ for (const col of cols) {
4286
+ if (!target.columns[col]) {
4287
+ throw new Error(
4288
+ `Column '${col}' not found on related table '${target.name}' for relation '${relationName}'`
4289
+ );
4290
+ }
4291
+ }
4292
+ return this.include(relationName, { columns: cols });
4293
+ }
4294
+ /**
4295
+ * Convenience alias for selecting specific columns from a relation.
4296
+ */
4297
+ includePick(relationName, cols) {
4298
+ return this.selectRelationColumns(relationName, ...cols);
4299
+ }
4300
+ /**
4301
+ * Selects columns for the root table and relations from a single config object.
4302
+ */
4303
+ selectColumnsDeep(config) {
4304
+ let qb = this;
4305
+ if (config.root?.length) {
4306
+ qb = qb.selectColumns(...config.root);
4307
+ }
4308
+ for (const key of Object.keys(config)) {
4309
+ if (key === "root") continue;
4310
+ const relName = key;
4311
+ const cols = config[relName];
4312
+ if (!cols || !cols.length) continue;
4313
+ qb = qb.selectRelationColumns(relName, ...cols);
4314
+ }
4315
+ return qb;
4316
+ }
4082
4317
  getLazyRelations() {
4083
4318
  return Array.from(this.lazyRelations);
4084
4319
  }
@@ -4088,125 +4323,186 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4088
4323
  async execute(ctx) {
4089
4324
  return executeHydrated(ctx, this);
4090
4325
  }
4326
+ async executeWithContexts(execCtx, hydCtx) {
4327
+ return executeHydratedWithContexts(execCtx, hydCtx, this);
4328
+ }
4091
4329
  /**
4092
- * Adds a WHERE condition to the query
4093
- * @param expr - Expression for the WHERE clause
4094
- * @returns New query builder instance with the WHERE condition
4095
- */
4330
+
4331
+ * Adds a WHERE condition to the query
4332
+
4333
+ * @param expr - Expression for the WHERE clause
4334
+
4335
+ * @returns New query builder instance with the WHERE condition
4336
+
4337
+ */
4096
4338
  where(expr) {
4097
4339
  const nextContext = this.applyAst(this.context, (service) => service.withWhere(expr));
4098
4340
  return this.clone(nextContext);
4099
4341
  }
4100
4342
  /**
4101
- * Adds a GROUP BY clause to the query
4102
- * @param col - Column definition or column node to group by
4103
- * @returns New query builder instance with the GROUP BY clause
4104
- */
4343
+
4344
+ * Adds a GROUP BY clause to the query
4345
+
4346
+ * @param col - Column definition or column node to group by
4347
+
4348
+ * @returns New query builder instance with the GROUP BY clause
4349
+
4350
+ */
4105
4351
  groupBy(col) {
4106
4352
  const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col));
4107
4353
  return this.clone(nextContext);
4108
4354
  }
4109
4355
  /**
4110
- * Adds a HAVING condition to the query
4111
- * @param expr - Expression for the HAVING clause
4112
- * @returns New query builder instance with the HAVING condition
4113
- */
4356
+
4357
+ * Adds a HAVING condition to the query
4358
+
4359
+ * @param expr - Expression for the HAVING clause
4360
+
4361
+ * @returns New query builder instance with the HAVING condition
4362
+
4363
+ */
4114
4364
  having(expr) {
4115
4365
  const nextContext = this.applyAst(this.context, (service) => service.withHaving(expr));
4116
4366
  return this.clone(nextContext);
4117
4367
  }
4118
4368
  /**
4119
- * Adds an ORDER BY clause to the query
4120
- * @param col - Column definition or column node to order by
4121
- * @param direction - Order direction (defaults to ASC)
4122
- * @returns New query builder instance with the ORDER BY clause
4123
- */
4369
+
4370
+ * Adds an ORDER BY clause to the query
4371
+
4372
+ * @param col - Column definition or column node to order by
4373
+
4374
+ * @param direction - Order direction (defaults to ASC)
4375
+
4376
+ * @returns New query builder instance with the ORDER BY clause
4377
+
4378
+ */
4124
4379
  orderBy(col, direction = ORDER_DIRECTIONS.ASC) {
4125
4380
  const nextContext = this.applyAst(this.context, (service) => service.withOrderBy(col, direction));
4126
4381
  return this.clone(nextContext);
4127
4382
  }
4128
4383
  /**
4129
- * Adds a DISTINCT clause to the query
4130
- * @param cols - Columns to make distinct
4131
- * @returns New query builder instance with the DISTINCT clause
4132
- */
4384
+
4385
+ * Adds a DISTINCT clause to the query
4386
+
4387
+ * @param cols - Columns to make distinct
4388
+
4389
+ * @returns New query builder instance with the DISTINCT clause
4390
+
4391
+ */
4133
4392
  distinct(...cols) {
4134
4393
  return this.clone(this.columnSelector.distinct(this.context, cols));
4135
4394
  }
4136
4395
  /**
4137
- * Adds a LIMIT clause to the query
4138
- * @param n - Maximum number of rows to return
4139
- * @returns New query builder instance with the LIMIT clause
4140
- */
4396
+
4397
+ * Adds a LIMIT clause to the query
4398
+
4399
+ * @param n - Maximum number of rows to return
4400
+
4401
+ * @returns New query builder instance with the LIMIT clause
4402
+
4403
+ */
4141
4404
  limit(n) {
4142
4405
  const nextContext = this.applyAst(this.context, (service) => service.withLimit(n));
4143
4406
  return this.clone(nextContext);
4144
4407
  }
4145
4408
  /**
4146
- * Adds an OFFSET clause to the query
4147
- * @param n - Number of rows to skip
4148
- * @returns New query builder instance with the OFFSET clause
4149
- */
4409
+
4410
+ * Adds an OFFSET clause to the query
4411
+
4412
+ * @param n - Number of rows to skip
4413
+
4414
+ * @returns New query builder instance with the OFFSET clause
4415
+
4416
+ */
4150
4417
  offset(n) {
4151
4418
  const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
4152
4419
  return this.clone(nextContext);
4153
4420
  }
4154
4421
  /**
4155
- * Combines this query with another using UNION
4156
- * @param query - Query to union with
4157
- * @returns New query builder instance with the set operation
4158
- */
4422
+
4423
+ * Combines this query with another using UNION
4424
+
4425
+ * @param query - Query to union with
4426
+
4427
+ * @returns New query builder instance with the set operation
4428
+
4429
+ */
4159
4430
  union(query) {
4160
4431
  return this.clone(this.applySetOperation("UNION", query));
4161
4432
  }
4162
4433
  /**
4163
- * Combines this query with another using UNION ALL
4164
- * @param query - Query to union with
4165
- * @returns New query builder instance with the set operation
4166
- */
4434
+
4435
+ * Combines this query with another using UNION ALL
4436
+
4437
+ * @param query - Query to union with
4438
+
4439
+ * @returns New query builder instance with the set operation
4440
+
4441
+ */
4167
4442
  unionAll(query) {
4168
4443
  return this.clone(this.applySetOperation("UNION ALL", query));
4169
4444
  }
4170
4445
  /**
4171
- * Combines this query with another using INTERSECT
4172
- * @param query - Query to intersect with
4173
- * @returns New query builder instance with the set operation
4174
- */
4446
+
4447
+ * Combines this query with another using INTERSECT
4448
+
4449
+ * @param query - Query to intersect with
4450
+
4451
+ * @returns New query builder instance with the set operation
4452
+
4453
+ */
4175
4454
  intersect(query) {
4176
4455
  return this.clone(this.applySetOperation("INTERSECT", query));
4177
4456
  }
4178
4457
  /**
4179
- * Combines this query with another using EXCEPT
4180
- * @param query - Query to subtract
4181
- * @returns New query builder instance with the set operation
4182
- */
4458
+
4459
+ * Combines this query with another using EXCEPT
4460
+
4461
+ * @param query - Query to subtract
4462
+
4463
+ * @returns New query builder instance with the set operation
4464
+
4465
+ */
4183
4466
  except(query) {
4184
4467
  return this.clone(this.applySetOperation("EXCEPT", query));
4185
4468
  }
4186
4469
  /**
4187
- * Adds a WHERE EXISTS condition to the query
4188
- * @param subquery - Subquery to check for existence
4189
- * @returns New query builder instance with the WHERE EXISTS condition
4190
- */
4470
+
4471
+ * Adds a WHERE EXISTS condition to the query
4472
+
4473
+ * @param subquery - Subquery to check for existence
4474
+
4475
+ * @returns New query builder instance with the WHERE EXISTS condition
4476
+
4477
+ */
4191
4478
  whereExists(subquery) {
4192
4479
  const subAst = this.resolveQueryNode(subquery);
4193
4480
  return this.where(exists(subAst));
4194
4481
  }
4195
4482
  /**
4196
- * Adds a WHERE NOT EXISTS condition to the query
4197
- * @param subquery - Subquery to check for non-existence
4198
- * @returns New query builder instance with the WHERE NOT EXISTS condition
4199
- */
4483
+
4484
+ * Adds a WHERE NOT EXISTS condition to the query
4485
+
4486
+ * @param subquery - Subquery to check for non-existence
4487
+
4488
+ * @returns New query builder instance with the WHERE NOT EXISTS condition
4489
+
4490
+ */
4200
4491
  whereNotExists(subquery) {
4201
4492
  const subAst = this.resolveQueryNode(subquery);
4202
4493
  return this.where(notExists(subAst));
4203
4494
  }
4204
4495
  /**
4205
- * Adds a WHERE EXISTS condition based on a relationship
4206
- * @param relationName - Name of the relationship to check
4207
- * @param callback - Optional callback to modify the relationship query
4208
- * @returns New query builder instance with the relationship existence check
4209
- */
4496
+
4497
+ * Adds a WHERE EXISTS condition based on a relationship
4498
+
4499
+ * @param relationName - Name of the relationship to check
4500
+
4501
+ * @param callback - Optional callback to modify the relationship query
4502
+
4503
+ * @returns New query builder instance with the relationship existence check
4504
+
4505
+ */
4210
4506
  whereHas(relationName, callback) {
4211
4507
  const relation = this.env.table.relations[relationName];
4212
4508
  if (!relation) {
@@ -4221,11 +4517,16 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4221
4517
  return this.where(exists(finalSubAst));
4222
4518
  }
4223
4519
  /**
4224
- * Adds a WHERE NOT EXISTS condition based on a relationship
4225
- * @param relationName - Name of the relationship to check
4226
- * @param callback - Optional callback to modify the relationship query
4227
- * @returns New query builder instance with the relationship non-existence check
4228
- */
4520
+
4521
+ * Adds a WHERE NOT EXISTS condition based on a relationship
4522
+
4523
+ * @param relationName - Name of the relationship to check
4524
+
4525
+ * @param callback - Optional callback to modify the relationship query
4526
+
4527
+ * @returns New query builder instance with the relationship non-existence check
4528
+
4529
+ */
4229
4530
  whereHasNot(relationName, callback) {
4230
4531
  const relation = this.env.table.relations[relationName];
4231
4532
  if (!relation) {
@@ -4240,33 +4541,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4240
4541
  return this.where(notExists(finalSubAst));
4241
4542
  }
4242
4543
  /**
4243
- * Compiles the query to SQL for a specific dialect
4244
- * @param dialect - Database dialect to compile for
4245
- * @returns Compiled query with SQL and parameters
4246
- */
4544
+
4545
+ * Compiles the query to SQL for a specific dialect
4546
+
4547
+ * @param dialect - Database dialect to compile for
4548
+
4549
+ * @returns Compiled query with SQL and parameters
4550
+
4551
+ */
4247
4552
  compile(dialect) {
4248
4553
  const resolved = resolveDialectInput(dialect);
4249
4554
  return resolved.compileSelect(this.context.state.ast);
4250
4555
  }
4251
4556
  /**
4252
- * Converts the query to SQL string for a specific dialect
4253
- * @param dialect - Database dialect to generate SQL for
4254
- * @returns SQL string representation of the query
4255
- */
4557
+
4558
+ * Converts the query to SQL string for a specific dialect
4559
+
4560
+ * @param dialect - Database dialect to generate SQL for
4561
+
4562
+ * @returns SQL string representation of the query
4563
+
4564
+ */
4256
4565
  toSql(dialect) {
4257
4566
  return this.compile(dialect).sql;
4258
4567
  }
4259
4568
  /**
4260
- * Gets the hydration plan for the query
4261
- * @returns Hydration plan or undefined if none exists
4262
- */
4569
+
4570
+ * Gets the hydration plan for the query
4571
+
4572
+ * @returns Hydration plan or undefined if none exists
4573
+
4574
+ */
4263
4575
  getHydrationPlan() {
4264
4576
  return this.context.hydration.getPlan();
4265
4577
  }
4266
4578
  /**
4267
- * Gets the Abstract Syntax Tree (AST) representation of the query
4268
- * @returns Query AST with hydration applied
4269
- */
4579
+
4580
+ * Gets the Abstract Syntax Tree (AST) representation of the query
4581
+
4582
+ * @returns Query AST with hydration applied
4583
+
4584
+ */
4270
4585
  getAST() {
4271
4586
  return this.context.hydration.applyToAst(this.context.state.ast);
4272
4587
  }