metal-orm 1.0.14 → 1.0.16

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 (129) hide show
  1. package/README.md +69 -67
  2. package/dist/decorators/index.cjs +1983 -224
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +6 -6
  5. package/dist/decorators/index.d.ts +6 -6
  6. package/dist/decorators/index.js +1982 -224
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +5284 -3751
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +524 -169
  11. package/dist/index.d.ts +524 -169
  12. package/dist/index.js +5197 -3736
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-CCp1oz9p.d.cts → select-BKZrMRCQ.d.cts} +555 -94
  15. package/dist/{select-CCp1oz9p.d.ts → select-BKZrMRCQ.d.ts} +555 -94
  16. package/package.json +1 -1
  17. package/src/codegen/naming-strategy.ts +64 -0
  18. package/src/codegen/typescript.ts +19 -21
  19. package/src/core/ast/adapters.ts +21 -0
  20. package/src/core/ast/aggregate-functions.ts +13 -13
  21. package/src/core/ast/builders.ts +56 -43
  22. package/src/core/ast/expression-builders.ts +34 -34
  23. package/src/core/ast/expression-nodes.ts +18 -16
  24. package/src/core/ast/expression-visitor.ts +122 -69
  25. package/src/core/ast/expression.ts +6 -4
  26. package/src/core/ast/join-metadata.ts +15 -0
  27. package/src/core/ast/join-node.ts +22 -20
  28. package/src/core/ast/join.ts +5 -5
  29. package/src/core/ast/query.ts +52 -88
  30. package/src/core/ast/types.ts +20 -0
  31. package/src/core/ast/window-functions.ts +55 -55
  32. package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
  33. package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
  34. package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
  35. package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
  36. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
  37. package/src/core/ddl/introspect/catalogs/index.ts +1 -0
  38. package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
  39. package/src/core/ddl/introspect/context.ts +9 -0
  40. package/src/core/ddl/introspect/functions/postgres.ts +26 -0
  41. package/src/core/ddl/introspect/mssql.ts +149 -149
  42. package/src/core/ddl/introspect/mysql.ts +99 -99
  43. package/src/core/ddl/introspect/postgres.ts +245 -154
  44. package/src/core/ddl/introspect/registry.ts +26 -0
  45. package/src/core/ddl/introspect/run-select.ts +25 -0
  46. package/src/core/ddl/introspect/sqlite.ts +7 -7
  47. package/src/core/ddl/introspect/types.ts +23 -19
  48. package/src/core/ddl/introspect/utils.ts +1 -1
  49. package/src/core/ddl/naming-strategy.ts +10 -0
  50. package/src/core/ddl/schema-dialect.ts +41 -0
  51. package/src/core/ddl/schema-diff.ts +211 -179
  52. package/src/core/ddl/schema-generator.ts +17 -90
  53. package/src/core/ddl/schema-introspect.ts +25 -32
  54. package/src/core/ddl/schema-plan-executor.ts +17 -0
  55. package/src/core/ddl/schema-types.ts +46 -39
  56. package/src/core/ddl/sql-writing.ts +170 -0
  57. package/src/core/dialect/abstract.ts +172 -126
  58. package/src/core/dialect/base/cte-compiler.ts +33 -0
  59. package/src/core/dialect/base/function-table-formatter.ts +132 -0
  60. package/src/core/dialect/base/groupby-compiler.ts +21 -0
  61. package/src/core/dialect/base/join-compiler.ts +26 -0
  62. package/src/core/dialect/base/orderby-compiler.ts +21 -0
  63. package/src/core/dialect/base/pagination-strategy.ts +32 -0
  64. package/src/core/dialect/base/returning-strategy.ts +56 -0
  65. package/src/core/dialect/base/sql-dialect.ts +181 -204
  66. package/src/core/dialect/dialect-factory.ts +91 -0
  67. package/src/core/dialect/mssql/functions.ts +101 -0
  68. package/src/core/dialect/mssql/index.ts +128 -126
  69. package/src/core/dialect/mysql/functions.ts +101 -0
  70. package/src/core/dialect/mysql/index.ts +20 -18
  71. package/src/core/dialect/postgres/functions.ts +95 -0
  72. package/src/core/dialect/postgres/index.ts +30 -28
  73. package/src/core/dialect/sqlite/functions.ts +115 -0
  74. package/src/core/dialect/sqlite/index.ts +30 -28
  75. package/src/core/driver/database-driver.ts +11 -0
  76. package/src/core/driver/mssql-driver.ts +20 -0
  77. package/src/core/driver/mysql-driver.ts +20 -0
  78. package/src/core/driver/postgres-driver.ts +20 -0
  79. package/src/core/driver/sqlite-driver.ts +20 -0
  80. package/src/core/execution/db-executor.ts +63 -0
  81. package/src/core/execution/executors/mssql-executor.ts +39 -0
  82. package/src/core/execution/executors/mysql-executor.ts +47 -0
  83. package/src/core/execution/executors/postgres-executor.ts +32 -0
  84. package/src/core/execution/executors/sqlite-executor.ts +31 -0
  85. package/src/core/functions/datetime.ts +132 -0
  86. package/src/core/functions/numeric.ts +179 -0
  87. package/src/core/functions/standard-strategy.ts +47 -0
  88. package/src/core/functions/text.ts +147 -0
  89. package/src/core/functions/types.ts +18 -0
  90. package/src/core/hydration/types.ts +57 -0
  91. package/src/decorators/bootstrap.ts +10 -0
  92. package/src/decorators/column.ts +13 -4
  93. package/src/decorators/relations.ts +15 -0
  94. package/src/index.ts +37 -19
  95. package/src/orm/entity-context.ts +30 -0
  96. package/src/orm/entity-meta.ts +2 -2
  97. package/src/orm/entity-metadata.ts +8 -6
  98. package/src/orm/entity.ts +72 -41
  99. package/src/orm/execute.ts +42 -25
  100. package/src/orm/execution-context.ts +12 -0
  101. package/src/orm/hydration-context.ts +14 -0
  102. package/src/orm/hydration.ts +25 -17
  103. package/src/orm/identity-map.ts +4 -0
  104. package/src/orm/interceptor-pipeline.ts +29 -0
  105. package/src/orm/lazy-batch.ts +50 -6
  106. package/src/orm/orm-session.ts +234 -0
  107. package/src/orm/orm.ts +58 -0
  108. package/src/orm/query-logger.ts +1 -1
  109. package/src/orm/relation-change-processor.ts +48 -3
  110. package/src/orm/relations/belongs-to.ts +45 -44
  111. package/src/orm/relations/has-many.ts +44 -43
  112. package/src/orm/relations/has-one.ts +140 -0
  113. package/src/orm/relations/many-to-many.ts +46 -45
  114. package/src/orm/transaction-runner.ts +1 -1
  115. package/src/orm/unit-of-work.ts +66 -61
  116. package/src/query-builder/delete.ts +22 -5
  117. package/src/query-builder/hydration-manager.ts +2 -1
  118. package/src/query-builder/hydration-planner.ts +8 -7
  119. package/src/query-builder/insert.ts +22 -5
  120. package/src/query-builder/relation-conditions.ts +9 -8
  121. package/src/query-builder/relation-service.ts +3 -2
  122. package/src/query-builder/select.ts +575 -64
  123. package/src/query-builder/update.ts +22 -5
  124. package/src/schema/column.ts +246 -246
  125. package/src/schema/relation.ts +35 -1
  126. package/src/schema/table.ts +28 -28
  127. package/src/schema/types.ts +41 -31
  128. package/src/orm/db-executor.ts +0 -11
  129. package/src/orm/orm-context.ts +0 -159
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metal-orm",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -0,0 +1,64 @@
1
+ import type { TableNode, FunctionTableNode } from '../core/ast/query.js';
2
+ import type { ColumnNode } from '../core/ast/expression.js';
3
+
4
+ /**
5
+ * Strategy interface for converting database names to TypeScript identifiers
6
+ */
7
+ export interface NamingStrategy {
8
+ /**
9
+ * Converts a table name to a TypeScript symbol name
10
+ * @param table - Table node, function table node, or name
11
+ * @returns Valid TypeScript identifier
12
+ */
13
+ tableToSymbol(table: TableNode | FunctionTableNode | string): string;
14
+
15
+ /**
16
+ * Converts a column reference to a property name
17
+ * @param column - Column node
18
+ * @returns Valid TypeScript property name
19
+ */
20
+ columnToProperty(column: ColumnNode): string;
21
+ }
22
+
23
+ /**
24
+ * Default naming strategy that maintains backward compatibility
25
+ * with the original capitalize() behavior
26
+ */
27
+ export class DefaultNamingStrategy implements NamingStrategy {
28
+ /**
29
+ * Converts table names to TypeScript symbols
30
+ * @param table - Table node, function table node, or string name
31
+ * @returns Capitalized table name (handles schema-qualified names)
32
+ */
33
+ tableToSymbol(table: TableNode | FunctionTableNode | string): string {
34
+ const tableName = typeof table === 'string' ? table : table.name;
35
+
36
+ // Handle schema-qualified names (e.g., "auth.user" → "AuthUser")
37
+ if (tableName.includes('.')) {
38
+ return tableName.split('.')
39
+ .map(part => this.capitalize(part))
40
+ .join('');
41
+ }
42
+
43
+ return this.capitalize(tableName);
44
+ }
45
+
46
+ /**
47
+ * Converts column references to property names
48
+ * @param column - Column node
49
+ * @returns Column name as-is (for backward compatibility)
50
+ */
51
+ columnToProperty(column: ColumnNode): string {
52
+ return column.name;
53
+ }
54
+
55
+ /**
56
+ * Capitalizes the first letter of a string
57
+ * @param s - String to capitalize
58
+ * @returns Capitalized string
59
+ */
60
+ private capitalize(s: string): string {
61
+ if (!s) return s;
62
+ return s.charAt(0).toUpperCase() + s.slice(1);
63
+ }
64
+ }
@@ -23,13 +23,9 @@ import {
23
23
  import { SQL_OPERATOR_REGISTRY } from '../core/sql/sql-operator-config.js';
24
24
  import { SqlOperator } from '../core/sql/sql.js';
25
25
  import { isRelationAlias } from '../query-builder/relation-alias.js';
26
-
27
- /**
28
- * Capitalizes the first letter of a string
29
- * @param s - String to capitalize
30
- * @returns Capitalized string
31
- */
32
- const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
26
+ import { HydrationMetadata } from '../core/hydration/types.js';
27
+ import { getJoinRelationName } from '../core/ast/join-metadata.js';
28
+ import { NamingStrategy, DefaultNamingStrategy } from './naming-strategy.js';
33
29
 
34
30
  const assertNever = (value: never): never => {
35
31
  throw new Error(`Unhandled SQL operator: ${value}`);
@@ -39,6 +35,7 @@ const assertNever = (value: never): never => {
39
35
  * Generates TypeScript code from query AST nodes
40
36
  */
41
37
  export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVisitor<string> {
38
+ constructor(private namingStrategy: NamingStrategy = new DefaultNamingStrategy()) {}
42
39
 
43
40
  /**
44
41
  * Generates TypeScript code from a query AST
@@ -59,7 +56,7 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
59
56
  */
60
57
  private buildSelectLines(ast: SelectQueryNode): string[] {
61
58
  const lines: string[] = [];
62
- const hydration = ast.meta?.hydration;
59
+ const hydration = (ast.meta as HydrationMetadata | undefined)?.hydration;
63
60
  const hydratedRelations = new Set(hydration?.relations?.map(r => r.name) ?? []);
64
61
 
65
62
  const selections = ast.columns
@@ -75,26 +72,27 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
75
72
  lines.push(` ${sel}${index < selections.length - 1 ? ',' : ''}`);
76
73
  });
77
74
  lines.push(`})`);
78
- lines.push(`.from(${capitalize(ast.from.name)})`);
75
+ lines.push(`.from(${this.namingStrategy.tableToSymbol(ast.from)})`);
79
76
 
80
77
  if (ast.distinct && ast.distinct.length) {
81
- const cols = ast.distinct.map(c => `${capitalize(c.table)}.${c.name}`).join(', ');
78
+ const cols = ast.distinct.map(c => `${this.namingStrategy.tableToSymbol(c.table)}.${c.name}`).join(', ');
82
79
  lines.push(`.distinct(${cols})`);
83
80
  }
84
81
 
85
82
  ast.joins.forEach(join => {
86
- if (join.relationName && hydratedRelations.has(join.relationName)) {
83
+ const relationName = getJoinRelationName(join);
84
+ if (relationName && hydratedRelations.has(relationName)) {
87
85
  return;
88
86
  }
89
87
 
90
- if (join.relationName) {
88
+ if (relationName) {
91
89
  if (join.kind === 'INNER') {
92
- lines.push(`.joinRelation('${join.relationName}')`);
90
+ lines.push(`.joinRelation('${relationName}')`);
93
91
  } else {
94
- lines.push(`.joinRelation('${join.relationName}', '${join.kind}')`);
92
+ lines.push(`.joinRelation('${relationName}', '${join.kind}')`);
95
93
  }
96
94
  } else {
97
- const table = capitalize(join.table.name);
95
+ const table = this.namingStrategy.tableToSymbol(join.table);
98
96
  const cond = this.printExpression(join.condition);
99
97
  let method = 'innerJoin';
100
98
  if (join.kind === 'LEFT') method = 'leftJoin';
@@ -118,7 +116,7 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
118
116
  }
119
117
 
120
118
  if (ast.groupBy && ast.groupBy.length) {
121
- const cols = ast.groupBy.map(c => `${capitalize(c.table)}.${c.name}`).join(', ');
119
+ const cols = ast.groupBy.map(c => `${this.namingStrategy.tableToSymbol(c.table)}.${c.name}`).join(', ');
122
120
  lines.push(`.groupBy(${cols})`);
123
121
  }
124
122
 
@@ -128,7 +126,7 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
128
126
 
129
127
  if (ast.orderBy && ast.orderBy.length) {
130
128
  ast.orderBy.forEach(o => {
131
- lines.push(`.orderBy(${capitalize(o.column.table)}.${o.column.name}, '${o.direction}')`);
129
+ lines.push(`.orderBy(${this.namingStrategy.tableToSymbol(o.column.table)}.${o.column.name}, '${o.direction}')`);
132
130
  });
133
131
  }
134
132
 
@@ -289,7 +287,7 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
289
287
  * @returns TypeScript code representation
290
288
  */
291
289
  private printColumnOperand(column: ColumnNode): string {
292
- return `${capitalize(column.table)}.${column.name}`;
290
+ return `${this.namingStrategy.tableToSymbol(column.table)}.${column.name}`;
293
291
  }
294
292
 
295
293
  /**
@@ -318,7 +316,7 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
318
316
  * @returns TypeScript code representation
319
317
  */
320
318
  private printJsonPathOperand(json: JsonPathNode): string {
321
- return `jsonPath(${capitalize(json.column.table)}.${json.column.name}, '${json.path}')`;
319
+ return `jsonPath(${this.namingStrategy.tableToSymbol(json.column.table)}.${json.column.name}, '${json.path}')`;
322
320
  }
323
321
 
324
322
  /**
@@ -361,14 +359,14 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
361
359
 
362
360
  if (node.partitionBy && node.partitionBy.length > 0) {
363
361
  const partitionClause =
364
- 'PARTITION BY ' + node.partitionBy.map(col => `${capitalize(col.table)}.${col.name}`).join(', ');
362
+ 'PARTITION BY ' + node.partitionBy.map(col => `${this.namingStrategy.tableToSymbol(col.table)}.${col.name}`).join(', ');
365
363
  parts.push(partitionClause);
366
364
  }
367
365
 
368
366
  if (node.orderBy && node.orderBy.length > 0) {
369
367
  const orderClause =
370
368
  'ORDER BY ' +
371
- node.orderBy.map(o => `${capitalize(o.column.table)}.${o.column.name} ${o.direction}`).join(', ');
369
+ node.orderBy.map(o => `${this.namingStrategy.tableToSymbol(o.column.table)}.${o.column.name} ${o.direction}`).join(', ');
372
370
  parts.push(orderClause);
373
371
  }
374
372
 
@@ -0,0 +1,21 @@
1
+ import { ColumnDef } from '../../schema/column.js';
2
+ import { TableDef } from '../../schema/table.js';
3
+ import { ColumnRef, TableRef } from './types.js';
4
+
5
+ /**
6
+ * Adapts a schema ColumnDef to an AST-friendly ColumnRef.
7
+ */
8
+ export const toColumnRef = (col: ColumnRef | ColumnDef): ColumnRef => ({
9
+ name: col.name,
10
+ table: col.table,
11
+ alias: (col as ColumnRef).alias
12
+ });
13
+
14
+ /**
15
+ * Adapts a schema TableDef to an AST-friendly TableRef.
16
+ */
17
+ export const toTableRef = (table: TableRef | TableDef): TableRef => ({
18
+ name: table.name,
19
+ schema: table.schema,
20
+ alias: (table as TableRef).alias
21
+ });
@@ -1,12 +1,12 @@
1
- import { ColumnDef } from '../../schema/column.js';
2
- import { ColumnNode, FunctionNode } from './expression-nodes.js';
3
- import { columnOperand } from './expression-builders.js';
4
-
5
- const buildAggregate = (name: string) => (col: ColumnDef | ColumnNode): FunctionNode => ({
6
- type: 'Function',
7
- name,
8
- args: [columnOperand(col)]
9
- });
1
+ import { ColumnNode, FunctionNode } from './expression-nodes.js';
2
+ import { columnOperand } from './expression-builders.js';
3
+ import { ColumnRef } from './types.js';
4
+
5
+ const buildAggregate = (name: string) => (col: ColumnRef | ColumnNode): FunctionNode => ({
6
+ type: 'Function',
7
+ name,
8
+ args: [columnOperand(col)]
9
+ });
10
10
 
11
11
  /**
12
12
  * Creates a COUNT function expression
@@ -17,10 +17,10 @@ export const count = buildAggregate('COUNT');
17
17
 
18
18
  /**
19
19
  * Creates a SUM function expression
20
- * @param col - Column to sum
21
- * @returns Function node with SUM
22
- */
23
- export const sum = buildAggregate('SUM');
20
+ * @param col - Column to sum
21
+ * @returns Function node with SUM
22
+ */
23
+ export const sum = buildAggregate('SUM');
24
24
 
25
25
  /**
26
26
  * Creates an AVG function expression
@@ -1,43 +1,56 @@
1
- import { ColumnDef } from '../../schema/column.js';
2
- import { TableDef } from '../../schema/table.js';
3
- import { ColumnNode } from './expression-nodes.js';
4
- import { TableNode } from './query.js';
5
-
6
- /**
7
- * Builds or normalizes a column AST node from a column definition or existing node
8
- * @param table - Table definition providing a default table name
9
- * @param column - Column definition or existing column node
10
- */
11
- export const buildColumnNode = (table: TableDef, column: ColumnDef | ColumnNode): ColumnNode => {
12
- if ((column as ColumnNode).type === 'Column') {
13
- return column as ColumnNode;
14
- }
15
-
16
- const def = column as ColumnDef;
17
- return {
18
- type: 'Column',
19
- table: def.table || table.name,
20
- name: def.name
21
- };
22
- };
23
-
24
- /**
25
- * Builds column AST nodes for a list of column names
26
- * @param table - Table definition providing the table name
27
- * @param names - Column names
28
- */
29
- export const buildColumnNodes = (table: TableDef, names: string[]): ColumnNode[] =>
30
- names.map(name => ({
31
- type: 'Column',
32
- table: table.name,
33
- name
34
- }));
35
-
36
- /**
37
- * Builds a table AST node for the provided table definition
38
- * @param table - Table definition
39
- */
40
- export const createTableNode = (table: TableDef): TableNode => ({
41
- type: 'Table',
42
- name: table.name
43
- });
1
+ import { ColumnNode } from './expression-nodes.js';
2
+ import { TableNode, FunctionTableNode } from './query.js';
3
+ import { ColumnRef, TableRef } from './types.js';
4
+
5
+ /**
6
+ * Builds or normalizes a column AST node from a column definition or existing node
7
+ * @param table - Table definition providing a default table name
8
+ * @param column - Column definition or existing column node
9
+ */
10
+ export const buildColumnNode = (table: TableRef, column: ColumnRef | ColumnNode): ColumnNode => {
11
+ if ((column as ColumnNode).type === 'Column') {
12
+ return column as ColumnNode;
13
+ }
14
+
15
+ const def = column as ColumnRef;
16
+ return {
17
+ type: 'Column',
18
+ table: def.table || table.name,
19
+ name: def.name
20
+ };
21
+ };
22
+
23
+ /**
24
+ * Builds column AST nodes for a list of column names
25
+ * @param table - Table definition providing the table name
26
+ * @param names - Column names
27
+ */
28
+ export const buildColumnNodes = (table: TableRef, names: string[]): ColumnNode[] =>
29
+ names.map(name => ({
30
+ type: 'Column',
31
+ table: table.name,
32
+ name
33
+ }));
34
+
35
+ /**
36
+ * Builds a table AST node for the provided table definition
37
+ * @param table - Table definition
38
+ */
39
+ export const createTableNode = (table: TableRef): TableNode => ({
40
+ type: 'Table',
41
+ name: table.name
42
+ });
43
+
44
+ /**
45
+ * Creates a FunctionTable node for expressions like `function_name(args...)` used in FROM
46
+ */
47
+ export const fnTable = (name: string, args: any[] = [], alias?: string, opts?: { lateral?: boolean; withOrdinality?: boolean; columnAliases?: string[]; schema?: string }): FunctionTableNode => ({
48
+ type: 'FunctionTable',
49
+ name,
50
+ args,
51
+ alias,
52
+ lateral: opts?.lateral,
53
+ withOrdinality: opts?.withOrdinality,
54
+ columnAliases: opts?.columnAliases,
55
+ schema: opts?.schema
56
+ });
@@ -1,6 +1,6 @@
1
- import { ColumnDef } from '../../schema/column.js';
2
1
  import { SelectQueryNode } from './query.js';
3
2
  import { SqlOperator } from '../sql/sql.js';
3
+ import { ColumnRef } from './types.js';
4
4
  import {
5
5
  ColumnNode,
6
6
  FunctionNode,
@@ -37,9 +37,9 @@ export const valueToOperand = (value: unknown): OperandNode => {
37
37
  return value as OperandNode;
38
38
  };
39
39
 
40
- const toNode = (col: ColumnDef | OperandNode): OperandNode => {
40
+ const toNode = (col: ColumnRef | OperandNode): OperandNode => {
41
41
  if (isOperandNode(col)) return col as OperandNode;
42
- const def = col as ColumnDef;
42
+ const def = col as ColumnRef;
43
43
  return { type: 'Column', table: def.table || 'unknown', name: def.name };
44
44
  };
45
45
 
@@ -48,20 +48,20 @@ const toLiteralNode = (value: string | number | boolean | null): LiteralNode =>
48
48
  value
49
49
  });
50
50
 
51
- const toOperand = (val: OperandNode | ColumnDef | string | number | boolean | null): OperandNode => {
51
+ const toOperand = (val: OperandNode | ColumnRef | string | number | boolean | null): OperandNode => {
52
52
  if (val === null) return { type: 'Literal', value: null };
53
53
  if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
54
54
  return { type: 'Literal', value: val };
55
55
  }
56
- return toNode(val as OperandNode | ColumnDef);
56
+ return toNode(val as OperandNode | ColumnRef);
57
57
  };
58
58
 
59
- export const columnOperand = (col: ColumnDef | ColumnNode): ColumnNode => toNode(col) as ColumnNode;
59
+ export const columnOperand = (col: ColumnRef | ColumnNode): ColumnNode => toNode(col) as ColumnNode;
60
60
 
61
61
  const createBinaryExpression = (
62
62
  operator: SqlOperator,
63
- left: OperandNode | ColumnDef,
64
- right: OperandNode | ColumnDef | string | number | boolean | null,
63
+ left: OperandNode | ColumnRef,
64
+ right: OperandNode | ColumnRef | string | number | boolean | null,
65
65
  escape?: string
66
66
  ): BinaryExpressionNode => {
67
67
  const node: BinaryExpressionNode = {
@@ -84,15 +84,15 @@ const createBinaryExpression = (
84
84
  * @param right - Right operand
85
85
  * @returns Binary expression node with equality operator
86
86
  */
87
- export const eq = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
87
+ export const eq = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number | boolean): BinaryExpressionNode =>
88
88
  createBinaryExpression('=', left, right);
89
89
 
90
90
  /**
91
91
  * Creates a not equal expression (left != right)
92
92
  */
93
93
  export const neq = (
94
- left: OperandNode | ColumnDef,
95
- right: OperandNode | ColumnDef | string | number
94
+ left: OperandNode | ColumnRef,
95
+ right: OperandNode | ColumnRef | string | number | boolean
96
96
  ): BinaryExpressionNode => createBinaryExpression('!=', left, right);
97
97
 
98
98
  /**
@@ -101,13 +101,13 @@ export const neq = (
101
101
  * @param right - Right operand
102
102
  * @returns Binary expression node with greater-than operator
103
103
  */
104
- export const gt = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
104
+ export const gt = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
105
105
  createBinaryExpression('>', left, right);
106
106
 
107
107
  /**
108
108
  * Creates a greater than or equal expression (left >= right)
109
109
  */
110
- export const gte = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
110
+ export const gte = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
111
111
  createBinaryExpression('>=', left, right);
112
112
 
113
113
  /**
@@ -116,13 +116,13 @@ export const gte = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDe
116
116
  * @param right - Right operand
117
117
  * @returns Binary expression node with less-than operator
118
118
  */
119
- export const lt = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
119
+ export const lt = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
120
120
  createBinaryExpression('<', left, right);
121
121
 
122
122
  /**
123
123
  * Creates a less than or equal expression (left <= right)
124
124
  */
125
- export const lte = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDef | string | number): BinaryExpressionNode =>
125
+ export const lte = (left: OperandNode | ColumnRef, right: OperandNode | ColumnRef | string | number): BinaryExpressionNode =>
126
126
  createBinaryExpression('<=', left, right);
127
127
 
128
128
  /**
@@ -132,7 +132,7 @@ export const lte = (left: OperandNode | ColumnDef, right: OperandNode | ColumnDe
132
132
  * @param escape - Optional escape character
133
133
  * @returns Binary expression node with LIKE operator
134
134
  */
135
- export const like = (left: OperandNode | ColumnDef, pattern: string, escape?: string): BinaryExpressionNode =>
135
+ export const like = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
136
136
  createBinaryExpression('LIKE', left, pattern, escape);
137
137
 
138
138
  /**
@@ -142,7 +142,7 @@ export const like = (left: OperandNode | ColumnDef, pattern: string, escape?: st
142
142
  * @param escape - Optional escape character
143
143
  * @returns Binary expression node with NOT LIKE operator
144
144
  */
145
- export const notLike = (left: OperandNode | ColumnDef, pattern: string, escape?: string): BinaryExpressionNode =>
145
+ export const notLike = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
146
146
  createBinaryExpression('NOT LIKE', left, pattern, escape);
147
147
 
148
148
  /**
@@ -172,7 +172,7 @@ export const or = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
172
172
  * @param left - Operand to check for null
173
173
  * @returns Null expression node with IS NULL operator
174
174
  */
175
- export const isNull = (left: OperandNode | ColumnDef): NullExpressionNode => ({
175
+ export const isNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
176
176
  type: 'NullExpression',
177
177
  left: toNode(left),
178
178
  operator: 'IS NULL'
@@ -183,7 +183,7 @@ export const isNull = (left: OperandNode | ColumnDef): NullExpressionNode => ({
183
183
  * @param left - Operand to check for non-null
184
184
  * @returns Null expression node with IS NOT NULL operator
185
185
  */
186
- export const isNotNull = (left: OperandNode | ColumnDef): NullExpressionNode => ({
186
+ export const isNotNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
187
187
  type: 'NullExpression',
188
188
  left: toNode(left),
189
189
  operator: 'IS NOT NULL'
@@ -191,7 +191,7 @@ export const isNotNull = (left: OperandNode | ColumnDef): NullExpressionNode =>
191
191
 
192
192
  const createInExpression = (
193
193
  operator: 'IN' | 'NOT IN',
194
- left: OperandNode | ColumnDef,
194
+ left: OperandNode | ColumnRef,
195
195
  values: (string | number | LiteralNode)[]
196
196
  ): InExpressionNode => ({
197
197
  type: 'InExpression',
@@ -206,7 +206,7 @@ const createInExpression = (
206
206
  * @param values - Values to check against
207
207
  * @returns IN expression node
208
208
  */
209
- export const inList = (left: OperandNode | ColumnDef, values: (string | number | LiteralNode)[]): InExpressionNode =>
209
+ export const inList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
210
210
  createInExpression('IN', left, values);
211
211
 
212
212
  /**
@@ -215,14 +215,14 @@ export const inList = (left: OperandNode | ColumnDef, values: (string | number |
215
215
  * @param values - Values to check against
216
216
  * @returns NOT IN expression node
217
217
  */
218
- export const notInList = (left: OperandNode | ColumnDef, values: (string | number | LiteralNode)[]): InExpressionNode =>
218
+ export const notInList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
219
219
  createInExpression('NOT IN', left, values);
220
220
 
221
221
  const createBetweenExpression = (
222
222
  operator: 'BETWEEN' | 'NOT BETWEEN',
223
- left: OperandNode | ColumnDef,
224
- lower: OperandNode | ColumnDef | string | number,
225
- upper: OperandNode | ColumnDef | string | number
223
+ left: OperandNode | ColumnRef,
224
+ lower: OperandNode | ColumnRef | string | number,
225
+ upper: OperandNode | ColumnRef | string | number
226
226
  ): BetweenExpressionNode => ({
227
227
  type: 'BetweenExpression',
228
228
  left: toNode(left),
@@ -239,9 +239,9 @@ const createBetweenExpression = (
239
239
  * @returns BETWEEN expression node
240
240
  */
241
241
  export const between = (
242
- left: OperandNode | ColumnDef,
243
- lower: OperandNode | ColumnDef | string | number,
244
- upper: OperandNode | ColumnDef | string | number
242
+ left: OperandNode | ColumnRef,
243
+ lower: OperandNode | ColumnRef | string | number,
244
+ upper: OperandNode | ColumnRef | string | number
245
245
  ): BetweenExpressionNode => createBetweenExpression('BETWEEN', left, lower, upper);
246
246
 
247
247
  /**
@@ -252,9 +252,9 @@ export const between = (
252
252
  * @returns NOT BETWEEN expression node
253
253
  */
254
254
  export const notBetween = (
255
- left: OperandNode | ColumnDef,
256
- lower: OperandNode | ColumnDef | string | number,
257
- upper: OperandNode | ColumnDef | string | number
255
+ left: OperandNode | ColumnRef,
256
+ lower: OperandNode | ColumnRef | string | number,
257
+ upper: OperandNode | ColumnRef | string | number
258
258
  ): BetweenExpressionNode => createBetweenExpression('NOT BETWEEN', left, lower, upper);
259
259
 
260
260
  /**
@@ -263,7 +263,7 @@ export const notBetween = (
263
263
  * @param path - JSON path expression
264
264
  * @returns JSON path node
265
265
  */
266
- export const jsonPath = (col: ColumnDef | ColumnNode, path: string): JsonPathNode => ({
266
+ export const jsonPath = (col: ColumnRef | ColumnNode, path: string): JsonPathNode => ({
267
267
  type: 'JsonPath',
268
268
  column: columnOperand(col),
269
269
  path
@@ -276,8 +276,8 @@ export const jsonPath = (col: ColumnDef | ColumnNode, path: string): JsonPathNod
276
276
  * @returns CASE expression node
277
277
  */
278
278
  export const caseWhen = (
279
- conditions: { when: ExpressionNode; then: OperandNode | ColumnDef | string | number | boolean | null }[],
280
- elseValue?: OperandNode | ColumnDef | string | number | boolean | null
279
+ conditions: { when: ExpressionNode; then: OperandNode | ColumnRef | string | number | boolean | null }[],
280
+ elseValue?: OperandNode | ColumnRef | string | number | boolean | null
281
281
  ): CaseExpressionNode => ({
282
282
  type: 'CaseExpression',
283
283
  conditions: conditions.map(c => ({
@@ -1,6 +1,6 @@
1
- import { ColumnDef } from '../../schema/column.js';
2
- import type { SelectQueryNode, OrderByNode } from './query.js';
3
- import { SqlOperator } from '../sql/sql.js';
1
+ import type { SelectQueryNode, OrderByNode } from './query.js';
2
+ import { SqlOperator } from '../sql/sql.js';
3
+ import { ColumnRef } from './types.js';
4
4
 
5
5
  /**
6
6
  * AST node representing a literal value
@@ -27,15 +27,17 @@ export interface ColumnNode {
27
27
  /**
28
28
  * AST node representing a function call
29
29
  */
30
- export interface FunctionNode {
31
- type: 'Function';
32
- /** Function name (e.g., COUNT, SUM) */
33
- name: string;
34
- /** Function arguments */
35
- args: (ColumnNode | LiteralNode | JsonPathNode)[];
36
- /** Optional alias for the function result */
37
- alias?: string;
38
- }
30
+ export interface FunctionNode {
31
+ type: 'Function';
32
+ /** Function name (e.g., COUNT, SUM) */
33
+ name: string;
34
+ /** Optional canonical function key for dialect-aware rendering */
35
+ fn?: string;
36
+ /** Function arguments */
37
+ args: OperandNode[];
38
+ /** Optional alias for the function result */
39
+ alias?: string;
40
+ }
39
41
 
40
42
  /**
41
43
  * AST node representing a JSON path expression
@@ -118,10 +120,10 @@ export const isOperandNode = (node: any): node is OperandNode => node && operand
118
120
  export const isFunctionNode = (node: any): node is FunctionNode => node?.type === 'Function';
119
121
  export const isCaseExpressionNode = (node: any): node is CaseExpressionNode => node?.type === 'CaseExpression';
120
122
  export const isWindowFunctionNode = (node: any): node is WindowFunctionNode => node?.type === 'WindowFunction';
121
- export const isExpressionSelectionNode = (
122
- node: ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode
123
- ): node is FunctionNode | CaseExpressionNode | WindowFunctionNode =>
124
- isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
123
+ export const isExpressionSelectionNode = (
124
+ node: ColumnRef | FunctionNode | CaseExpressionNode | WindowFunctionNode
125
+ ): node is FunctionNode | CaseExpressionNode | WindowFunctionNode =>
126
+ isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
125
127
 
126
128
  /**
127
129
  * AST node representing a binary expression (e.g., column = value)