metal-orm 1.0.2 → 1.0.4

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 (37) hide show
  1. package/README.md +20 -0
  2. package/package.json +1 -1
  3. package/src/ast/expression.ts +433 -175
  4. package/src/ast/join.ts +8 -1
  5. package/src/ast/query.ts +64 -9
  6. package/src/builder/hydration-manager.ts +42 -11
  7. package/src/builder/hydration-planner.ts +80 -31
  8. package/src/builder/operations/column-selector.ts +37 -1
  9. package/src/builder/operations/cte-manager.ts +16 -0
  10. package/src/builder/operations/filter-manager.ts +32 -0
  11. package/src/builder/operations/join-manager.ts +17 -7
  12. package/src/builder/operations/pagination-manager.ts +19 -0
  13. package/src/builder/operations/relation-manager.ts +58 -3
  14. package/src/builder/query-ast-service.ts +100 -29
  15. package/src/builder/relation-conditions.ts +30 -1
  16. package/src/builder/relation-projection-helper.ts +43 -1
  17. package/src/builder/relation-service.ts +68 -13
  18. package/src/builder/relation-types.ts +6 -0
  19. package/src/builder/select-query-builder-deps.ts +64 -3
  20. package/src/builder/select-query-state.ts +72 -0
  21. package/src/builder/select.ts +166 -0
  22. package/src/codegen/typescript.ts +142 -44
  23. package/src/constants/sql-operator-config.ts +36 -0
  24. package/src/constants/sql.ts +125 -58
  25. package/src/dialect/abstract.ts +97 -22
  26. package/src/dialect/mssql/index.ts +27 -0
  27. package/src/dialect/mysql/index.ts +22 -0
  28. package/src/dialect/postgres/index.ts +22 -0
  29. package/src/dialect/sqlite/index.ts +22 -0
  30. package/src/runtime/als.ts +15 -1
  31. package/src/runtime/hydration.ts +20 -15
  32. package/src/schema/column.ts +45 -5
  33. package/src/schema/relation.ts +49 -2
  34. package/src/schema/table.ts +27 -3
  35. package/src/utils/join-node.ts +20 -0
  36. package/src/utils/raw-column-parser.ts +32 -0
  37. package/src/utils/relation-alias.ts +43 -0
package/src/ast/join.ts CHANGED
@@ -2,10 +2,17 @@ import { TableNode } from './query';
2
2
  import { ExpressionNode } from './expression';
3
3
  import { JoinKind } from '../constants/sql';
4
4
 
5
+ /**
6
+ * AST node representing a JOIN clause
7
+ */
5
8
  export interface JoinNode {
6
9
  type: 'Join';
10
+ /** Type of join (INNER, LEFT, RIGHT, etc.) */
7
11
  kind: JoinKind;
12
+ /** Table to join */
8
13
  table: TableNode;
14
+ /** Join condition expression */
9
15
  condition: ExpressionNode;
10
- relationName?: string; // Metadata for code generation to reconstruct .joinRelation()
16
+ /** Optional relation name for code generation */
17
+ relationName?: string;
11
18
  }
package/src/ast/query.ts CHANGED
@@ -1,63 +1,118 @@
1
- import { ColumnNode, FunctionNode, ExpressionNode, ScalarSubqueryNode, CaseExpressionNode, WindowFunctionNode } from './expression';
2
- import { JoinNode } from './join';
3
- import { RelationType } from '../schema/relation';
4
- import { OrderDirection } from '../constants/sql';
1
+ import { ColumnNode, FunctionNode, ExpressionNode, ScalarSubqueryNode, CaseExpressionNode, WindowFunctionNode } from './expression';
2
+ import { JoinNode } from './join';
3
+ import { RelationType } from '../schema/relation';
4
+ import { OrderDirection } from '../constants/sql';
5
5
 
6
+ /**
7
+ * AST node representing a table reference in a query
8
+ */
6
9
  export interface TableNode {
7
10
  type: 'Table';
11
+ /** Table name */
8
12
  name: string;
13
+ /** Optional schema name */
9
14
  schema?: string;
15
+ /** Optional table alias */
10
16
  alias?: string;
11
17
  }
12
18
 
13
- export interface OrderByNode {
14
- type: 'OrderBy';
15
- column: ColumnNode;
16
- direction: OrderDirection;
17
- }
19
+ /**
20
+ * AST node representing an ORDER BY clause
21
+ */
22
+ export interface OrderByNode {
23
+ type: 'OrderBy';
24
+ /** Column to order by */
25
+ column: ColumnNode;
26
+ /** Order direction (ASC or DESC) */
27
+ direction: OrderDirection;
28
+ }
18
29
 
30
+ /**
31
+ * Plan for hydrating relationship data
32
+ */
19
33
  export interface HydrationRelationPlan {
34
+ /** Name of the relationship */
20
35
  name: string;
36
+ /** Alias prefix for the relationship */
21
37
  aliasPrefix: string;
38
+ /** Type of relationship */
22
39
  type: RelationType;
40
+ /** Target table name */
23
41
  targetTable: string;
42
+ /** Target table primary key */
24
43
  targetPrimaryKey: string;
44
+ /** Foreign key column */
25
45
  foreignKey: string;
46
+ /** Local key column */
26
47
  localKey: string;
48
+ /** Columns to include */
27
49
  columns: string[];
28
50
  }
29
51
 
52
+ /**
53
+ * Complete hydration plan for a query
54
+ */
30
55
  export interface HydrationPlan {
56
+ /** Root table name */
31
57
  rootTable: string;
58
+ /** Root table primary key */
32
59
  rootPrimaryKey: string;
60
+ /** Root table columns */
33
61
  rootColumns: string[];
62
+ /** Relationship hydration plans */
34
63
  relations: HydrationRelationPlan[];
35
64
  }
36
65
 
66
+ /**
67
+ * Query metadata including hydration information
68
+ */
37
69
  export interface QueryMetadata {
70
+ /** Optional hydration plan */
38
71
  hydration?: HydrationPlan;
39
72
  }
40
73
 
74
+ /**
75
+ * AST node representing a Common Table Expression (CTE)
76
+ */
41
77
  export interface CommonTableExpressionNode {
42
78
  type: 'CommonTableExpression';
79
+ /** CTE name */
43
80
  name: string;
81
+ /** Optional column names */
44
82
  columns?: string[];
83
+ /** CTE query */
45
84
  query: SelectQueryNode;
85
+ /** Whether the CTE is recursive */
46
86
  recursive: boolean;
47
87
  }
48
88
 
89
+ /**
90
+ * AST node representing a complete SELECT query
91
+ */
49
92
  export interface SelectQueryNode {
50
93
  type: 'SelectQuery';
94
+ /** Optional CTEs (WITH clauses) */
51
95
  ctes?: CommonTableExpressionNode[];
96
+ /** FROM clause table */
52
97
  from: TableNode;
98
+ /** SELECT clause columns */
53
99
  columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[];
100
+ /** JOIN clauses */
54
101
  joins: JoinNode[];
102
+ /** Optional WHERE clause */
55
103
  where?: ExpressionNode;
104
+ /** Optional GROUP BY clause */
56
105
  groupBy?: ColumnNode[];
106
+ /** Optional HAVING clause */
57
107
  having?: ExpressionNode;
108
+ /** Optional ORDER BY clause */
58
109
  orderBy?: OrderByNode[];
110
+ /** Optional LIMIT clause */
59
111
  limit?: number;
112
+ /** Optional OFFSET clause */
60
113
  offset?: number;
114
+ /** Optional query metadata */
61
115
  meta?: QueryMetadata;
116
+ /** Optional DISTINCT clause */
62
117
  distinct?: ColumnNode[];
63
118
  }
@@ -4,26 +4,49 @@ import { SelectQueryNode, HydrationPlan } from '../ast/query';
4
4
  import { HydrationPlanner } from './hydration-planner';
5
5
  import { SelectQueryState, ProjectionNode } from './select-query-state';
6
6
 
7
+ /**
8
+ * Manages hydration planning for query results
9
+ */
7
10
  export class HydrationManager {
11
+ /**
12
+ * Creates a new HydrationManager instance
13
+ * @param table - Table definition
14
+ * @param planner - Hydration planner
15
+ */
8
16
  constructor(
9
17
  private readonly table: TableDef,
10
- private readonly planner?: HydrationPlanner
18
+ private readonly planner: HydrationPlanner
11
19
  ) {}
12
20
 
13
- private getPlanner(): HydrationPlanner {
14
- return this.planner ?? new HydrationPlanner(this.table);
15
- }
16
-
21
+ /**
22
+ * Creates a new HydrationManager with updated planner
23
+ * @param nextPlanner - Updated hydration planner
24
+ * @returns New HydrationManager instance
25
+ */
17
26
  private clone(nextPlanner: HydrationPlanner): HydrationManager {
18
27
  return new HydrationManager(this.table, nextPlanner);
19
28
  }
20
29
 
30
+ /**
31
+ * Handles column selection for hydration planning
32
+ * @param state - Current query state
33
+ * @param newColumns - Newly selected columns
34
+ * @returns Updated HydrationManager with captured columns
35
+ */
21
36
  onColumnsSelected(state: SelectQueryState, newColumns: ProjectionNode[]): HydrationManager {
22
- const planner = this.getPlanner();
23
- const updated = planner.captureRootColumns(state.ast.columns);
37
+ const updated = this.planner.captureRootColumns(newColumns);
24
38
  return this.clone(updated);
25
39
  }
26
40
 
41
+ /**
42
+ * Handles relation inclusion for hydration planning
43
+ * @param state - Current query state
44
+ * @param relation - Relation definition
45
+ * @param relationName - Name of the relation
46
+ * @param aliasPrefix - Alias prefix for the relation
47
+ * @param targetColumns - Target columns to include
48
+ * @returns Updated HydrationManager with included relation
49
+ */
27
50
  onRelationIncluded(
28
51
  state: SelectQueryState,
29
52
  relation: RelationDef,
@@ -31,14 +54,18 @@ export class HydrationManager {
31
54
  aliasPrefix: string,
32
55
  targetColumns: string[]
33
56
  ): HydrationManager {
34
- const planner = this.getPlanner();
35
- const withRoots = planner.captureRootColumns(state.ast.columns);
57
+ const withRoots = this.planner.captureRootColumns(state.ast.columns);
36
58
  const next = withRoots.includeRelation(relation, relationName, aliasPrefix, targetColumns);
37
59
  return this.clone(next);
38
60
  }
39
61
 
62
+ /**
63
+ * Applies hydration plan to the AST
64
+ * @param ast - Query AST to modify
65
+ * @returns AST with hydration metadata
66
+ */
40
67
  applyToAst(ast: SelectQueryNode): SelectQueryNode {
41
- const plan = this.planner?.getPlan();
68
+ const plan = this.planner.getPlan();
42
69
  if (!plan) return ast;
43
70
  return {
44
71
  ...ast,
@@ -49,7 +76,11 @@ export class HydrationManager {
49
76
  };
50
77
  }
51
78
 
79
+ /**
80
+ * Gets the current hydration plan
81
+ * @returns Hydration plan or undefined if none exists
82
+ */
52
83
  getPlan(): HydrationPlan | undefined {
53
- return this.planner?.getPlan();
84
+ return this.planner.getPlan();
54
85
  }
55
86
  }
@@ -1,27 +1,37 @@
1
- import { TableDef } from '../schema/table';
2
- import { RelationDef } from '../schema/relation';
3
- import { ColumnNode, FunctionNode, ScalarSubqueryNode, CaseExpressionNode, WindowFunctionNode } from '../ast/expression';
4
- import { HydrationPlan, HydrationRelationPlan } from '../ast/query';
5
-
6
- export const findPrimaryKey = (table: TableDef): string => {
7
- const pk = Object.values(table.columns).find(c => c.primary);
8
- return pk?.name || 'id';
9
- };
10
-
11
- const buildDefaultHydrationPlan = (table: TableDef): HydrationPlan => ({
12
- rootTable: table.name,
13
- rootPrimaryKey: findPrimaryKey(table),
14
- rootColumns: [],
15
- relations: []
16
- });
17
-
18
- export const isRelationAlias = (alias?: string): boolean => alias ? alias.includes('__') : false;
1
+ import { TableDef } from '../schema/table';
2
+ import { RelationDef, RelationKinds } from '../schema/relation';
3
+ import { ProjectionNode } from './select-query-state';
4
+ import { HydrationPlan, HydrationRelationPlan } from '../ast/query';
5
+ import { isRelationAlias } from '../utils/relation-alias';
6
+
7
+ /**
8
+ * Finds the primary key column name for a table
9
+ * @param table - Table definition
10
+ * @returns Name of the primary key column, defaults to 'id'
11
+ */
12
+ export const findPrimaryKey = (table: TableDef): string => {
13
+ const pk = Object.values(table.columns).find(c => c.primary);
14
+ return pk?.name || 'id';
15
+ };
19
16
 
17
+ /**
18
+ * Manages hydration planning for query results
19
+ */
20
20
  export class HydrationPlanner {
21
+ /**
22
+ * Creates a new HydrationPlanner instance
23
+ * @param table - Table definition
24
+ * @param plan - Optional existing hydration plan
25
+ */
21
26
  constructor(private readonly table: TableDef, private readonly plan?: HydrationPlan) { }
22
27
 
23
- captureRootColumns(columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[]): HydrationPlanner {
24
- const currentPlan = this.getPlanOrDefault();
28
+ /**
29
+ * Captures root table columns for hydration planning
30
+ * @param columns - Columns to capture
31
+ * @returns Updated HydrationPlanner with captured columns
32
+ */
33
+ captureRootColumns(columns: ProjectionNode[]): HydrationPlanner {
34
+ const currentPlan = this.getPlanOrDefault();
25
35
  const rootCols = new Set(currentPlan.rootColumns);
26
36
  let changed = false;
27
37
 
@@ -44,6 +54,14 @@ export class HydrationPlanner {
44
54
  });
45
55
  }
46
56
 
57
+ /**
58
+ * Includes a relation in the hydration plan
59
+ * @param rel - Relation definition
60
+ * @param relationName - Name of the relation
61
+ * @param aliasPrefix - Alias prefix for relation columns
62
+ * @param columns - Columns to include from the relation
63
+ * @returns Updated HydrationPlanner with included relation
64
+ */
47
65
  includeRelation(rel: RelationDef, relationName: string, aliasPrefix: string, columns: string[]): HydrationPlanner {
48
66
  const currentPlan = this.getPlanOrDefault();
49
67
  const relations = currentPlan.relations.filter(r => r.name !== relationName);
@@ -54,24 +72,55 @@ export class HydrationPlanner {
54
72
  });
55
73
  }
56
74
 
75
+ /**
76
+ * Gets the current hydration plan
77
+ * @returns Current hydration plan or undefined
78
+ */
57
79
  getPlan(): HydrationPlan | undefined {
58
80
  return this.plan;
59
81
  }
60
82
 
83
+ /**
84
+ * Gets the current hydration plan or creates a default one
85
+ * @returns Current hydration plan or default plan
86
+ */
61
87
  private getPlanOrDefault(): HydrationPlan {
62
88
  return this.plan ?? buildDefaultHydrationPlan(this.table);
63
89
  }
64
90
 
91
+ /**
92
+ * Builds a relation plan for hydration
93
+ * @param rel - Relation definition
94
+ * @param relationName - Name of the relation
95
+ * @param aliasPrefix - Alias prefix for relation columns
96
+ * @param columns - Columns to include from the relation
97
+ * @returns Hydration relation plan
98
+ */
65
99
  private buildRelationPlan(rel: RelationDef, relationName: string, aliasPrefix: string, columns: string[]): HydrationRelationPlan {
66
- return {
67
- name: relationName,
68
- aliasPrefix,
69
- type: rel.type,
70
- targetTable: rel.target.name,
71
- targetPrimaryKey: findPrimaryKey(rel.target),
72
- foreignKey: rel.foreignKey,
73
- localKey: rel.localKey || 'id',
74
- columns
75
- };
76
- }
100
+ const localKeyFallback =
101
+ rel.type === RelationKinds.HasMany ? findPrimaryKey(this.table) : findPrimaryKey(rel.target);
102
+
103
+ return {
104
+ name: relationName,
105
+ aliasPrefix,
106
+ type: rel.type,
107
+ targetTable: rel.target.name,
108
+ targetPrimaryKey: findPrimaryKey(rel.target),
109
+ foreignKey: rel.foreignKey,
110
+ localKey: rel.localKey || localKeyFallback,
111
+ columns
112
+ };
113
+ }
77
114
  }
115
+
116
+ /**
117
+ * Builds a default hydration plan for a table
118
+ * @param table - Table definition
119
+ * @returns Default hydration plan
120
+ */
121
+ const buildDefaultHydrationPlan = (table: TableDef): HydrationPlan => ({
122
+ rootTable: table.name,
123
+ rootPrimaryKey: findPrimaryKey(table),
124
+ rootColumns: [],
125
+ relations: []
126
+ });
@@ -1,13 +1,30 @@
1
1
  import { ColumnDef } from '../../schema/column';
2
- import { CaseExpressionNode, ColumnNode, FunctionNode, SelectQueryNode, WindowFunctionNode } from '../../ast/expression';
2
+ import { CaseExpressionNode, ColumnNode, FunctionNode, WindowFunctionNode } from '../../ast/expression';
3
+ import { SelectQueryNode } from '../../ast/query';
3
4
  import { buildColumnNode } from '../query-ast-service';
4
5
  import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
5
6
 
7
+ /**
8
+ * Type for column selection input
9
+ */
6
10
  type ColumnSelectionInput = Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>;
7
11
 
12
+ /**
13
+ * Handles column selection operations for the query builder
14
+ */
8
15
  export class ColumnSelector {
16
+ /**
17
+ * Creates a new ColumnSelector instance
18
+ * @param env - Query builder environment
19
+ */
9
20
  constructor(private readonly env: SelectQueryBuilderEnvironment) {}
10
21
 
22
+ /**
23
+ * Selects columns for the query
24
+ * @param context - Current query context
25
+ * @param columns - Columns to select
26
+ * @returns Updated query context with selected columns
27
+ */
11
28
  select(context: SelectQueryBuilderContext, columns: ColumnSelectionInput): SelectQueryBuilderContext {
12
29
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
13
30
  const { state: nextState, addedColumns } = astService.select(columns);
@@ -17,12 +34,25 @@ export class ColumnSelector {
17
34
  };
18
35
  }
19
36
 
37
+ /**
38
+ * Selects raw column expressions
39
+ * @param context - Current query context
40
+ * @param columns - Raw column expressions
41
+ * @returns Updated query context with raw column selections
42
+ */
20
43
  selectRaw(context: SelectQueryBuilderContext, columns: string[]): SelectQueryBuilderContext {
21
44
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
22
45
  const nextState = astService.selectRaw(columns).state;
23
46
  return { state: nextState, hydration: context.hydration };
24
47
  }
25
48
 
49
+ /**
50
+ * Selects a subquery as a column
51
+ * @param context - Current query context
52
+ * @param alias - Alias for the subquery
53
+ * @param query - Subquery to select
54
+ * @returns Updated query context with subquery selection
55
+ */
26
56
  selectSubquery(
27
57
  context: SelectQueryBuilderContext,
28
58
  alias: string,
@@ -33,6 +63,12 @@ export class ColumnSelector {
33
63
  return { state: nextState, hydration: context.hydration };
34
64
  }
35
65
 
66
+ /**
67
+ * Adds DISTINCT clause to the query
68
+ * @param context - Current query context
69
+ * @param columns - Columns to make distinct
70
+ * @returns Updated query context with DISTINCT clause
71
+ */
36
72
  distinct(context: SelectQueryBuilderContext, columns: (ColumnDef | ColumnNode)[]): SelectQueryBuilderContext {
37
73
  const nodes = columns.map(col => buildColumnNode(this.env.table, col));
38
74
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
@@ -1,9 +1,25 @@
1
1
  import { SelectQueryNode } from '../../ast/query';
2
2
  import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
3
3
 
4
+ /**
5
+ * Manages Common Table Expressions (CTEs) for query building
6
+ */
4
7
  export class CteManager {
8
+ /**
9
+ * Creates a new CteManager instance
10
+ * @param env - Query builder environment
11
+ */
5
12
  constructor(private readonly env: SelectQueryBuilderEnvironment) {}
6
13
 
14
+ /**
15
+ * Adds a Common Table Expression (CTE) to the query
16
+ * @param context - Current query context
17
+ * @param name - Name of the CTE
18
+ * @param query - Query for the CTE
19
+ * @param columns - Optional column names for the CTE
20
+ * @param recursive - Whether the CTE is recursive
21
+ * @returns Updated query context with CTE
22
+ */
7
23
  withCte(
8
24
  context: SelectQueryBuilderContext,
9
25
  name: string,
@@ -3,27 +3,59 @@ import { ColumnNode, ExpressionNode } from '../../ast/expression';
3
3
  import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
4
4
  import { OrderDirection } from '../../constants/sql';
5
5
 
6
+ /**
7
+ * Manages filtering, ordering, and grouping operations for queries
8
+ */
6
9
  export class FilterManager {
10
+ /**
11
+ * Creates a new FilterManager instance
12
+ * @param env - Query builder environment
13
+ */
7
14
  constructor(private readonly env: SelectQueryBuilderEnvironment) {}
8
15
 
16
+ /**
17
+ * Adds a WHERE clause to the query
18
+ * @param context - Current query context
19
+ * @param expr - Expression for the WHERE clause
20
+ * @returns Updated query context with WHERE clause
21
+ */
9
22
  where(context: SelectQueryBuilderContext, expr: ExpressionNode): SelectQueryBuilderContext {
10
23
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
11
24
  const nextState = astService.withWhere(expr);
12
25
  return { state: nextState, hydration: context.hydration };
13
26
  }
14
27
 
28
+ /**
29
+ * Adds a GROUP BY clause to the query
30
+ * @param context - Current query context
31
+ * @param column - Column to group by
32
+ * @returns Updated query context with GROUP BY clause
33
+ */
15
34
  groupBy(context: SelectQueryBuilderContext, column: ColumnDef | ColumnNode): SelectQueryBuilderContext {
16
35
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
17
36
  const nextState = astService.withGroupBy(column);
18
37
  return { state: nextState, hydration: context.hydration };
19
38
  }
20
39
 
40
+ /**
41
+ * Adds a HAVING clause to the query
42
+ * @param context - Current query context
43
+ * @param expr - Expression for the HAVING clause
44
+ * @returns Updated query context with HAVING clause
45
+ */
21
46
  having(context: SelectQueryBuilderContext, expr: ExpressionNode): SelectQueryBuilderContext {
22
47
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
23
48
  const nextState = astService.withHaving(expr);
24
49
  return { state: nextState, hydration: context.hydration };
25
50
  }
26
51
 
52
+ /**
53
+ * Adds an ORDER BY clause to the query
54
+ * @param context - Current query context
55
+ * @param column - Column to order by
56
+ * @param direction - Order direction (ASC/DESC)
57
+ * @returns Updated query context with ORDER BY clause
58
+ */
27
59
  orderBy(
28
60
  context: SelectQueryBuilderContext,
29
61
  column: ColumnDef | ColumnNode,
@@ -1,24 +1,34 @@
1
1
  import { BinaryExpressionNode } from '../../ast/expression';
2
- import { JoinNode } from '../../ast/join';
3
2
  import { TableDef } from '../../schema/table';
4
3
  import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
5
4
  import { JoinKind } from '../../constants/sql';
5
+ import { createJoinNode } from '../../utils/join-node';
6
6
 
7
+ /**
8
+ * Manages JOIN operations for query building
9
+ */
7
10
  export class JoinManager {
11
+ /**
12
+ * Creates a new JoinManager instance
13
+ * @param env - Query builder environment
14
+ */
8
15
  constructor(private readonly env: SelectQueryBuilderEnvironment) {}
9
16
 
17
+ /**
18
+ * Adds a JOIN clause to the query
19
+ * @param context - Current query context
20
+ * @param table - Table to join
21
+ * @param condition - Join condition expression
22
+ * @param kind - Type of join (INNER, LEFT, RIGHT, etc.)
23
+ * @returns Updated query context with JOIN clause
24
+ */
10
25
  join(
11
26
  context: SelectQueryBuilderContext,
12
27
  table: TableDef,
13
28
  condition: BinaryExpressionNode,
14
29
  kind: JoinKind
15
30
  ): SelectQueryBuilderContext {
16
- const joinNode: JoinNode = {
17
- type: 'Join',
18
- kind,
19
- table: { type: 'Table', name: table.name },
20
- condition
21
- };
31
+ const joinNode = createJoinNode(kind, table.name, condition);
22
32
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
23
33
  const nextState = astService.withJoin(joinNode);
24
34
  return { state: nextState, hydration: context.hydration };
@@ -1,14 +1,33 @@
1
1
  import { SelectQueryBuilderContext, SelectQueryBuilderEnvironment } from '../select-query-builder-deps';
2
2
 
3
+ /**
4
+ * Manages pagination operations (LIMIT and OFFSET) for queries
5
+ */
3
6
  export class PaginationManager {
7
+ /**
8
+ * Creates a new PaginationManager instance
9
+ * @param env - Query builder environment
10
+ */
4
11
  constructor(private readonly env: SelectQueryBuilderEnvironment) {}
5
12
 
13
+ /**
14
+ * Adds a LIMIT clause to the query
15
+ * @param context - Current query context
16
+ * @param value - Maximum number of rows to return
17
+ * @returns Updated query context with LIMIT clause
18
+ */
6
19
  limit(context: SelectQueryBuilderContext, value: number): SelectQueryBuilderContext {
7
20
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
8
21
  const nextState = astService.withLimit(value);
9
22
  return { state: nextState, hydration: context.hydration };
10
23
  }
11
24
 
25
+ /**
26
+ * Adds an OFFSET clause to the query
27
+ * @param context - Current query context
28
+ * @param value - Number of rows to skip
29
+ * @returns Updated query context with OFFSET clause
30
+ */
12
31
  offset(context: SelectQueryBuilderContext, value: number): SelectQueryBuilderContext {
13
32
  const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
14
33
  const nextState = astService.withOffset(value);