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
@@ -134,11 +134,20 @@ function Entity(options = {}) {
134
134
 
135
135
  // src/decorators/column.ts
136
136
  var normalizeColumnInput = (input) => {
137
+ const asOptions = input;
138
+ const asDefinition = input;
137
139
  const column = {
138
- type: input.type ?? input.type,
139
- args: input.args ?? input.args,
140
- notNull: input.notNull ?? input.notNull,
141
- primary: input.primary ?? input.primary
140
+ type: asOptions.type ?? asDefinition.type,
141
+ args: asOptions.args ?? asDefinition.args,
142
+ notNull: asOptions.notNull ?? asDefinition.notNull,
143
+ primary: asOptions.primary ?? asDefinition.primary,
144
+ unique: asDefinition.unique,
145
+ default: asDefinition.default,
146
+ autoIncrement: asDefinition.autoIncrement,
147
+ generated: asDefinition.generated,
148
+ check: asDefinition.check,
149
+ references: asDefinition.references,
150
+ comment: asDefinition.comment
142
151
  };
143
152
  if (!column.type) {
144
153
  throw new Error("Column decorator requires a column type");
@@ -323,6 +332,15 @@ var isWindowFunctionNode = (node) => node?.type === "WindowFunction";
323
332
  var isExpressionSelectionNode = (node) => isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
324
333
 
325
334
  // src/core/ast/expression-builders.ts
335
+ var valueToOperand = (value) => {
336
+ if (isOperandNode(value)) {
337
+ return value;
338
+ }
339
+ return {
340
+ type: "Literal",
341
+ value
342
+ };
343
+ };
326
344
  var toNode = (col) => {
327
345
  if (isOperandNode(col)) return col;
328
346
  const def = col;
@@ -332,10 +350,10 @@ var toLiteralNode = (value) => ({
332
350
  type: "Literal",
333
351
  value
334
352
  });
353
+ var isLiteralValue = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
335
354
  var toOperand = (val) => {
336
- if (val === null) return { type: "Literal", value: null };
337
- if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
338
- return { type: "Literal", value: val };
355
+ if (isLiteralValue(val)) {
356
+ return valueToOperand(val);
339
357
  }
340
358
  return toNode(val);
341
359
  };
@@ -376,6 +394,24 @@ var notExists = (subquery) => ({
376
394
  subquery
377
395
  });
378
396
 
397
+ // src/core/sql/sql.ts
398
+ var JOIN_KINDS = {
399
+ /** INNER JOIN type */
400
+ INNER: "INNER",
401
+ /** LEFT JOIN type */
402
+ LEFT: "LEFT",
403
+ /** RIGHT JOIN type */
404
+ RIGHT: "RIGHT",
405
+ /** CROSS JOIN type */
406
+ CROSS: "CROSS"
407
+ };
408
+ var ORDER_DIRECTIONS = {
409
+ /** Ascending order */
410
+ ASC: "ASC",
411
+ /** Descending order */
412
+ DESC: "DESC"
413
+ };
414
+
379
415
  // src/core/ast/aggregate-functions.ts
380
416
  var buildAggregate = (name) => (col) => ({
381
417
  type: "Function",
@@ -385,14 +421,21 @@ var buildAggregate = (name) => (col) => ({
385
421
  var count = buildAggregate("COUNT");
386
422
  var sum = buildAggregate("SUM");
387
423
  var avg = buildAggregate("AVG");
424
+ var min = buildAggregate("MIN");
425
+ var max = buildAggregate("MAX");
388
426
 
389
427
  // src/core/functions/standard-strategy.ts
390
- var StandardFunctionStrategy = class {
428
+ var StandardFunctionStrategy = class _StandardFunctionStrategy {
391
429
  constructor() {
392
430
  this.renderers = /* @__PURE__ */ new Map();
393
431
  this.registerStandard();
394
432
  }
395
433
  registerStandard() {
434
+ this.add("COUNT", ({ compiledArgs }) => `COUNT(${compiledArgs.join(", ")})`);
435
+ this.add("SUM", ({ compiledArgs }) => `SUM(${compiledArgs[0]})`);
436
+ this.add("AVG", ({ compiledArgs }) => `AVG(${compiledArgs[0]})`);
437
+ this.add("MIN", ({ compiledArgs }) => `MIN(${compiledArgs[0]})`);
438
+ this.add("MAX", ({ compiledArgs }) => `MAX(${compiledArgs[0]})`);
396
439
  this.add("ABS", ({ compiledArgs }) => `ABS(${compiledArgs[0]})`);
397
440
  this.add("UPPER", ({ compiledArgs }) => `UPPER(${compiledArgs[0]})`);
398
441
  this.add("LOWER", ({ compiledArgs }) => `LOWER(${compiledArgs[0]})`);
@@ -419,6 +462,7 @@ var StandardFunctionStrategy = class {
419
462
  this.add("DAY_OF_WEEK", ({ compiledArgs }) => `DAYOFWEEK(${compiledArgs[0]})`);
420
463
  this.add("WEEK_OF_YEAR", ({ compiledArgs }) => `WEEKOFYEAR(${compiledArgs[0]})`);
421
464
  this.add("DATE_TRUNC", ({ compiledArgs }) => `DATE_TRUNC(${compiledArgs[0]}, ${compiledArgs[1]})`);
465
+ this.add("GROUP_CONCAT", (ctx) => this.renderGroupConcat(ctx));
422
466
  }
423
467
  add(name, renderer) {
424
468
  this.renderers.set(name, renderer);
@@ -426,10 +470,40 @@ var StandardFunctionStrategy = class {
426
470
  getRenderer(name) {
427
471
  return this.renderers.get(name);
428
472
  }
473
+ renderGroupConcat(ctx) {
474
+ const arg = ctx.compiledArgs[0];
475
+ const orderClause = this.buildOrderByExpression(ctx);
476
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
477
+ const separatorClause = this.formatGroupConcatSeparator(ctx);
478
+ return `GROUP_CONCAT(${arg}${orderSegment}${separatorClause})`;
479
+ }
480
+ buildOrderByExpression(ctx) {
481
+ const orderBy = ctx.node.orderBy;
482
+ if (!orderBy || orderBy.length === 0) {
483
+ return "";
484
+ }
485
+ const parts = orderBy.map((order) => `${ctx.compileOperand(order.column)} ${order.direction}`);
486
+ return `ORDER BY ${parts.join(", ")}`;
487
+ }
488
+ formatGroupConcatSeparator(ctx) {
489
+ if (!ctx.node.separator) {
490
+ return "";
491
+ }
492
+ return ` SEPARATOR ${ctx.compileOperand(ctx.node.separator)}`;
493
+ }
494
+ getGroupConcatSeparatorOperand(ctx) {
495
+ return ctx.node.separator ?? _StandardFunctionStrategy.DEFAULT_GROUP_CONCAT_SEPARATOR;
496
+ }
497
+ static {
498
+ this.DEFAULT_GROUP_CONCAT_SEPARATOR = {
499
+ type: "Literal",
500
+ value: ","
501
+ };
502
+ }
429
503
  };
430
504
 
431
505
  // src/core/dialect/abstract.ts
432
- var Dialect = class {
506
+ var Dialect = class _Dialect {
433
507
  /**
434
508
  * Compiles a SELECT query AST to SQL
435
509
  * @param ast - Query AST to compile
@@ -602,6 +676,35 @@ var Dialect = class {
602
676
  this.registerDefaultOperandCompilers();
603
677
  this.registerDefaultExpressionCompilers();
604
678
  }
679
+ /**
680
+ * Creates a new Dialect instance (for testing purposes)
681
+ * @param functionStrategy - Optional function strategy
682
+ * @returns New Dialect instance
683
+ */
684
+ static create(functionStrategy) {
685
+ class TestDialect extends _Dialect {
686
+ constructor() {
687
+ super(...arguments);
688
+ this.dialect = "sqlite";
689
+ }
690
+ quoteIdentifier(id) {
691
+ return `"${id}"`;
692
+ }
693
+ compileSelectAst() {
694
+ throw new Error("Not implemented");
695
+ }
696
+ compileInsertAst() {
697
+ throw new Error("Not implemented");
698
+ }
699
+ compileUpdateAst() {
700
+ throw new Error("Not implemented");
701
+ }
702
+ compileDeleteAst() {
703
+ throw new Error("Not implemented");
704
+ }
705
+ }
706
+ return new TestDialect(functionStrategy);
707
+ }
605
708
  /**
606
709
  * Registers an expression compiler for a specific node type
607
710
  * @param type - Expression node type
@@ -743,7 +846,11 @@ var Dialect = class {
743
846
  const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
744
847
  const renderer = this.functionStrategy.getRenderer(fnNode.name);
745
848
  if (renderer) {
746
- return renderer({ node: fnNode, compiledArgs });
849
+ return renderer({
850
+ node: fnNode,
851
+ compiledArgs,
852
+ compileOperand: (operand) => this.compileOperand(operand, ctx)
853
+ });
747
854
  }
748
855
  return `${fnNode.name}(${compiledArgs.join(", ")})`;
749
856
  }
@@ -1166,6 +1273,14 @@ var PostgresFunctionStrategy = class extends StandardFunctionStrategy {
1166
1273
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1167
1274
  return `DATE_TRUNC('${partClean}', ${date})`;
1168
1275
  });
1276
+ this.add("GROUP_CONCAT", (ctx) => {
1277
+ const arg = ctx.compiledArgs[0];
1278
+ const orderClause = this.buildOrderByExpression(ctx);
1279
+ const orderSegment = orderClause ? ` ${orderClause}` : "";
1280
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1281
+ const separator = ctx.compileOperand(separatorOperand);
1282
+ return `STRING_AGG(${arg}, ${separator}${orderSegment})`;
1283
+ });
1169
1284
  }
1170
1285
  };
1171
1286
 
@@ -1410,6 +1525,12 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
1410
1525
  }
1411
1526
  return `date(${date}, 'start of ${partClean}')`;
1412
1527
  });
1528
+ this.add("GROUP_CONCAT", (ctx) => {
1529
+ const arg = ctx.compiledArgs[0];
1530
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1531
+ const separator = ctx.compileOperand(separatorOperand);
1532
+ return `GROUP_CONCAT(${arg}, ${separator})`;
1533
+ });
1413
1534
  }
1414
1535
  };
1415
1536
 
@@ -1526,6 +1647,14 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
1526
1647
  const partClean = String(partArg.value).replace(/['"]/g, "").toLowerCase();
1527
1648
  return `DATETRUNC(${partClean}, ${date})`;
1528
1649
  });
1650
+ this.add("GROUP_CONCAT", (ctx) => {
1651
+ const arg = ctx.compiledArgs[0];
1652
+ const separatorOperand = this.getGroupConcatSeparatorOperand(ctx);
1653
+ const separator = ctx.compileOperand(separatorOperand);
1654
+ const orderClause = this.buildOrderByExpression(ctx);
1655
+ const withinGroup = orderClause ? ` WITHIN GROUP (${orderClause})` : "";
1656
+ return `STRING_AGG(${arg}, ${separator})${withinGroup}`;
1657
+ });
1529
1658
  }
1530
1659
  };
1531
1660
 
@@ -1895,24 +2024,6 @@ var createJoinNode = (kind, tableName, condition, relationName) => ({
1895
2024
  meta: relationName ? { relationName } : void 0
1896
2025
  });
1897
2026
 
1898
- // src/core/sql/sql.ts
1899
- var JOIN_KINDS = {
1900
- /** INNER JOIN type */
1901
- INNER: "INNER",
1902
- /** LEFT JOIN type */
1903
- LEFT: "LEFT",
1904
- /** RIGHT JOIN type */
1905
- RIGHT: "RIGHT",
1906
- /** CROSS JOIN type */
1907
- CROSS: "CROSS"
1908
- };
1909
- var ORDER_DIRECTIONS = {
1910
- /** Ascending order */
1911
- ASC: "ASC",
1912
- /** Descending order */
1913
- DESC: "DESC"
1914
- };
1915
-
1916
2027
  // src/query-builder/hydration-manager.ts
1917
2028
  var HydrationManager = class _HydrationManager {
1918
2029
  /**
@@ -3937,31 +4048,43 @@ var flattenResults = (results) => {
3937
4048
  }
3938
4049
  return rows;
3939
4050
  };
3940
- async function executeHydrated(ctx, qb) {
4051
+ var executeWithEntityContext = async (entityCtx, qb) => {
3941
4052
  const ast = qb.getAST();
3942
- const compiled = ctx.dialect.compileSelect(ast);
3943
- const executed = await ctx.executor.executeSql(compiled.sql, compiled.params);
4053
+ const compiled = entityCtx.dialect.compileSelect(ast);
4054
+ const executed = await entityCtx.executor.executeSql(compiled.sql, compiled.params);
3944
4055
  const rows = flattenResults(executed);
3945
4056
  if (ast.setOps && ast.setOps.length > 0) {
3946
- return rows.map(
3947
- (row) => createEntityProxy(ctx, qb.getTable(), row, qb.getLazyRelations())
3948
- );
4057
+ return rows.map((row) => createEntityProxy(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
3949
4058
  }
3950
4059
  const hydrated = hydrateRows(rows, qb.getHydrationPlan());
3951
- return hydrated.map(
3952
- (row) => createEntityFromRow(ctx, qb.getTable(), row, qb.getLazyRelations())
3953
- );
4060
+ return hydrated.map((row) => createEntityFromRow(entityCtx, qb.getTable(), row, qb.getLazyRelations()));
4061
+ };
4062
+ async function executeHydrated(session, qb) {
4063
+ return executeWithEntityContext(session, qb);
4064
+ }
4065
+ async function executeHydratedWithContexts(_execCtx, hydCtx, qb) {
4066
+ const entityCtx = hydCtx.entityContext;
4067
+ if (!entityCtx) {
4068
+ throw new Error("Hydration context is missing an EntityContext");
4069
+ }
4070
+ return executeWithEntityContext(entityCtx, qb);
3954
4071
  }
3955
4072
 
3956
4073
  // src/query-builder/select.ts
3957
4074
  var SelectQueryBuilder = class _SelectQueryBuilder {
3958
4075
  /**
3959
- * Creates a new SelectQueryBuilder instance
3960
- * @param table - Table definition to query
3961
- * @param state - Optional initial query state
3962
- * @param hydration - Optional hydration manager
3963
- * @param dependencies - Optional query builder dependencies
3964
- */
4076
+
4077
+ * Creates a new SelectQueryBuilder instance
4078
+
4079
+ * @param table - Table definition to query
4080
+
4081
+ * @param state - Optional initial query state
4082
+
4083
+ * @param hydration - Optional hydration manager
4084
+
4085
+ * @param dependencies - Optional query builder dependencies
4086
+
4087
+ */
3965
4088
  constructor(table, state, hydration, dependencies, lazyRelations) {
3966
4089
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
3967
4090
  this.env = { table, deps };
@@ -3998,112 +4121,183 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
3998
4121
  return this.applyAst(this.context, (service) => service.withSetOperation(operator, subAst));
3999
4122
  }
4000
4123
  /**
4001
- * Selects specific columns for the query
4002
- * @param columns - Record of column definitions, function nodes, case expressions, or window functions
4003
- * @returns New query builder instance with selected columns
4004
- */
4124
+
4125
+ * Selects specific columns for the query
4126
+
4127
+ * @param columns - Record of column definitions, function nodes, case expressions, or window functions
4128
+
4129
+ * @returns New query builder instance with selected columns
4130
+
4131
+ */
4005
4132
  select(columns) {
4006
4133
  return this.clone(this.columnSelector.select(this.context, columns));
4007
4134
  }
4008
4135
  /**
4009
- * Selects raw column expressions
4010
- * @param cols - Column expressions as strings
4011
- * @returns New query builder instance with raw column selections
4136
+ * Selects columns from the root table by name (typed).
4137
+ * @param cols - Column names on the root table
4012
4138
  */
4139
+ selectColumns(...cols) {
4140
+ const selection = {};
4141
+ for (const key of cols) {
4142
+ const col = this.env.table.columns[key];
4143
+ if (!col) {
4144
+ throw new Error(`Column '${key}' not found on table '${this.env.table.name}'`);
4145
+ }
4146
+ selection[key] = col;
4147
+ }
4148
+ return this.select(selection);
4149
+ }
4150
+ /**
4151
+
4152
+ * Selects raw column expressions
4153
+
4154
+ * @param cols - Column expressions as strings
4155
+
4156
+ * @returns New query builder instance with raw column selections
4157
+
4158
+ */
4013
4159
  selectRaw(...cols) {
4014
4160
  return this.clone(this.columnSelector.selectRaw(this.context, cols));
4015
4161
  }
4016
4162
  /**
4017
- * Adds a Common Table Expression (CTE) to the query
4018
- * @param name - Name of the CTE
4019
- * @param query - Query builder or query node for the CTE
4020
- * @param columns - Optional column names for the CTE
4021
- * @returns New query builder instance with the CTE
4022
- */
4163
+
4164
+ * Adds a Common Table Expression (CTE) to the query
4165
+
4166
+ * @param name - Name of the CTE
4167
+
4168
+ * @param query - Query builder or query node for the CTE
4169
+
4170
+ * @param columns - Optional column names for the CTE
4171
+
4172
+ * @returns New query builder instance with the CTE
4173
+
4174
+ */
4023
4175
  with(name, query, columns) {
4024
4176
  const subAst = this.resolveQueryNode(query);
4025
4177
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, false));
4026
4178
  return this.clone(nextContext);
4027
4179
  }
4028
4180
  /**
4029
- * Adds a recursive Common Table Expression (CTE) to the query
4030
- * @param name - Name of the CTE
4031
- * @param query - Query builder or query node for the CTE
4032
- * @param columns - Optional column names for the CTE
4033
- * @returns New query builder instance with the recursive CTE
4034
- */
4181
+
4182
+ * Adds a recursive Common Table Expression (CTE) to the query
4183
+
4184
+ * @param name - Name of the CTE
4185
+
4186
+ * @param query - Query builder or query node for the CTE
4187
+
4188
+ * @param columns - Optional column names for the CTE
4189
+
4190
+ * @returns New query builder instance with the recursive CTE
4191
+
4192
+ */
4035
4193
  withRecursive(name, query, columns) {
4036
4194
  const subAst = this.resolveQueryNode(query);
4037
4195
  const nextContext = this.applyAst(this.context, (service) => service.withCte(name, subAst, columns, true));
4038
4196
  return this.clone(nextContext);
4039
4197
  }
4040
4198
  /**
4041
- * Selects a subquery as a column
4042
- * @param alias - Alias for the subquery column
4043
- * @param sub - Query builder or query node for the subquery
4044
- * @returns New query builder instance with the subquery selection
4045
- */
4199
+
4200
+ * Selects a subquery as a column
4201
+
4202
+ * @param alias - Alias for the subquery column
4203
+
4204
+ * @param sub - Query builder or query node for the subquery
4205
+
4206
+ * @returns New query builder instance with the subquery selection
4207
+
4208
+ */
4046
4209
  selectSubquery(alias, sub) {
4047
4210
  const query = this.resolveQueryNode(sub);
4048
4211
  return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
4049
4212
  }
4050
4213
  /**
4051
- * Adds an INNER JOIN to the query
4052
- * @param table - Table to join
4053
- * @param condition - Join condition expression
4054
- * @returns New query builder instance with the INNER JOIN
4055
- */
4214
+
4215
+ * Adds an INNER JOIN to the query
4216
+
4217
+ * @param table - Table to join
4218
+
4219
+ * @param condition - Join condition expression
4220
+
4221
+ * @returns New query builder instance with the INNER JOIN
4222
+
4223
+ */
4056
4224
  innerJoin(table, condition) {
4057
4225
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
4058
4226
  return this.clone(nextContext);
4059
4227
  }
4060
4228
  /**
4061
- * Adds a LEFT JOIN to the query
4062
- * @param table - Table to join
4063
- * @param condition - Join condition expression
4064
- * @returns New query builder instance with the LEFT JOIN
4065
- */
4229
+
4230
+ * Adds a LEFT JOIN to the query
4231
+
4232
+ * @param table - Table to join
4233
+
4234
+ * @param condition - Join condition expression
4235
+
4236
+ * @returns New query builder instance with the LEFT JOIN
4237
+
4238
+ */
4066
4239
  leftJoin(table, condition) {
4067
4240
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
4068
4241
  return this.clone(nextContext);
4069
4242
  }
4070
4243
  /**
4071
- * Adds a RIGHT JOIN to the query
4072
- * @param table - Table to join
4073
- * @param condition - Join condition expression
4074
- * @returns New query builder instance with the RIGHT JOIN
4075
- */
4244
+
4245
+ * Adds a RIGHT JOIN to the query
4246
+
4247
+ * @param table - Table to join
4248
+
4249
+ * @param condition - Join condition expression
4250
+
4251
+ * @returns New query builder instance with the RIGHT JOIN
4252
+
4253
+ */
4076
4254
  rightJoin(table, condition) {
4077
4255
  const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
4078
4256
  return this.clone(nextContext);
4079
4257
  }
4080
4258
  /**
4081
- * Matches records based on a relationship
4082
- * @param relationName - Name of the relationship to match
4083
- * @param predicate - Optional predicate expression
4084
- * @returns New query builder instance with the relationship match
4085
- */
4259
+
4260
+ * Matches records based on a relationship
4261
+
4262
+ * @param relationName - Name of the relationship to match
4263
+
4264
+ * @param predicate - Optional predicate expression
4265
+
4266
+ * @returns New query builder instance with the relationship match
4267
+
4268
+ */
4086
4269
  match(relationName, predicate) {
4087
4270
  const nextContext = this.relationManager.match(this.context, relationName, predicate);
4088
4271
  return this.clone(nextContext);
4089
4272
  }
4090
4273
  /**
4091
- * Joins a related table
4092
- * @param relationName - Name of the relationship to join
4093
- * @param joinKind - Type of join (defaults to INNER)
4094
- * @param extraCondition - Optional additional join condition
4095
- * @returns New query builder instance with the relationship join
4096
- */
4274
+
4275
+ * Joins a related table
4276
+
4277
+ * @param relationName - Name of the relationship to join
4278
+
4279
+ * @param joinKind - Type of join (defaults to INNER)
4280
+
4281
+ * @param extraCondition - Optional additional join condition
4282
+
4283
+ * @returns New query builder instance with the relationship join
4284
+
4285
+ */
4097
4286
  joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
4098
4287
  const nextContext = this.relationManager.joinRelation(this.context, relationName, joinKind, extraCondition);
4099
4288
  return this.clone(nextContext);
4100
4289
  }
4101
4290
  /**
4102
- * Includes related data in the query results
4103
- * @param relationName - Name of the relationship to include
4104
- * @param options - Optional include options
4105
- * @returns New query builder instance with the relationship inclusion
4106
- */
4291
+
4292
+ * Includes related data in the query results
4293
+
4294
+ * @param relationName - Name of the relationship to include
4295
+
4296
+ * @param options - Optional include options
4297
+
4298
+ * @returns New query builder instance with the relationship inclusion
4299
+
4300
+ */
4107
4301
  include(relationName, options) {
4108
4302
  const nextContext = this.relationManager.include(this.context, relationName, options);
4109
4303
  return this.clone(nextContext);
@@ -4113,6 +4307,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4113
4307
  nextLazy.add(relationName);
4114
4308
  return this.clone(this.context, nextLazy);
4115
4309
  }
4310
+ /**
4311
+ * Selects columns for a related table in a single hop.
4312
+ */
4313
+ selectRelationColumns(relationName, ...cols) {
4314
+ const relation = this.env.table.relations[relationName];
4315
+ if (!relation) {
4316
+ throw new Error(`Relation '${relationName}' not found on table '${this.env.table.name}'`);
4317
+ }
4318
+ const target = relation.target;
4319
+ for (const col of cols) {
4320
+ if (!target.columns[col]) {
4321
+ throw new Error(
4322
+ `Column '${col}' not found on related table '${target.name}' for relation '${relationName}'`
4323
+ );
4324
+ }
4325
+ }
4326
+ return this.include(relationName, { columns: cols });
4327
+ }
4328
+ /**
4329
+ * Convenience alias for selecting specific columns from a relation.
4330
+ */
4331
+ includePick(relationName, cols) {
4332
+ return this.selectRelationColumns(relationName, ...cols);
4333
+ }
4334
+ /**
4335
+ * Selects columns for the root table and relations from a single config object.
4336
+ */
4337
+ selectColumnsDeep(config) {
4338
+ let qb = this;
4339
+ if (config.root?.length) {
4340
+ qb = qb.selectColumns(...config.root);
4341
+ }
4342
+ for (const key of Object.keys(config)) {
4343
+ if (key === "root") continue;
4344
+ const relName = key;
4345
+ const cols = config[relName];
4346
+ if (!cols || !cols.length) continue;
4347
+ qb = qb.selectRelationColumns(relName, ...cols);
4348
+ }
4349
+ return qb;
4350
+ }
4116
4351
  getLazyRelations() {
4117
4352
  return Array.from(this.lazyRelations);
4118
4353
  }
@@ -4122,125 +4357,186 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4122
4357
  async execute(ctx) {
4123
4358
  return executeHydrated(ctx, this);
4124
4359
  }
4360
+ async executeWithContexts(execCtx, hydCtx) {
4361
+ return executeHydratedWithContexts(execCtx, hydCtx, this);
4362
+ }
4125
4363
  /**
4126
- * Adds a WHERE condition to the query
4127
- * @param expr - Expression for the WHERE clause
4128
- * @returns New query builder instance with the WHERE condition
4129
- */
4364
+
4365
+ * Adds a WHERE condition to the query
4366
+
4367
+ * @param expr - Expression for the WHERE clause
4368
+
4369
+ * @returns New query builder instance with the WHERE condition
4370
+
4371
+ */
4130
4372
  where(expr) {
4131
4373
  const nextContext = this.applyAst(this.context, (service) => service.withWhere(expr));
4132
4374
  return this.clone(nextContext);
4133
4375
  }
4134
4376
  /**
4135
- * Adds a GROUP BY clause to the query
4136
- * @param col - Column definition or column node to group by
4137
- * @returns New query builder instance with the GROUP BY clause
4138
- */
4377
+
4378
+ * Adds a GROUP BY clause to the query
4379
+
4380
+ * @param col - Column definition or column node to group by
4381
+
4382
+ * @returns New query builder instance with the GROUP BY clause
4383
+
4384
+ */
4139
4385
  groupBy(col) {
4140
4386
  const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col));
4141
4387
  return this.clone(nextContext);
4142
4388
  }
4143
4389
  /**
4144
- * Adds a HAVING condition to the query
4145
- * @param expr - Expression for the HAVING clause
4146
- * @returns New query builder instance with the HAVING condition
4147
- */
4390
+
4391
+ * Adds a HAVING condition to the query
4392
+
4393
+ * @param expr - Expression for the HAVING clause
4394
+
4395
+ * @returns New query builder instance with the HAVING condition
4396
+
4397
+ */
4148
4398
  having(expr) {
4149
4399
  const nextContext = this.applyAst(this.context, (service) => service.withHaving(expr));
4150
4400
  return this.clone(nextContext);
4151
4401
  }
4152
4402
  /**
4153
- * Adds an ORDER BY clause to the query
4154
- * @param col - Column definition or column node to order by
4155
- * @param direction - Order direction (defaults to ASC)
4156
- * @returns New query builder instance with the ORDER BY clause
4157
- */
4403
+
4404
+ * Adds an ORDER BY clause to the query
4405
+
4406
+ * @param col - Column definition or column node to order by
4407
+
4408
+ * @param direction - Order direction (defaults to ASC)
4409
+
4410
+ * @returns New query builder instance with the ORDER BY clause
4411
+
4412
+ */
4158
4413
  orderBy(col, direction = ORDER_DIRECTIONS.ASC) {
4159
4414
  const nextContext = this.applyAst(this.context, (service) => service.withOrderBy(col, direction));
4160
4415
  return this.clone(nextContext);
4161
4416
  }
4162
4417
  /**
4163
- * Adds a DISTINCT clause to the query
4164
- * @param cols - Columns to make distinct
4165
- * @returns New query builder instance with the DISTINCT clause
4166
- */
4418
+
4419
+ * Adds a DISTINCT clause to the query
4420
+
4421
+ * @param cols - Columns to make distinct
4422
+
4423
+ * @returns New query builder instance with the DISTINCT clause
4424
+
4425
+ */
4167
4426
  distinct(...cols) {
4168
4427
  return this.clone(this.columnSelector.distinct(this.context, cols));
4169
4428
  }
4170
4429
  /**
4171
- * Adds a LIMIT clause to the query
4172
- * @param n - Maximum number of rows to return
4173
- * @returns New query builder instance with the LIMIT clause
4174
- */
4430
+
4431
+ * Adds a LIMIT clause to the query
4432
+
4433
+ * @param n - Maximum number of rows to return
4434
+
4435
+ * @returns New query builder instance with the LIMIT clause
4436
+
4437
+ */
4175
4438
  limit(n) {
4176
4439
  const nextContext = this.applyAst(this.context, (service) => service.withLimit(n));
4177
4440
  return this.clone(nextContext);
4178
4441
  }
4179
4442
  /**
4180
- * Adds an OFFSET clause to the query
4181
- * @param n - Number of rows to skip
4182
- * @returns New query builder instance with the OFFSET clause
4183
- */
4443
+
4444
+ * Adds an OFFSET clause to the query
4445
+
4446
+ * @param n - Number of rows to skip
4447
+
4448
+ * @returns New query builder instance with the OFFSET clause
4449
+
4450
+ */
4184
4451
  offset(n) {
4185
4452
  const nextContext = this.applyAst(this.context, (service) => service.withOffset(n));
4186
4453
  return this.clone(nextContext);
4187
4454
  }
4188
4455
  /**
4189
- * Combines this query with another using UNION
4190
- * @param query - Query to union with
4191
- * @returns New query builder instance with the set operation
4192
- */
4456
+
4457
+ * Combines this query with another using UNION
4458
+
4459
+ * @param query - Query to union with
4460
+
4461
+ * @returns New query builder instance with the set operation
4462
+
4463
+ */
4193
4464
  union(query) {
4194
4465
  return this.clone(this.applySetOperation("UNION", query));
4195
4466
  }
4196
4467
  /**
4197
- * Combines this query with another using UNION ALL
4198
- * @param query - Query to union with
4199
- * @returns New query builder instance with the set operation
4200
- */
4468
+
4469
+ * Combines this query with another using UNION ALL
4470
+
4471
+ * @param query - Query to union with
4472
+
4473
+ * @returns New query builder instance with the set operation
4474
+
4475
+ */
4201
4476
  unionAll(query) {
4202
4477
  return this.clone(this.applySetOperation("UNION ALL", query));
4203
4478
  }
4204
4479
  /**
4205
- * Combines this query with another using INTERSECT
4206
- * @param query - Query to intersect with
4207
- * @returns New query builder instance with the set operation
4208
- */
4480
+
4481
+ * Combines this query with another using INTERSECT
4482
+
4483
+ * @param query - Query to intersect with
4484
+
4485
+ * @returns New query builder instance with the set operation
4486
+
4487
+ */
4209
4488
  intersect(query) {
4210
4489
  return this.clone(this.applySetOperation("INTERSECT", query));
4211
4490
  }
4212
4491
  /**
4213
- * Combines this query with another using EXCEPT
4214
- * @param query - Query to subtract
4215
- * @returns New query builder instance with the set operation
4216
- */
4492
+
4493
+ * Combines this query with another using EXCEPT
4494
+
4495
+ * @param query - Query to subtract
4496
+
4497
+ * @returns New query builder instance with the set operation
4498
+
4499
+ */
4217
4500
  except(query) {
4218
4501
  return this.clone(this.applySetOperation("EXCEPT", query));
4219
4502
  }
4220
4503
  /**
4221
- * Adds a WHERE EXISTS condition to the query
4222
- * @param subquery - Subquery to check for existence
4223
- * @returns New query builder instance with the WHERE EXISTS condition
4224
- */
4504
+
4505
+ * Adds a WHERE EXISTS condition to the query
4506
+
4507
+ * @param subquery - Subquery to check for existence
4508
+
4509
+ * @returns New query builder instance with the WHERE EXISTS condition
4510
+
4511
+ */
4225
4512
  whereExists(subquery) {
4226
4513
  const subAst = this.resolveQueryNode(subquery);
4227
4514
  return this.where(exists(subAst));
4228
4515
  }
4229
4516
  /**
4230
- * Adds a WHERE NOT EXISTS condition to the query
4231
- * @param subquery - Subquery to check for non-existence
4232
- * @returns New query builder instance with the WHERE NOT EXISTS condition
4233
- */
4517
+
4518
+ * Adds a WHERE NOT EXISTS condition to the query
4519
+
4520
+ * @param subquery - Subquery to check for non-existence
4521
+
4522
+ * @returns New query builder instance with the WHERE NOT EXISTS condition
4523
+
4524
+ */
4234
4525
  whereNotExists(subquery) {
4235
4526
  const subAst = this.resolveQueryNode(subquery);
4236
4527
  return this.where(notExists(subAst));
4237
4528
  }
4238
4529
  /**
4239
- * Adds a WHERE EXISTS condition based on a relationship
4240
- * @param relationName - Name of the relationship to check
4241
- * @param callback - Optional callback to modify the relationship query
4242
- * @returns New query builder instance with the relationship existence check
4243
- */
4530
+
4531
+ * Adds a WHERE EXISTS condition based on a relationship
4532
+
4533
+ * @param relationName - Name of the relationship to check
4534
+
4535
+ * @param callback - Optional callback to modify the relationship query
4536
+
4537
+ * @returns New query builder instance with the relationship existence check
4538
+
4539
+ */
4244
4540
  whereHas(relationName, callback) {
4245
4541
  const relation = this.env.table.relations[relationName];
4246
4542
  if (!relation) {
@@ -4255,11 +4551,16 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4255
4551
  return this.where(exists(finalSubAst));
4256
4552
  }
4257
4553
  /**
4258
- * Adds a WHERE NOT EXISTS condition based on a relationship
4259
- * @param relationName - Name of the relationship to check
4260
- * @param callback - Optional callback to modify the relationship query
4261
- * @returns New query builder instance with the relationship non-existence check
4262
- */
4554
+
4555
+ * Adds a WHERE NOT EXISTS condition based on a relationship
4556
+
4557
+ * @param relationName - Name of the relationship to check
4558
+
4559
+ * @param callback - Optional callback to modify the relationship query
4560
+
4561
+ * @returns New query builder instance with the relationship non-existence check
4562
+
4563
+ */
4263
4564
  whereHasNot(relationName, callback) {
4264
4565
  const relation = this.env.table.relations[relationName];
4265
4566
  if (!relation) {
@@ -4274,33 +4575,47 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
4274
4575
  return this.where(notExists(finalSubAst));
4275
4576
  }
4276
4577
  /**
4277
- * Compiles the query to SQL for a specific dialect
4278
- * @param dialect - Database dialect to compile for
4279
- * @returns Compiled query with SQL and parameters
4280
- */
4578
+
4579
+ * Compiles the query to SQL for a specific dialect
4580
+
4581
+ * @param dialect - Database dialect to compile for
4582
+
4583
+ * @returns Compiled query with SQL and parameters
4584
+
4585
+ */
4281
4586
  compile(dialect) {
4282
4587
  const resolved = resolveDialectInput(dialect);
4283
4588
  return resolved.compileSelect(this.context.state.ast);
4284
4589
  }
4285
4590
  /**
4286
- * Converts the query to SQL string for a specific dialect
4287
- * @param dialect - Database dialect to generate SQL for
4288
- * @returns SQL string representation of the query
4289
- */
4591
+
4592
+ * Converts the query to SQL string for a specific dialect
4593
+
4594
+ * @param dialect - Database dialect to generate SQL for
4595
+
4596
+ * @returns SQL string representation of the query
4597
+
4598
+ */
4290
4599
  toSql(dialect) {
4291
4600
  return this.compile(dialect).sql;
4292
4601
  }
4293
4602
  /**
4294
- * Gets the hydration plan for the query
4295
- * @returns Hydration plan or undefined if none exists
4296
- */
4603
+
4604
+ * Gets the hydration plan for the query
4605
+
4606
+ * @returns Hydration plan or undefined if none exists
4607
+
4608
+ */
4297
4609
  getHydrationPlan() {
4298
4610
  return this.context.hydration.getPlan();
4299
4611
  }
4300
4612
  /**
4301
- * Gets the Abstract Syntax Tree (AST) representation of the query
4302
- * @returns Query AST with hydration applied
4303
- */
4613
+
4614
+ * Gets the Abstract Syntax Tree (AST) representation of the query
4615
+
4616
+ * @returns Query AST with hydration applied
4617
+
4618
+ */
4304
4619
  getAST() {
4305
4620
  return this.context.hydration.applyToAst(this.context.state.ast);
4306
4621
  }