metal-orm 1.1.11 → 1.1.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 (182) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/package.json +100 -96
  5. package/scripts/generate-entities/schema.mjs +122 -122
  6. package/scripts/generate-entities/tree-detection.mjs +92 -92
  7. package/scripts/inflection/pt-br.mjs +468 -468
  8. package/scripts/run-eslint.mjs +33 -34
  9. package/scripts/show-sql-seed.sql +102 -0
  10. package/scripts/show-sql.mjs +321 -325
  11. package/src/codegen/naming-strategy.ts +54 -54
  12. package/src/codegen/typescript.ts +482 -482
  13. package/src/core/ast/adapters.ts +1 -1
  14. package/src/core/ast/builders.ts +123 -123
  15. package/src/core/ast/expression-builders.ts +745 -745
  16. package/src/core/ast/expression-nodes.ts +304 -304
  17. package/src/core/ast/expression-visitor.ts +212 -212
  18. package/src/core/ast/expression.ts +20 -20
  19. package/src/core/ast/helpers.ts +14 -14
  20. package/src/core/ast/join.ts +18 -18
  21. package/src/core/ast/query.ts +200 -200
  22. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  23. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  24. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  25. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  26. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  27. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  28. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  29. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  30. package/src/core/ddl/introspect/context.ts +15 -15
  31. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  32. package/src/core/ddl/introspect/registry.ts +40 -40
  33. package/src/core/ddl/introspect/run-select.ts +35 -35
  34. package/src/core/ddl/introspect/utils.ts +56 -56
  35. package/src/core/ddl/naming-strategy.ts +16 -16
  36. package/src/core/ddl/schema-dialect.ts +56 -56
  37. package/src/core/ddl/schema-diff.ts +234 -234
  38. package/src/core/ddl/schema-generator.ts +1 -1
  39. package/src/core/ddl/schema-introspect.ts +25 -25
  40. package/src/core/ddl/sql-writing.ts +171 -171
  41. package/src/core/dialect/abstract.ts +541 -541
  42. package/src/core/dialect/base/cte-compiler.ts +33 -33
  43. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  44. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  45. package/src/core/dialect/base/join-compiler.ts +25 -25
  46. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  47. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  48. package/src/core/dialect/base/returning-strategy.ts +57 -57
  49. package/src/core/dialect/base/sql-dialect.ts +294 -294
  50. package/src/core/dialect/dialect-factory.ts +91 -91
  51. package/src/core/dialect/mssql/functions.ts +120 -120
  52. package/src/core/dialect/mssql/index.ts +317 -317
  53. package/src/core/dialect/mysql/functions.ts +103 -103
  54. package/src/core/dialect/mysql/index.ts +105 -105
  55. package/src/core/dialect/postgres/functions.ts +108 -108
  56. package/src/core/dialect/postgres/index.ts +91 -91
  57. package/src/core/dialect/sqlite/functions.ts +129 -129
  58. package/src/core/dialect/sqlite/index.ts +67 -67
  59. package/src/core/driver/database-driver.ts +19 -19
  60. package/src/core/driver/mssql-driver.ts +23 -23
  61. package/src/core/driver/mysql-driver.ts +23 -23
  62. package/src/core/driver/postgres-driver.ts +23 -23
  63. package/src/core/driver/sqlite-driver.ts +23 -23
  64. package/src/core/execution/db-executor.ts +92 -92
  65. package/src/core/execution/executors/better-sqlite3-executor.ts +90 -90
  66. package/src/core/execution/executors/mssql-executor.ts +184 -184
  67. package/src/core/execution/executors/mysql-executor.ts +88 -88
  68. package/src/core/execution/executors/postgres-executor.ts +26 -26
  69. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  70. package/src/core/execution/pooling/pool-types.ts +30 -30
  71. package/src/core/execution/pooling/pool.ts +275 -275
  72. package/src/core/functions/array.ts +35 -35
  73. package/src/core/functions/control-flow.ts +79 -79
  74. package/src/core/functions/datetime.ts +237 -237
  75. package/src/core/functions/json.ts +70 -70
  76. package/src/core/functions/numeric.ts +297 -297
  77. package/src/core/functions/text.ts +334 -334
  78. package/src/core/functions/types.ts +33 -33
  79. package/src/core/hydration/types.ts +51 -51
  80. package/src/core/sql/sql-operator-config.ts +39 -39
  81. package/src/core/sql/sql.ts +152 -152
  82. package/src/decorators/column-decorator.ts +87 -87
  83. package/src/decorators/decorator-metadata.ts +143 -143
  84. package/src/decorators/entity.ts +93 -93
  85. package/src/decorators/index.ts +39 -39
  86. package/src/decorators/relations.ts +231 -231
  87. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  88. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  89. package/src/decorators/transformers/transformer-executor.ts +171 -171
  90. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  91. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  92. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  93. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  94. package/src/decorators/validators/country-validator-registry.ts +64 -64
  95. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  96. package/src/decorators/validators/country-validators.ts +105 -105
  97. package/src/decorators/validators/validator-metadata.ts +59 -59
  98. package/src/dto/dto-types.ts +226 -226
  99. package/src/dto/filter-types.ts +141 -141
  100. package/src/dto/index.ts +95 -95
  101. package/src/dto/openapi/generators/base.ts +29 -29
  102. package/src/dto/openapi/generators/column.ts +37 -37
  103. package/src/dto/openapi/generators/dto.ts +91 -91
  104. package/src/dto/openapi/generators/filter.ts +75 -75
  105. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  106. package/src/dto/openapi/generators/pagination.ts +111 -111
  107. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  108. package/src/dto/openapi/index.ts +12 -12
  109. package/src/dto/openapi/types.ts +101 -101
  110. package/src/dto/openapi/utilities.ts +95 -95
  111. package/src/dto/pagination-utils.ts +150 -150
  112. package/src/dto/transform.ts +197 -197
  113. package/src/index.ts +86 -86
  114. package/src/orm/als.ts +58 -58
  115. package/src/orm/domain-event-bus.ts +119 -119
  116. package/src/orm/entity-context.ts +86 -86
  117. package/src/orm/entity-materializer.ts +159 -159
  118. package/src/orm/entity-meta.ts +97 -97
  119. package/src/orm/entity-registry.ts +39 -39
  120. package/src/orm/entity-relation-cache.ts +39 -39
  121. package/src/orm/execute.ts +194 -194
  122. package/src/orm/execution-context.ts +18 -18
  123. package/src/orm/hydration-context.ts +26 -26
  124. package/src/orm/hydration.ts +112 -112
  125. package/src/orm/identity-map.ts +61 -61
  126. package/src/orm/interceptor-pipeline.ts +16 -16
  127. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  128. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  129. package/src/orm/lazy-batch/has-many.ts +69 -69
  130. package/src/orm/lazy-batch/has-one.ts +68 -68
  131. package/src/orm/lazy-batch/shared.ts +125 -125
  132. package/src/orm/orm-session.ts +620 -620
  133. package/src/orm/orm.ts +34 -34
  134. package/src/orm/pooled-executor-factory.ts +121 -121
  135. package/src/orm/query-logger.ts +40 -40
  136. package/src/orm/relation-change-processor.ts +420 -420
  137. package/src/orm/relations/belongs-to.ts +143 -143
  138. package/src/orm/relations/has-many.ts +204 -204
  139. package/src/orm/relations/has-one.ts +174 -174
  140. package/src/orm/relations/many-to-many.ts +288 -288
  141. package/src/orm/relations/morph-many.ts +156 -156
  142. package/src/orm/relations/morph-one.ts +151 -151
  143. package/src/orm/relations/morph-to.ts +162 -162
  144. package/src/orm/runtime-types.ts +92 -92
  145. package/src/orm/save-graph.ts +561 -561
  146. package/src/orm/transaction-runner.ts +24 -24
  147. package/src/orm/unit-of-work.ts +426 -426
  148. package/src/query/index.ts +69 -69
  149. package/src/query/target.ts +46 -46
  150. package/src/query-builder/column-selector.ts +81 -81
  151. package/src/query-builder/delete.ts +141 -141
  152. package/src/query-builder/insert-query-state.ts +149 -149
  153. package/src/query-builder/insert.ts +117 -117
  154. package/src/query-builder/query-ast-service.ts +287 -287
  155. package/src/query-builder/query-resolution.ts +78 -78
  156. package/src/query-builder/raw-column-parser.ts +38 -38
  157. package/src/query-builder/relation-filter-utils.ts +153 -153
  158. package/src/query-builder/relation-join-planner.ts +10 -10
  159. package/src/query-builder/relation-manager.ts +87 -87
  160. package/src/query-builder/relation-projection-helper.ts +102 -102
  161. package/src/query-builder/relation-types.ts +9 -9
  162. package/src/query-builder/relation-utils.ts +15 -15
  163. package/src/query-builder/select/cte-facet.ts +40 -40
  164. package/src/query-builder/select/from-facet.ts +80 -80
  165. package/src/query-builder/select/join-facet.ts +62 -62
  166. package/src/query-builder/select/predicate-facet.ts +104 -104
  167. package/src/query-builder/select/projection-facet.ts +70 -70
  168. package/src/query-builder/select/relation-facet.ts +81 -81
  169. package/src/query-builder/select/setop-facet.ts +36 -36
  170. package/src/query-builder/select-helpers.ts +1 -1
  171. package/src/query-builder/select-query-builder-deps.ts +130 -130
  172. package/src/query-builder/select-query-state.ts +207 -207
  173. package/src/query-builder/update.ts +151 -151
  174. package/src/schema/column-types.ts +322 -322
  175. package/src/schema/table-guards.ts +37 -37
  176. package/src/schema/table.ts +281 -281
  177. package/src/tree/index.ts +13 -13
  178. package/src/tree/nested-set-strategy.ts +545 -545
  179. package/src/tree/tree-decorator.ts +397 -397
  180. package/src/tree/tree-manager.ts +754 -754
  181. package/src/tree/tree-query.ts +427 -427
  182. package/src/tree/tree-types.ts +299 -299
@@ -1,31 +1,31 @@
1
- import { SelectQueryNode } from './query.js';
2
- import { SqlOperator, BitwiseOperator } from '../sql/sql.js';
3
- import { ColumnRef } from './types.js';
4
- import {
5
- ColumnNode,
6
- LiteralNode,
7
- JsonPathNode,
8
- OperandNode,
9
- CaseExpressionNode,
10
- CastExpressionNode,
11
- BinaryExpressionNode,
1
+ import { SelectQueryNode } from './query.js';
2
+ import { SqlOperator, BitwiseOperator } from '../sql/sql.js';
3
+ import { ColumnRef } from './types.js';
4
+ import {
5
+ ColumnNode,
6
+ LiteralNode,
7
+ JsonPathNode,
8
+ OperandNode,
9
+ CaseExpressionNode,
10
+ CastExpressionNode,
11
+ BinaryExpressionNode,
12
12
  ExpressionNode,
13
13
  LogicalExpressionNode,
14
14
  NotExpressionNode,
15
15
  NullExpressionNode,
16
16
  InExpressionNode,
17
- ExistsExpressionNode,
18
- InExpressionRight,
19
- ScalarSubqueryNode,
20
- BetweenExpressionNode,
21
- isOperandNode,
22
- AliasRefNode,
23
- ArithmeticExpressionNode,
24
- BitwiseExpressionNode,
25
- CollateExpressionNode,
26
- IsDistinctExpressionNode
27
- } from './expression-nodes.js';
28
-
17
+ ExistsExpressionNode,
18
+ InExpressionRight,
19
+ ScalarSubqueryNode,
20
+ BetweenExpressionNode,
21
+ isOperandNode,
22
+ AliasRefNode,
23
+ ArithmeticExpressionNode,
24
+ BitwiseExpressionNode,
25
+ CollateExpressionNode,
26
+ IsDistinctExpressionNode
27
+ } from './expression-nodes.js';
28
+
29
29
  export type LiteralValue = LiteralNode['value'];
30
30
  export type ValueOperandInput = OperandNode | LiteralValue;
31
31
  type ScalarComparable = string | number | boolean | Date | Buffer | null;
@@ -33,53 +33,53 @@ type OrderedComparable = string | number | Date | Buffer;
33
33
  type NotArray<T> = T extends readonly unknown[] ? never : T;
34
34
 
35
35
  export type TypedLike<T> = { tsType?: T } | { __tsType: T };
36
-
37
- /**
38
- * Type guard to check if a value is a literal value
39
- */
40
- const isLiteralValue = (value: unknown): value is LiteralValue =>
41
- value === null ||
42
- typeof value === 'string' ||
43
- typeof value === 'number' ||
44
- typeof value === 'boolean' ||
45
- value instanceof Date ||
46
- Buffer.isBuffer(value);
47
-
48
-
49
- /**
50
- * Converts a primitive value to a LiteralNode
51
- */
52
- const toLiteralNode = (value: LiteralValue): LiteralNode => ({
53
- type: 'Literal',
54
- value: value instanceof Date ? value.toISOString() : value
55
- });
56
-
57
- /**
58
- * Converts a ColumnRef to a ColumnNode
59
- * @throws Error if the ColumnRef doesn't have a table specified
60
- */
61
- const columnRefToNode = (col: ColumnRef): ColumnNode => {
62
- if (!col.table) {
63
- throw new Error(
64
- `Column "${col.name}" requires a table reference. ` +
65
- `Use columnOperand with a fully qualified ColumnRef or ColumnNode.`
66
- );
67
- }
68
- return { type: 'Column', table: col.table, name: col.name };
69
- };
70
-
71
- /**
72
- * Unified conversion function: converts any valid input to an OperandNode
73
- * @param value - Value to convert (OperandNode, ColumnRef, or literal value)
74
- * @returns OperandNode representing the value
75
- */
36
+
37
+ /**
38
+ * Type guard to check if a value is a literal value
39
+ */
40
+ const isLiteralValue = (value: unknown): value is LiteralValue =>
41
+ value === null ||
42
+ typeof value === 'string' ||
43
+ typeof value === 'number' ||
44
+ typeof value === 'boolean' ||
45
+ value instanceof Date ||
46
+ Buffer.isBuffer(value);
47
+
48
+
49
+ /**
50
+ * Converts a primitive value to a LiteralNode
51
+ */
52
+ const toLiteralNode = (value: LiteralValue): LiteralNode => ({
53
+ type: 'Literal',
54
+ value: value instanceof Date ? value.toISOString() : value
55
+ });
56
+
57
+ /**
58
+ * Converts a ColumnRef to a ColumnNode
59
+ * @throws Error if the ColumnRef doesn't have a table specified
60
+ */
61
+ const columnRefToNode = (col: ColumnRef): ColumnNode => {
62
+ if (!col.table) {
63
+ throw new Error(
64
+ `Column "${col.name}" requires a table reference. ` +
65
+ `Use columnOperand with a fully qualified ColumnRef or ColumnNode.`
66
+ );
67
+ }
68
+ return { type: 'Column', table: col.table, name: col.name };
69
+ };
70
+
71
+ /**
72
+ * Unified conversion function: converts any valid input to an OperandNode
73
+ * @param value - Value to convert (OperandNode, ColumnRef, or literal value)
74
+ * @returns OperandNode representing the value
75
+ */
76
76
  const toOperandNode = (value: OperandNode | ColumnRef | LiteralValue): OperandNode => {
77
77
  // Already an operand node
78
78
  if (isOperandNode(value)) {
79
79
  return value;
80
80
  }
81
-
82
- // Literal value
81
+
82
+ // Literal value
83
83
  if (isLiteralValue(value)) {
84
84
  return toLiteralNode(value);
85
85
  }
@@ -94,255 +94,255 @@ const toOperandNode = (value: OperandNode | ColumnRef | LiteralValue): OperandNo
94
94
  // Must be ColumnRef
95
95
  return columnRefToNode(value as ColumnRef);
96
96
  };
97
-
98
- /**
99
- * Converts a primitive or existing operand into an operand node
100
- * @param value - Value or operand to normalize
101
- * @returns OperandNode representing the value
102
- */
103
- export const valueToOperand = (value: ValueOperandInput): OperandNode => {
104
- if (isOperandNode(value)) {
105
- return value;
106
- }
107
- return toLiteralNode(value);
108
- };
109
-
110
- /**
111
- * Converts various input types to an OperandNode
112
- */
113
- const toOperand = (val: OperandNode | ColumnRef | LiteralValue): OperandNode => toOperandNode(val);
114
-
115
- export const isValueOperandInput = (value: unknown): value is ValueOperandInput =>
116
- isOperandNode(value) || isLiteralValue(value);
117
-
118
- export type SelectQueryInput = SelectQueryNode | { getAST(): SelectQueryNode };
119
-
120
- const hasQueryAst = (value: SelectQueryInput): value is { getAST(): SelectQueryNode } =>
121
- typeof (value as { getAST?: unknown }).getAST === 'function';
122
-
123
- const resolveSelectQueryNode = (query: SelectQueryInput): SelectQueryNode =>
124
- hasQueryAst(query) ? query.getAST() : query;
125
-
126
- const toScalarSubqueryNode = (query: SelectQueryInput): ScalarSubqueryNode => ({
127
- type: 'ScalarSubquery',
128
- query: resolveSelectQueryNode(query)
129
- });
130
-
131
- /**
132
- * Converts a ColumnRef or ColumnNode to a ColumnNode
133
- * @param col - Column reference or node
134
- * @returns ColumnNode
135
- * @throws Error if ColumnRef doesn't have a table specified
136
- */
137
- export const columnOperand = (col: ColumnRef | ColumnNode): ColumnNode => {
138
- if (isOperandNode(col) && col.type === 'Column') {
139
- return col;
140
- }
141
- return columnRefToNode(col as ColumnRef);
142
- };
143
-
144
- /**
145
- * Marks a column reference as an outer-scope reference for correlated subqueries.
146
- * Primarily semantic; SQL rendering still uses the provided table/alias name.
147
- */
148
- export const outerRef = (col: ColumnRef | ColumnNode): ColumnNode => ({
149
- ...columnOperand(col),
150
- scope: 'outer'
151
- });
152
-
153
- /**
154
- * References a SELECT alias (useful for ORDER BY / GROUP BY).
155
- */
156
- export const aliasRef = (name: string): AliasRefNode => ({
157
- type: 'AliasRef',
158
- name
159
- });
160
-
161
- /**
162
- * Creates an outer-scoped column reference using a specific table or alias name.
163
- */
164
- export const correlateBy = (table: string, column: string): ColumnNode => outerRef({ name: column, table });
165
-
97
+
98
+ /**
99
+ * Converts a primitive or existing operand into an operand node
100
+ * @param value - Value or operand to normalize
101
+ * @returns OperandNode representing the value
102
+ */
103
+ export const valueToOperand = (value: ValueOperandInput): OperandNode => {
104
+ if (isOperandNode(value)) {
105
+ return value;
106
+ }
107
+ return toLiteralNode(value);
108
+ };
109
+
110
+ /**
111
+ * Converts various input types to an OperandNode
112
+ */
113
+ const toOperand = (val: OperandNode | ColumnRef | LiteralValue): OperandNode => toOperandNode(val);
114
+
115
+ export const isValueOperandInput = (value: unknown): value is ValueOperandInput =>
116
+ isOperandNode(value) || isLiteralValue(value);
117
+
118
+ export type SelectQueryInput = SelectQueryNode | { getAST(): SelectQueryNode };
119
+
120
+ const hasQueryAst = (value: SelectQueryInput): value is { getAST(): SelectQueryNode } =>
121
+ typeof (value as { getAST?: unknown }).getAST === 'function';
122
+
123
+ const resolveSelectQueryNode = (query: SelectQueryInput): SelectQueryNode =>
124
+ hasQueryAst(query) ? query.getAST() : query;
125
+
126
+ const toScalarSubqueryNode = (query: SelectQueryInput): ScalarSubqueryNode => ({
127
+ type: 'ScalarSubquery',
128
+ query: resolveSelectQueryNode(query)
129
+ });
130
+
131
+ /**
132
+ * Converts a ColumnRef or ColumnNode to a ColumnNode
133
+ * @param col - Column reference or node
134
+ * @returns ColumnNode
135
+ * @throws Error if ColumnRef doesn't have a table specified
136
+ */
137
+ export const columnOperand = (col: ColumnRef | ColumnNode): ColumnNode => {
138
+ if (isOperandNode(col) && col.type === 'Column') {
139
+ return col;
140
+ }
141
+ return columnRefToNode(col as ColumnRef);
142
+ };
143
+
144
+ /**
145
+ * Marks a column reference as an outer-scope reference for correlated subqueries.
146
+ * Primarily semantic; SQL rendering still uses the provided table/alias name.
147
+ */
148
+ export const outerRef = (col: ColumnRef | ColumnNode): ColumnNode => ({
149
+ ...columnOperand(col),
150
+ scope: 'outer'
151
+ });
152
+
153
+ /**
154
+ * References a SELECT alias (useful for ORDER BY / GROUP BY).
155
+ */
156
+ export const aliasRef = (name: string): AliasRefNode => ({
157
+ type: 'AliasRef',
158
+ name
159
+ });
160
+
161
+ /**
162
+ * Creates an outer-scoped column reference using a specific table or alias name.
163
+ */
164
+ export const correlateBy = (table: string, column: string): ColumnNode => outerRef({ name: column, table });
165
+
166
166
  const createBinaryExpression = (
167
167
  operator: SqlOperator,
168
168
  left: OperandNode | ColumnRef,
169
169
  right: OperandNode | ColumnRef | LiteralValue,
170
170
  escape?: string
171
171
  ): BinaryExpressionNode => {
172
- const node: BinaryExpressionNode = {
173
- type: 'BinaryExpression',
174
- left: toOperandNode(left),
175
- operator,
176
- right: toOperand(right)
177
- };
178
-
179
- if (escape !== undefined) {
180
- node.escape = toLiteralNode(escape);
181
- }
182
-
183
- return node;
184
- };
185
-
186
- /**
187
- * Creates an equality expression (`left = right`).
188
- *
189
- * Supports type safety when used with `ColumnDef` objects containing strict type information.
190
- *
191
- * @param left - The left operand (column or value).
192
- * @param right - The right operand (column or value).
193
- * @returns A `BinaryExpressionNode` representing the equality check.
194
- *
195
- * @example
196
- * // Basic usage
197
- * eq(users.id, 1);
198
- *
199
- * // With strict typing (typescript will error if types mismatch)
200
- * eq(users.firstName, 'Ada');
201
- */
172
+ const node: BinaryExpressionNode = {
173
+ type: 'BinaryExpression',
174
+ left: toOperandNode(left),
175
+ operator,
176
+ right: toOperand(right)
177
+ };
178
+
179
+ if (escape !== undefined) {
180
+ node.escape = toLiteralNode(escape);
181
+ }
182
+
183
+ return node;
184
+ };
185
+
186
+ /**
187
+ * Creates an equality expression (`left = right`).
188
+ *
189
+ * Supports type safety when used with `ColumnDef` objects containing strict type information.
190
+ *
191
+ * @param left - The left operand (column or value).
192
+ * @param right - The right operand (column or value).
193
+ * @returns A `BinaryExpressionNode` representing the equality check.
194
+ *
195
+ * @example
196
+ * // Basic usage
197
+ * eq(users.id, 1);
198
+ *
199
+ * // With strict typing (typescript will error if types mismatch)
200
+ * eq(users.firstName, 'Ada');
201
+ */
202
202
  export function eq<T extends ScalarComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
203
203
  export function eq<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
204
204
  export function eq(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
205
205
  return createBinaryExpression('=', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
206
206
  }
207
-
208
- /**
209
- * Creates a not equal expression (`left != right`).
210
- *
211
- * @param left - The left operand (column or value).
212
- * @param right - The right operand (column or value).
213
- * @returns A `BinaryExpressionNode` representing the inequality check.
214
- *
215
- * @example
216
- * neq(users.status, 'inactive');
217
- */
207
+
208
+ /**
209
+ * Creates a not equal expression (`left != right`).
210
+ *
211
+ * @param left - The left operand (column or value).
212
+ * @param right - The right operand (column or value).
213
+ * @returns A `BinaryExpressionNode` representing the inequality check.
214
+ *
215
+ * @example
216
+ * neq(users.status, 'inactive');
217
+ */
218
218
  export function neq<T extends ScalarComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
219
219
  export function neq<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
220
220
  export function neq(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
221
221
  return createBinaryExpression('!=', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
222
222
  }
223
-
224
- /**
225
- * Creates a greater-than expression (`left > right`).
226
- *
227
- * @param left - The left operand.
228
- * @param right - The right operand.
229
- * @returns A `BinaryExpressionNode`.
230
- *
231
- * @example
232
- * gt(users.age, 18);
233
- */
223
+
224
+ /**
225
+ * Creates a greater-than expression (`left > right`).
226
+ *
227
+ * @param left - The left operand.
228
+ * @param right - The right operand.
229
+ * @returns A `BinaryExpressionNode`.
230
+ *
231
+ * @example
232
+ * gt(users.age, 18);
233
+ */
234
234
  export function gt<T extends OrderedComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
235
235
  export function gt<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
236
236
  export function gt(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
237
237
  return createBinaryExpression('>', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
238
238
  }
239
-
240
- /**
241
- * Creates a greater-than-or-equal expression (`left >= right`).
242
- *
243
- * @param left - The left operand.
244
- * @param right - The right operand.
245
- * @returns A `BinaryExpressionNode`.
246
- *
247
- * @example
248
- * gte(users.score, 100);
249
- */
239
+
240
+ /**
241
+ * Creates a greater-than-or-equal expression (`left >= right`).
242
+ *
243
+ * @param left - The left operand.
244
+ * @param right - The right operand.
245
+ * @returns A `BinaryExpressionNode`.
246
+ *
247
+ * @example
248
+ * gte(users.score, 100);
249
+ */
250
250
  export function gte<T extends OrderedComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
251
251
  export function gte<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
252
252
  export function gte(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
253
253
  return createBinaryExpression('>=', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
254
254
  }
255
-
256
- /**
257
- * Creates a less-than expression (`left < right`).
258
- *
259
- * @param left - The left operand.
260
- * @param right - The right operand.
261
- * @returns A `BinaryExpressionNode`.
262
- *
263
- * @example
264
- * lt(inventory.stock, 5);
265
- */
255
+
256
+ /**
257
+ * Creates a less-than expression (`left < right`).
258
+ *
259
+ * @param left - The left operand.
260
+ * @param right - The right operand.
261
+ * @returns A `BinaryExpressionNode`.
262
+ *
263
+ * @example
264
+ * lt(inventory.stock, 5);
265
+ */
266
266
  export function lt<T extends OrderedComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
267
267
  export function lt<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
268
268
  export function lt(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
269
269
  return createBinaryExpression('<', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
270
270
  }
271
-
272
- /**
273
- * Creates a less-than-or-equal expression (`left <= right`).
274
- *
275
- * @param left - The left operand.
276
- * @param right - The right operand.
277
- * @returns A `BinaryExpressionNode`.
278
- *
279
- * @example
280
- * lte(products.price, 50.00);
281
- */
271
+
272
+ /**
273
+ * Creates a less-than-or-equal expression (`left <= right`).
274
+ *
275
+ * @param left - The left operand.
276
+ * @param right - The right operand.
277
+ * @returns A `BinaryExpressionNode`.
278
+ *
279
+ * @example
280
+ * lte(products.price, 50.00);
281
+ */
282
282
  export function lte<T extends OrderedComparable>(left: TypedLike<T>, right: T | TypedLike<T>): BinaryExpressionNode;
283
283
  export function lte<T>(left: OperandNode | ColumnRef, right: T & NotArray<T>): BinaryExpressionNode;
284
284
  export function lte(left: OperandNode | ColumnRef | TypedLike<unknown>, right: unknown): BinaryExpressionNode {
285
285
  return createBinaryExpression('<=', left as OperandNode | ColumnRef, right as OperandNode | ColumnRef | LiteralValue);
286
286
  }
287
-
288
- /**
289
- * Creates a `LIKE` pattern matching expression.
290
- *
291
- * @param left - The column or expression to check.
292
- * @param pattern - The pattern string (e.g., 'A%').
293
- * @param escape - Optional escape character.
294
- * @returns A `BinaryExpressionNode`.
295
- *
296
- * @example
297
- * like(users.email, '%@gmail.com');
298
- */
299
- export const like = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
300
- createBinaryExpression('LIKE', left, pattern, escape);
301
-
302
- /**
303
- * Creates a `NOT LIKE` pattern matching expression.
304
- *
305
- * @param left - The column or expression to check.
306
- * @param pattern - The pattern string.
307
- * @param escape - Optional escape character.
308
- * @returns A `BinaryExpressionNode`.
309
- *
310
- * @example
311
- * notLike(users.email, 'test%');
312
- */
313
- export const notLike = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
314
- createBinaryExpression('NOT LIKE', left, pattern, escape);
315
-
316
- /**
317
- * Creates a logical AND expression to combine multiple conditions.
318
- *
319
- * @param operands - One or more conditions to combine.
320
- * @returns A `LogicalExpressionNode`.
321
- *
322
- * @example
323
- * and(
324
- * eq(users.isActive, true),
325
- * gt(users.age, 18)
326
- * );
327
- */
328
- export const and = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
329
- type: 'LogicalExpression',
330
- operator: 'AND',
331
- operands
332
- });
333
-
334
- /**
335
- * Creates a logical OR expression to combine multiple conditions.
336
- *
337
- * @param operands - One or more conditions to combine.
338
- * @returns A `LogicalExpressionNode`.
339
- *
340
- * @example
341
- * or(
342
- * eq(users.role, 'admin'),
343
- * eq(users.role, 'moderator')
344
- * );
345
- */
287
+
288
+ /**
289
+ * Creates a `LIKE` pattern matching expression.
290
+ *
291
+ * @param left - The column or expression to check.
292
+ * @param pattern - The pattern string (e.g., 'A%').
293
+ * @param escape - Optional escape character.
294
+ * @returns A `BinaryExpressionNode`.
295
+ *
296
+ * @example
297
+ * like(users.email, '%@gmail.com');
298
+ */
299
+ export const like = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
300
+ createBinaryExpression('LIKE', left, pattern, escape);
301
+
302
+ /**
303
+ * Creates a `NOT LIKE` pattern matching expression.
304
+ *
305
+ * @param left - The column or expression to check.
306
+ * @param pattern - The pattern string.
307
+ * @param escape - Optional escape character.
308
+ * @returns A `BinaryExpressionNode`.
309
+ *
310
+ * @example
311
+ * notLike(users.email, 'test%');
312
+ */
313
+ export const notLike = (left: OperandNode | ColumnRef, pattern: string, escape?: string): BinaryExpressionNode =>
314
+ createBinaryExpression('NOT LIKE', left, pattern, escape);
315
+
316
+ /**
317
+ * Creates a logical AND expression to combine multiple conditions.
318
+ *
319
+ * @param operands - One or more conditions to combine.
320
+ * @returns A `LogicalExpressionNode`.
321
+ *
322
+ * @example
323
+ * and(
324
+ * eq(users.isActive, true),
325
+ * gt(users.age, 18)
326
+ * );
327
+ */
328
+ export const and = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
329
+ type: 'LogicalExpression',
330
+ operator: 'AND',
331
+ operands
332
+ });
333
+
334
+ /**
335
+ * Creates a logical OR expression to combine multiple conditions.
336
+ *
337
+ * @param operands - One or more conditions to combine.
338
+ * @returns A `LogicalExpressionNode`.
339
+ *
340
+ * @example
341
+ * or(
342
+ * eq(users.role, 'admin'),
343
+ * eq(users.role, 'moderator')
344
+ * );
345
+ */
346
346
  export const or = (...operands: ExpressionNode[]): LogicalExpressionNode => ({
347
347
  type: 'LogicalExpression',
348
348
  operator: 'OR',
@@ -365,470 +365,470 @@ export const not = (operand: ExpressionNode): NotExpressionNode => ({
365
365
  type: 'NotExpression',
366
366
  operand
367
367
  });
368
-
369
- /**
370
- * Creates an IS NULL check (`left IS NULL`).
371
- *
372
- * @param left - The operand to check.
373
- * @returns A `NullExpressionNode`.
374
- *
375
- * @example
376
- * isNull(users.deletedAt);
377
- */
378
- export const isNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
379
- type: 'NullExpression',
380
- left: toOperandNode(left),
381
- operator: 'IS NULL'
382
- });
383
-
384
- /**
385
- * Creates an IS NOT NULL check (`left IS NOT NULL`).
386
- *
387
- * @param left - The operand to check.
388
- * @returns A `NullExpressionNode`.
389
- *
390
- * @example
391
- * isNotNull(users.email);
392
- */
393
- export const isNotNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
394
- type: 'NullExpression',
395
- left: toOperandNode(left),
396
- operator: 'IS NOT NULL'
397
- });
398
-
399
- const createInExpression = (
400
- operator: 'IN' | 'NOT IN',
401
- left: OperandNode | ColumnRef,
402
- right: InExpressionRight
403
- ): InExpressionNode => ({
404
- type: 'InExpression',
405
- left: toOperandNode(left),
406
- operator,
407
- right
408
- });
409
-
410
- /**
411
- * Creates an IN list check (`left IN (v1, v2, ...)`).
412
- *
413
- * @param left - The operand to check.
414
- * @param values - An array of values to check against.
415
- * @returns An `InExpressionNode`.
416
- *
417
- * @example
418
- * inList(users.status, ['active', 'pending']);
419
- */
420
- export const inList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
421
- createInExpression('IN', left, values.map(v => toOperand(v)));
422
-
423
- /**
424
- * Creates a NOT IN list check (`left NOT IN (v1, v2, ...)`).
425
- *
426
- * @param left - The operand to check.
427
- * @param values - An array of values to check against.
428
- * @returns An `InExpressionNode`.
429
- *
430
- * @example
431
- * notInList(users.id, [1, 2, 3]);
432
- */
433
- export const notInList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
434
- createInExpression('NOT IN', left, values.map(v => toOperand(v)));
435
-
436
- /**
437
- * Creates an IN subquery check (`left IN (SELECT ...)`).
438
- *
439
- * @param left - The operand to check.
440
- * @param subquery - The subquery to run.
441
- * @returns An `InExpressionNode`.
442
- *
443
- * @example
444
- * inSubquery(
445
- * posts.authorId,
446
- * selectFromEntity(User).select({ id: users.id }).where(eq(users.isActive, true))
447
- * );
448
- */
449
- export const inSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
450
- createInExpression('IN', left, toScalarSubqueryNode(subquery));
451
-
452
- /**
453
- * Creates a NOT IN subquery check (`left NOT IN (SELECT ...)`).
454
- *
455
- * @param left - The operand to check.
456
- * @param subquery - The subquery to run.
457
- * @returns An `InExpressionNode`.
458
- *
459
- * @example
460
- * notInSubquery(
461
- * users.id,
462
- * selectFromEntity(Blacklist).select({ userId: blacklist.userId })
463
- * );
464
- */
465
- export const notInSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
466
- createInExpression('NOT IN', left, toScalarSubqueryNode(subquery));
467
-
468
- const createBetweenExpression = (
469
- operator: 'BETWEEN' | 'NOT BETWEEN',
470
- left: OperandNode | ColumnRef,
471
- lower: OperandNode | ColumnRef | string | number,
472
- upper: OperandNode | ColumnRef | string | number
473
- ): BetweenExpressionNode => ({
474
- type: 'BetweenExpression',
475
- left: toOperandNode(left),
476
- operator,
477
- lower: toOperand(lower),
478
- upper: toOperand(upper)
479
- });
480
-
481
- /**
482
- * Creates a BETWEEN check (`left BETWEEN lower AND upper`).
483
- *
484
- * @param left - The operand to check.
485
- * @param lower - The lower bound (inclusive).
486
- * @param upper - The upper bound (inclusive).
487
- * @returns A `BetweenExpressionNode`.
488
- *
489
- * @example
490
- * between(products.price, 10, 100);
491
- */
492
- export const between = (
493
- left: OperandNode | ColumnRef,
494
- lower: OperandNode | ColumnRef | string | number,
495
- upper: OperandNode | ColumnRef | string | number
496
- ): BetweenExpressionNode => createBetweenExpression('BETWEEN', left, lower, upper);
497
-
498
- /**
499
- * Creates a NOT BETWEEN check (`left NOT BETWEEN lower AND upper`).
500
- *
501
- * @param left - The operand to check.
502
- * @param lower - The lower bound (inclusive).
503
- * @param upper - The upper bound (inclusive).
504
- * @returns A `BetweenExpressionNode`.
505
- *
506
- * @example
507
- * notBetween(users.age, 20, 30);
508
- */
509
- export const notBetween = (
510
- left: OperandNode | ColumnRef,
511
- lower: OperandNode | ColumnRef | string | number,
512
- upper: OperandNode | ColumnRef | string | number
513
- ): BetweenExpressionNode => createBetweenExpression('NOT BETWEEN', left, lower, upper);
514
-
515
- const createArithmeticExpression = (
516
- operator: '+' | '-' | '*' | '/',
517
- left: OperandNode | ColumnRef,
518
- right: OperandNode | ColumnRef | string | number
519
- ): ArithmeticExpressionNode => ({
520
- type: 'ArithmeticExpression',
521
- left: toOperand(left),
522
- operator,
523
- right: toOperand(right)
524
- });
525
-
526
- /**
527
- * Creates an addition expression (`left + right`).
528
- *
529
- * @param left - The left operand.
530
- * @param right - The right operand.
531
- * @returns An `ArithmeticExpressionNode`.
532
- */
533
- export const add = (
534
- left: OperandNode | ColumnRef,
535
- right: OperandNode | ColumnRef | string | number
536
- ): ArithmeticExpressionNode => createArithmeticExpression('+', left, right);
537
-
538
- /**
539
- * Creates a subtraction expression (`left - right`).
540
- *
541
- * @param left - The left operand.
542
- * @param right - The right operand.
543
- * @returns An `ArithmeticExpressionNode`.
544
- */
545
- export const sub = (
546
- left: OperandNode | ColumnRef,
547
- right: OperandNode | ColumnRef | string | number
548
- ): ArithmeticExpressionNode => createArithmeticExpression('-', left, right);
549
-
550
- /**
551
- * Creates a multiplication expression (`left * right`).
552
- *
553
- * @param left - The left operand.
554
- * @param right - The right operand.
555
- * @returns An `ArithmeticExpressionNode`.
556
- */
557
- export const mul = (
558
- left: OperandNode | ColumnRef,
559
- right: OperandNode | ColumnRef | string | number
560
- ): ArithmeticExpressionNode => createArithmeticExpression('*', left, right);
561
-
562
- /**
563
- * Creates a division expression (`left / right`).
564
- *
565
- * @param left - The left operand.
566
- * @param right - The right operand.
567
- * @returns An `ArithmeticExpressionNode`.
568
- */
569
- export const div = (
570
- left: OperandNode | ColumnRef,
571
- right: OperandNode | ColumnRef | string | number
572
- ): ArithmeticExpressionNode => createArithmeticExpression('/', left, right);
573
-
574
- const createBitwiseExpression = (
575
- operator: BitwiseOperator,
576
- left: OperandNode | ColumnRef,
577
- right: OperandNode | ColumnRef | string | number
578
- ): BitwiseExpressionNode => ({
579
- type: 'BitwiseExpression',
580
- left: toOperand(left),
581
- operator,
582
- right: toOperand(right)
583
- });
584
-
585
- /**
586
- * Creates a bitwise AND expression (`left & right`).
587
- *
588
- * @param left - The left operand.
589
- * @param right - The right operand.
590
- * @returns A `BitwiseExpressionNode`.
591
- */
592
- export const bitAnd = (
593
- left: OperandNode | ColumnRef,
594
- right: OperandNode | ColumnRef | string | number
595
- ): BitwiseExpressionNode => createBitwiseExpression('&', left, right);
596
-
597
- /**
598
- * Creates a bitwise OR expression (`left | right`).
599
- *
600
- * @param left - The left operand.
601
- * @param right - The right operand.
602
- * @returns A `BitwiseExpressionNode`.
603
- */
604
- export const bitOr = (
605
- left: OperandNode | ColumnRef,
606
- right: OperandNode | ColumnRef | string | number
607
- ): BitwiseExpressionNode => createBitwiseExpression('|', left, right);
608
-
609
- /**
610
- * Creates a bitwise XOR expression (`left ^ right`).
611
- *
612
- * @param left - The left operand.
613
- * @param right - The right operand.
614
- * @returns A `BitwiseExpressionNode`.
615
- */
616
- export const bitXor = (
617
- left: OperandNode | ColumnRef,
618
- right: OperandNode | ColumnRef | string | number
619
- ): BitwiseExpressionNode => createBitwiseExpression('^', left, right);
620
-
621
- /**
622
- * Creates a bitwise left-shift expression (`left << right`).
623
- *
624
- * @param left - The left operand.
625
- * @param right - The right operand (number of bits).
626
- * @returns A `BitwiseExpressionNode`.
627
- */
628
- export const shiftLeft = (
629
- left: OperandNode | ColumnRef,
630
- right: OperandNode | ColumnRef | string | number
631
- ): BitwiseExpressionNode => createBitwiseExpression('<<', left, right);
632
-
633
- /**
634
- * Creates a bitwise right-shift expression (`left >> right`).
635
- *
636
- * @param left - The left operand.
637
- * @param right - The right operand (number of bits).
638
- * @returns A `BitwiseExpressionNode`.
639
- */
640
- export const shiftRight = (
641
- left: OperandNode | ColumnRef,
642
- right: OperandNode | ColumnRef | string | number
643
- ): BitwiseExpressionNode => createBitwiseExpression('>>', left, right);
644
-
645
- /**
646
- * Creates a JSON path extraction expression.
647
- *
648
- * @param col - The source column (should be a JSON/JSONB column).
649
- * @param path - The JSON path expression (e.g., '$.address.city').
650
- * @returns A `JsonPathNode`.
651
- *
652
- * @example
653
- * jsonPath(users.profile, '$.settings.theme');
654
- */
655
- export const jsonPath = (col: ColumnRef | ColumnNode, path: string): JsonPathNode => ({
656
- type: 'JsonPath',
657
- column: columnOperand(col),
658
- path
659
- });
660
-
661
- /**
662
- * Creates a CASE expression (`CASE WHEN ... THEN ... ELSE ... END`).
663
- *
664
- * @param conditions - An array of `{ when, then }` objects.
665
- * @param elseValue - Optional value for the `ELSE` clause.
666
- * @returns A `CaseExpressionNode`.
667
- *
668
- * @example
669
- * caseWhen([
670
- * { when: gt(users.age, 65), then: 'Senior' },
671
- * { when: gt(users.age, 18), then: 'Adult' }
672
- * ], 'Minor');
673
- */
674
- export const caseWhen = (
675
- conditions: { when: ExpressionNode; then: OperandNode | ColumnRef | string | number | boolean | null }[],
676
- elseValue?: OperandNode | ColumnRef | string | number | boolean | null
677
- ): CaseExpressionNode => ({
678
- type: 'CaseExpression',
679
- conditions: conditions.map(c => ({
680
- when: c.when,
681
- then: toOperand(c.then)
682
- })),
683
- else: elseValue !== undefined ? toOperand(elseValue) : undefined
684
- });
685
-
686
- /**
687
- * Creates a CAST expression (`CAST(expression AS type)`).
688
- *
689
- * @param expression - The expression to cast.
690
- * @param castType - The target SQL type (e.g., 'VARCHAR', 'INTEGER').
691
- * @returns A `CastExpressionNode`.
692
- *
693
- * @example
694
- * cast(users.age, 'VARCHAR');
695
- */
696
- export const cast = (
697
- expression: OperandNode | ColumnRef | string | number | boolean | null,
698
- castType: string
699
- ): CastExpressionNode => ({
700
- type: 'Cast',
701
- expression: toOperand(expression),
702
- castType
703
- });
704
-
705
- /**
706
- * Creates an EXISTS check (`EXISTS (SELECT ...)`).
707
- *
708
- * @param subquery - The subquery to check.
709
- * @returns An `ExistsExpressionNode`.
710
- *
711
- * @example
712
- * exists(
713
- * selectFromEntity(Order).where(eq(orders.userId, users.id))
714
- * );
715
- */
716
- export const exists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
717
- type: 'ExistsExpression',
718
- operator: 'EXISTS',
719
- subquery
720
- });
721
-
722
- /**
723
- * Creates a NOT EXISTS check (`NOT EXISTS (SELECT ...)`).
724
- *
725
- * @param subquery - The subquery to check.
726
- * @returns An `ExistsExpressionNode`.
727
- *
728
- * @example
729
- * notExists(
730
- * selectFromEntity(Subscription).where(eq(subscriptions.userId, users.id))
731
- * );
732
- */
733
- export const notExists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
734
- type: 'ExistsExpression',
735
- operator: 'NOT EXISTS',
736
- subquery
737
- });
738
-
739
- /**
740
- * Creates a COLLATE expression (`expression COLLATE collation`).
741
- *
742
- * @param expression - The expression string.
743
- * @param collation - The collation name (e.g., 'nocase').
744
- * @returns A `CollateExpressionNode`.
745
- *
746
- * @example
747
- * collate(users.email, 'nocase');
748
- */
749
- export const collate = (
750
- expression: OperandNode | ColumnRef | string | number | boolean | null,
751
- collation: string
752
- ): CollateExpressionNode => ({
753
- type: 'Collate',
754
- expression: toOperand(expression),
755
- collation
756
- });
757
-
758
- /**
759
- * Creates an `IS DISTINCT FROM` expression.
760
- *
761
- * Unlike `neq(a, b)`, this comparison is **null-safe**:
762
- * returns `true` when values are different **including** when one
763
- * (or both) is `NULL`. Never returns `NULL`.
764
- *
765
- * | left | right | neq | isDistinctFrom |
766
- * |-------|-------|-------|----------------|
767
- * | 1 | 2 | true | true |
768
- * | 1 | 1 | false | false |
769
- * | NULL | 1 | NULL | **true** |
770
- * | NULL | NULL | NULL | **false** |
771
- *
772
- * Compilation by dialect:
773
- * - PostgreSQL / SQLite / MSSQL → `left IS DISTINCT FROM right`
774
- * - MySQL → `NOT (left <=> right)`
775
- *
776
- * @param left - The left operand.
777
- * @param right - The right operand.
778
- * @returns An `IsDistinctExpressionNode`.
779
- *
780
- * @example
781
- * ```ts
782
- * // Find users whose email has changed compared to backup
783
- * where(isDistinctFrom(users.columns.email, backup.columns.email))
784
- * ```
785
- */
786
- export const isDistinctFrom = (
787
- left: OperandNode | ColumnRef,
788
- right: OperandNode | ColumnRef | LiteralValue
789
- ): IsDistinctExpressionNode => ({
790
- type: 'IsDistinctExpression',
791
- left: toOperandNode(left),
792
- operator: 'IS DISTINCT FROM',
793
- right: toOperand(right),
794
- });
795
-
796
- /**
797
- * Creates an `IS NOT DISTINCT FROM` expression.
798
- *
799
- * The inverse of `isDistinctFrom`. Equivalent to a **null-safe** equality:
800
- * returns `true` when values are equal **including** when both
801
- * are `NULL`. Never returns `NULL`.
802
- *
803
- * | left | right | eq | isNotDistinctFrom |
804
- * |-------|-------|-------|------------------|
805
- * | 1 | 1 | true | true |
806
- * | 1 | 2 | false | false |
807
- * | NULL | 1 | NULL | **false** |
808
- * | NULL | NULL | NULL | **true** |
809
- *
810
- * Compilation by dialect:
811
- * - PostgreSQL / SQLite / MSSQL → `left IS NOT DISTINCT FROM right`
812
- * - MySQL → `left <=> right`
813
- *
814
- * @param left - The left operand.
815
- * @param right - The right operand.
816
- * @returns An `IsDistinctExpressionNode`.
817
- *
818
- * @example
819
- * ```ts
820
- * // Rows where deletedAt is NULL or equal to a specific date
821
- * where(isNotDistinctFrom(orders.columns.deletedAt, null))
822
- * // → WHERE "orders"."deletedAt" IS NOT DISTINCT FROM NULL
823
- * // equivalent to: WHERE deletedAt IS NULL (but works with any value)
824
- * ```
825
- */
826
- export const isNotDistinctFrom = (
827
- left: OperandNode | ColumnRef,
828
- right: OperandNode | ColumnRef | LiteralValue
829
- ): IsDistinctExpressionNode => ({
830
- type: 'IsDistinctExpression',
831
- left: toOperandNode(left),
832
- operator: 'IS NOT DISTINCT FROM',
833
- right: toOperand(right),
834
- });
368
+
369
+ /**
370
+ * Creates an IS NULL check (`left IS NULL`).
371
+ *
372
+ * @param left - The operand to check.
373
+ * @returns A `NullExpressionNode`.
374
+ *
375
+ * @example
376
+ * isNull(users.deletedAt);
377
+ */
378
+ export const isNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
379
+ type: 'NullExpression',
380
+ left: toOperandNode(left),
381
+ operator: 'IS NULL'
382
+ });
383
+
384
+ /**
385
+ * Creates an IS NOT NULL check (`left IS NOT NULL`).
386
+ *
387
+ * @param left - The operand to check.
388
+ * @returns A `NullExpressionNode`.
389
+ *
390
+ * @example
391
+ * isNotNull(users.email);
392
+ */
393
+ export const isNotNull = (left: OperandNode | ColumnRef): NullExpressionNode => ({
394
+ type: 'NullExpression',
395
+ left: toOperandNode(left),
396
+ operator: 'IS NOT NULL'
397
+ });
398
+
399
+ const createInExpression = (
400
+ operator: 'IN' | 'NOT IN',
401
+ left: OperandNode | ColumnRef,
402
+ right: InExpressionRight
403
+ ): InExpressionNode => ({
404
+ type: 'InExpression',
405
+ left: toOperandNode(left),
406
+ operator,
407
+ right
408
+ });
409
+
410
+ /**
411
+ * Creates an IN list check (`left IN (v1, v2, ...)`).
412
+ *
413
+ * @param left - The operand to check.
414
+ * @param values - An array of values to check against.
415
+ * @returns An `InExpressionNode`.
416
+ *
417
+ * @example
418
+ * inList(users.status, ['active', 'pending']);
419
+ */
420
+ export const inList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
421
+ createInExpression('IN', left, values.map(v => toOperand(v)));
422
+
423
+ /**
424
+ * Creates a NOT IN list check (`left NOT IN (v1, v2, ...)`).
425
+ *
426
+ * @param left - The operand to check.
427
+ * @param values - An array of values to check against.
428
+ * @returns An `InExpressionNode`.
429
+ *
430
+ * @example
431
+ * notInList(users.id, [1, 2, 3]);
432
+ */
433
+ export const notInList = (left: OperandNode | ColumnRef, values: (string | number | LiteralNode)[]): InExpressionNode =>
434
+ createInExpression('NOT IN', left, values.map(v => toOperand(v)));
435
+
436
+ /**
437
+ * Creates an IN subquery check (`left IN (SELECT ...)`).
438
+ *
439
+ * @param left - The operand to check.
440
+ * @param subquery - The subquery to run.
441
+ * @returns An `InExpressionNode`.
442
+ *
443
+ * @example
444
+ * inSubquery(
445
+ * posts.authorId,
446
+ * selectFromEntity(User).select({ id: users.id }).where(eq(users.isActive, true))
447
+ * );
448
+ */
449
+ export const inSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
450
+ createInExpression('IN', left, toScalarSubqueryNode(subquery));
451
+
452
+ /**
453
+ * Creates a NOT IN subquery check (`left NOT IN (SELECT ...)`).
454
+ *
455
+ * @param left - The operand to check.
456
+ * @param subquery - The subquery to run.
457
+ * @returns An `InExpressionNode`.
458
+ *
459
+ * @example
460
+ * notInSubquery(
461
+ * users.id,
462
+ * selectFromEntity(Blacklist).select({ userId: blacklist.userId })
463
+ * );
464
+ */
465
+ export const notInSubquery = (left: OperandNode | ColumnRef, subquery: SelectQueryInput): InExpressionNode =>
466
+ createInExpression('NOT IN', left, toScalarSubqueryNode(subquery));
467
+
468
+ const createBetweenExpression = (
469
+ operator: 'BETWEEN' | 'NOT BETWEEN',
470
+ left: OperandNode | ColumnRef,
471
+ lower: OperandNode | ColumnRef | string | number,
472
+ upper: OperandNode | ColumnRef | string | number
473
+ ): BetweenExpressionNode => ({
474
+ type: 'BetweenExpression',
475
+ left: toOperandNode(left),
476
+ operator,
477
+ lower: toOperand(lower),
478
+ upper: toOperand(upper)
479
+ });
480
+
481
+ /**
482
+ * Creates a BETWEEN check (`left BETWEEN lower AND upper`).
483
+ *
484
+ * @param left - The operand to check.
485
+ * @param lower - The lower bound (inclusive).
486
+ * @param upper - The upper bound (inclusive).
487
+ * @returns A `BetweenExpressionNode`.
488
+ *
489
+ * @example
490
+ * between(products.price, 10, 100);
491
+ */
492
+ export const between = (
493
+ left: OperandNode | ColumnRef,
494
+ lower: OperandNode | ColumnRef | string | number,
495
+ upper: OperandNode | ColumnRef | string | number
496
+ ): BetweenExpressionNode => createBetweenExpression('BETWEEN', left, lower, upper);
497
+
498
+ /**
499
+ * Creates a NOT BETWEEN check (`left NOT BETWEEN lower AND upper`).
500
+ *
501
+ * @param left - The operand to check.
502
+ * @param lower - The lower bound (inclusive).
503
+ * @param upper - The upper bound (inclusive).
504
+ * @returns A `BetweenExpressionNode`.
505
+ *
506
+ * @example
507
+ * notBetween(users.age, 20, 30);
508
+ */
509
+ export const notBetween = (
510
+ left: OperandNode | ColumnRef,
511
+ lower: OperandNode | ColumnRef | string | number,
512
+ upper: OperandNode | ColumnRef | string | number
513
+ ): BetweenExpressionNode => createBetweenExpression('NOT BETWEEN', left, lower, upper);
514
+
515
+ const createArithmeticExpression = (
516
+ operator: '+' | '-' | '*' | '/',
517
+ left: OperandNode | ColumnRef,
518
+ right: OperandNode | ColumnRef | string | number
519
+ ): ArithmeticExpressionNode => ({
520
+ type: 'ArithmeticExpression',
521
+ left: toOperand(left),
522
+ operator,
523
+ right: toOperand(right)
524
+ });
525
+
526
+ /**
527
+ * Creates an addition expression (`left + right`).
528
+ *
529
+ * @param left - The left operand.
530
+ * @param right - The right operand.
531
+ * @returns An `ArithmeticExpressionNode`.
532
+ */
533
+ export const add = (
534
+ left: OperandNode | ColumnRef,
535
+ right: OperandNode | ColumnRef | string | number
536
+ ): ArithmeticExpressionNode => createArithmeticExpression('+', left, right);
537
+
538
+ /**
539
+ * Creates a subtraction expression (`left - right`).
540
+ *
541
+ * @param left - The left operand.
542
+ * @param right - The right operand.
543
+ * @returns An `ArithmeticExpressionNode`.
544
+ */
545
+ export const sub = (
546
+ left: OperandNode | ColumnRef,
547
+ right: OperandNode | ColumnRef | string | number
548
+ ): ArithmeticExpressionNode => createArithmeticExpression('-', left, right);
549
+
550
+ /**
551
+ * Creates a multiplication expression (`left * right`).
552
+ *
553
+ * @param left - The left operand.
554
+ * @param right - The right operand.
555
+ * @returns An `ArithmeticExpressionNode`.
556
+ */
557
+ export const mul = (
558
+ left: OperandNode | ColumnRef,
559
+ right: OperandNode | ColumnRef | string | number
560
+ ): ArithmeticExpressionNode => createArithmeticExpression('*', left, right);
561
+
562
+ /**
563
+ * Creates a division expression (`left / right`).
564
+ *
565
+ * @param left - The left operand.
566
+ * @param right - The right operand.
567
+ * @returns An `ArithmeticExpressionNode`.
568
+ */
569
+ export const div = (
570
+ left: OperandNode | ColumnRef,
571
+ right: OperandNode | ColumnRef | string | number
572
+ ): ArithmeticExpressionNode => createArithmeticExpression('/', left, right);
573
+
574
+ const createBitwiseExpression = (
575
+ operator: BitwiseOperator,
576
+ left: OperandNode | ColumnRef,
577
+ right: OperandNode | ColumnRef | string | number
578
+ ): BitwiseExpressionNode => ({
579
+ type: 'BitwiseExpression',
580
+ left: toOperand(left),
581
+ operator,
582
+ right: toOperand(right)
583
+ });
584
+
585
+ /**
586
+ * Creates a bitwise AND expression (`left & right`).
587
+ *
588
+ * @param left - The left operand.
589
+ * @param right - The right operand.
590
+ * @returns A `BitwiseExpressionNode`.
591
+ */
592
+ export const bitAnd = (
593
+ left: OperandNode | ColumnRef,
594
+ right: OperandNode | ColumnRef | string | number
595
+ ): BitwiseExpressionNode => createBitwiseExpression('&', left, right);
596
+
597
+ /**
598
+ * Creates a bitwise OR expression (`left | right`).
599
+ *
600
+ * @param left - The left operand.
601
+ * @param right - The right operand.
602
+ * @returns A `BitwiseExpressionNode`.
603
+ */
604
+ export const bitOr = (
605
+ left: OperandNode | ColumnRef,
606
+ right: OperandNode | ColumnRef | string | number
607
+ ): BitwiseExpressionNode => createBitwiseExpression('|', left, right);
608
+
609
+ /**
610
+ * Creates a bitwise XOR expression (`left ^ right`).
611
+ *
612
+ * @param left - The left operand.
613
+ * @param right - The right operand.
614
+ * @returns A `BitwiseExpressionNode`.
615
+ */
616
+ export const bitXor = (
617
+ left: OperandNode | ColumnRef,
618
+ right: OperandNode | ColumnRef | string | number
619
+ ): BitwiseExpressionNode => createBitwiseExpression('^', left, right);
620
+
621
+ /**
622
+ * Creates a bitwise left-shift expression (`left << right`).
623
+ *
624
+ * @param left - The left operand.
625
+ * @param right - The right operand (number of bits).
626
+ * @returns A `BitwiseExpressionNode`.
627
+ */
628
+ export const shiftLeft = (
629
+ left: OperandNode | ColumnRef,
630
+ right: OperandNode | ColumnRef | string | number
631
+ ): BitwiseExpressionNode => createBitwiseExpression('<<', left, right);
632
+
633
+ /**
634
+ * Creates a bitwise right-shift expression (`left >> right`).
635
+ *
636
+ * @param left - The left operand.
637
+ * @param right - The right operand (number of bits).
638
+ * @returns A `BitwiseExpressionNode`.
639
+ */
640
+ export const shiftRight = (
641
+ left: OperandNode | ColumnRef,
642
+ right: OperandNode | ColumnRef | string | number
643
+ ): BitwiseExpressionNode => createBitwiseExpression('>>', left, right);
644
+
645
+ /**
646
+ * Creates a JSON path extraction expression.
647
+ *
648
+ * @param col - The source column (should be a JSON/JSONB column).
649
+ * @param path - The JSON path expression (e.g., '$.address.city').
650
+ * @returns A `JsonPathNode`.
651
+ *
652
+ * @example
653
+ * jsonPath(users.profile, '$.settings.theme');
654
+ */
655
+ export const jsonPath = (col: ColumnRef | ColumnNode, path: string): JsonPathNode => ({
656
+ type: 'JsonPath',
657
+ column: columnOperand(col),
658
+ path
659
+ });
660
+
661
+ /**
662
+ * Creates a CASE expression (`CASE WHEN ... THEN ... ELSE ... END`).
663
+ *
664
+ * @param conditions - An array of `{ when, then }` objects.
665
+ * @param elseValue - Optional value for the `ELSE` clause.
666
+ * @returns A `CaseExpressionNode`.
667
+ *
668
+ * @example
669
+ * caseWhen([
670
+ * { when: gt(users.age, 65), then: 'Senior' },
671
+ * { when: gt(users.age, 18), then: 'Adult' }
672
+ * ], 'Minor');
673
+ */
674
+ export const caseWhen = (
675
+ conditions: { when: ExpressionNode; then: OperandNode | ColumnRef | string | number | boolean | null }[],
676
+ elseValue?: OperandNode | ColumnRef | string | number | boolean | null
677
+ ): CaseExpressionNode => ({
678
+ type: 'CaseExpression',
679
+ conditions: conditions.map(c => ({
680
+ when: c.when,
681
+ then: toOperand(c.then)
682
+ })),
683
+ else: elseValue !== undefined ? toOperand(elseValue) : undefined
684
+ });
685
+
686
+ /**
687
+ * Creates a CAST expression (`CAST(expression AS type)`).
688
+ *
689
+ * @param expression - The expression to cast.
690
+ * @param castType - The target SQL type (e.g., 'VARCHAR', 'INTEGER').
691
+ * @returns A `CastExpressionNode`.
692
+ *
693
+ * @example
694
+ * cast(users.age, 'VARCHAR');
695
+ */
696
+ export const cast = (
697
+ expression: OperandNode | ColumnRef | string | number | boolean | null,
698
+ castType: string
699
+ ): CastExpressionNode => ({
700
+ type: 'Cast',
701
+ expression: toOperand(expression),
702
+ castType
703
+ });
704
+
705
+ /**
706
+ * Creates an EXISTS check (`EXISTS (SELECT ...)`).
707
+ *
708
+ * @param subquery - The subquery to check.
709
+ * @returns An `ExistsExpressionNode`.
710
+ *
711
+ * @example
712
+ * exists(
713
+ * selectFromEntity(Order).where(eq(orders.userId, users.id))
714
+ * );
715
+ */
716
+ export const exists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
717
+ type: 'ExistsExpression',
718
+ operator: 'EXISTS',
719
+ subquery
720
+ });
721
+
722
+ /**
723
+ * Creates a NOT EXISTS check (`NOT EXISTS (SELECT ...)`).
724
+ *
725
+ * @param subquery - The subquery to check.
726
+ * @returns An `ExistsExpressionNode`.
727
+ *
728
+ * @example
729
+ * notExists(
730
+ * selectFromEntity(Subscription).where(eq(subscriptions.userId, users.id))
731
+ * );
732
+ */
733
+ export const notExists = (subquery: SelectQueryNode): ExistsExpressionNode => ({
734
+ type: 'ExistsExpression',
735
+ operator: 'NOT EXISTS',
736
+ subquery
737
+ });
738
+
739
+ /**
740
+ * Creates a COLLATE expression (`expression COLLATE collation`).
741
+ *
742
+ * @param expression - The expression string.
743
+ * @param collation - The collation name (e.g., 'nocase').
744
+ * @returns A `CollateExpressionNode`.
745
+ *
746
+ * @example
747
+ * collate(users.email, 'nocase');
748
+ */
749
+ export const collate = (
750
+ expression: OperandNode | ColumnRef | string | number | boolean | null,
751
+ collation: string
752
+ ): CollateExpressionNode => ({
753
+ type: 'Collate',
754
+ expression: toOperand(expression),
755
+ collation
756
+ });
757
+
758
+ /**
759
+ * Creates an `IS DISTINCT FROM` expression.
760
+ *
761
+ * Unlike `neq(a, b)`, this comparison is **null-safe**:
762
+ * returns `true` when values are different **including** when one
763
+ * (or both) is `NULL`. Never returns `NULL`.
764
+ *
765
+ * | left | right | neq | isDistinctFrom |
766
+ * |-------|-------|-------|----------------|
767
+ * | 1 | 2 | true | true |
768
+ * | 1 | 1 | false | false |
769
+ * | NULL | 1 | NULL | **true** |
770
+ * | NULL | NULL | NULL | **false** |
771
+ *
772
+ * Compilation by dialect:
773
+ * - PostgreSQL / SQLite / MSSQL → `left IS DISTINCT FROM right`
774
+ * - MySQL → `NOT (left <=> right)`
775
+ *
776
+ * @param left - The left operand.
777
+ * @param right - The right operand.
778
+ * @returns An `IsDistinctExpressionNode`.
779
+ *
780
+ * @example
781
+ * ```ts
782
+ * // Find users whose email has changed compared to backup
783
+ * where(isDistinctFrom(users.columns.email, backup.columns.email))
784
+ * ```
785
+ */
786
+ export const isDistinctFrom = (
787
+ left: OperandNode | ColumnRef,
788
+ right: OperandNode | ColumnRef | LiteralValue
789
+ ): IsDistinctExpressionNode => ({
790
+ type: 'IsDistinctExpression',
791
+ left: toOperandNode(left),
792
+ operator: 'IS DISTINCT FROM',
793
+ right: toOperand(right),
794
+ });
795
+
796
+ /**
797
+ * Creates an `IS NOT DISTINCT FROM` expression.
798
+ *
799
+ * The inverse of `isDistinctFrom`. Equivalent to a **null-safe** equality:
800
+ * returns `true` when values are equal **including** when both
801
+ * are `NULL`. Never returns `NULL`.
802
+ *
803
+ * | left | right | eq | isNotDistinctFrom |
804
+ * |-------|-------|-------|------------------|
805
+ * | 1 | 1 | true | true |
806
+ * | 1 | 2 | false | false |
807
+ * | NULL | 1 | NULL | **false** |
808
+ * | NULL | NULL | NULL | **true** |
809
+ *
810
+ * Compilation by dialect:
811
+ * - PostgreSQL / SQLite / MSSQL → `left IS NOT DISTINCT FROM right`
812
+ * - MySQL → `left <=> right`
813
+ *
814
+ * @param left - The left operand.
815
+ * @param right - The right operand.
816
+ * @returns An `IsDistinctExpressionNode`.
817
+ *
818
+ * @example
819
+ * ```ts
820
+ * // Rows where deletedAt is NULL or equal to a specific date
821
+ * where(isNotDistinctFrom(orders.columns.deletedAt, null))
822
+ * // → WHERE "orders"."deletedAt" IS NOT DISTINCT FROM NULL
823
+ * // equivalent to: WHERE deletedAt IS NULL (but works with any value)
824
+ * ```
825
+ */
826
+ export const isNotDistinctFrom = (
827
+ left: OperandNode | ColumnRef,
828
+ right: OperandNode | ColumnRef | LiteralValue
829
+ ): IsDistinctExpressionNode => ({
830
+ type: 'IsDistinctExpression',
831
+ left: toOperandNode(left),
832
+ operator: 'IS NOT DISTINCT FROM',
833
+ right: toOperand(right),
834
+ });