metal-orm 1.1.10 → 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 (192) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs +106 -43
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +5 -5
  5. package/dist/index.d.ts +5 -5
  6. package/dist/index.js +106 -43
  7. package/dist/index.js.map +1 -1
  8. package/package.json +100 -96
  9. package/scripts/generate-entities/render.mjs +21 -12
  10. package/scripts/generate-entities/schema.mjs +209 -195
  11. package/scripts/generate-entities/tree-detection.mjs +159 -153
  12. package/scripts/inflection/pt-br.mjs +468 -468
  13. package/scripts/run-eslint.mjs +33 -34
  14. package/scripts/show-sql-seed.sql +102 -0
  15. package/scripts/show-sql.mjs +321 -325
  16. package/src/bulk/bulk-delete-executor.ts +3 -5
  17. package/src/bulk/bulk-insert-executor.ts +7 -7
  18. package/src/bulk/bulk-update-executor.ts +2 -2
  19. package/src/bulk/bulk-upsert-executor.ts +5 -7
  20. package/src/codegen/naming-strategy.ts +54 -54
  21. package/src/codegen/typescript.ts +482 -482
  22. package/src/core/ast/adapters.ts +1 -1
  23. package/src/core/ast/builders.ts +123 -123
  24. package/src/core/ast/expression-builders.ts +745 -745
  25. package/src/core/ast/expression-nodes.ts +304 -304
  26. package/src/core/ast/expression-visitor.ts +212 -212
  27. package/src/core/ast/expression.ts +20 -20
  28. package/src/core/ast/helpers.ts +14 -14
  29. package/src/core/ast/join.ts +18 -18
  30. package/src/core/ast/query.ts +200 -200
  31. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  32. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  33. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  34. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  35. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  36. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  37. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  38. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  39. package/src/core/ddl/introspect/context.ts +15 -15
  40. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  41. package/src/core/ddl/introspect/mysql.ts +113 -36
  42. package/src/core/ddl/introspect/registry.ts +40 -40
  43. package/src/core/ddl/introspect/run-select.ts +35 -35
  44. package/src/core/ddl/introspect/utils.ts +56 -56
  45. package/src/core/ddl/naming-strategy.ts +16 -16
  46. package/src/core/ddl/schema-dialect.ts +56 -56
  47. package/src/core/ddl/schema-diff.ts +234 -234
  48. package/src/core/ddl/schema-generator.ts +1 -1
  49. package/src/core/ddl/schema-introspect.ts +25 -25
  50. package/src/core/ddl/sql-writing.ts +171 -171
  51. package/src/core/dialect/abstract.ts +541 -541
  52. package/src/core/dialect/base/cte-compiler.ts +33 -33
  53. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  54. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  55. package/src/core/dialect/base/join-compiler.ts +25 -25
  56. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  57. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  58. package/src/core/dialect/base/returning-strategy.ts +57 -57
  59. package/src/core/dialect/base/sql-dialect.ts +294 -294
  60. package/src/core/dialect/dialect-factory.ts +91 -91
  61. package/src/core/dialect/mssql/functions.ts +120 -120
  62. package/src/core/dialect/mssql/index.ts +317 -317
  63. package/src/core/dialect/mysql/functions.ts +103 -103
  64. package/src/core/dialect/mysql/index.ts +105 -105
  65. package/src/core/dialect/postgres/functions.ts +108 -108
  66. package/src/core/dialect/postgres/index.ts +91 -91
  67. package/src/core/dialect/sqlite/functions.ts +129 -129
  68. package/src/core/dialect/sqlite/index.ts +67 -67
  69. package/src/core/driver/database-driver.ts +19 -19
  70. package/src/core/driver/mssql-driver.ts +23 -23
  71. package/src/core/driver/mysql-driver.ts +23 -23
  72. package/src/core/driver/postgres-driver.ts +23 -23
  73. package/src/core/driver/sqlite-driver.ts +23 -23
  74. package/src/core/execution/db-executor.ts +92 -92
  75. package/src/core/execution/executors/better-sqlite3-executor.ts +94 -94
  76. package/src/core/execution/executors/mssql-executor.ts +184 -184
  77. package/src/core/execution/executors/mysql-executor.ts +88 -88
  78. package/src/core/execution/executors/postgres-executor.ts +26 -26
  79. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  80. package/src/core/execution/pooling/pool-types.ts +30 -30
  81. package/src/core/execution/pooling/pool.ts +275 -275
  82. package/src/core/functions/array.ts +35 -35
  83. package/src/core/functions/control-flow.ts +79 -79
  84. package/src/core/functions/datetime.ts +237 -237
  85. package/src/core/functions/json.ts +70 -70
  86. package/src/core/functions/numeric.ts +297 -297
  87. package/src/core/functions/text.ts +334 -334
  88. package/src/core/functions/types.ts +33 -33
  89. package/src/core/hydration/types.ts +51 -51
  90. package/src/core/sql/sql-operator-config.ts +39 -39
  91. package/src/core/sql/sql.ts +152 -152
  92. package/src/decorators/column-decorator.ts +87 -87
  93. package/src/decorators/decorator-metadata.ts +143 -143
  94. package/src/decorators/entity.ts +93 -93
  95. package/src/decorators/index.ts +39 -39
  96. package/src/decorators/relations.ts +231 -231
  97. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  98. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  99. package/src/decorators/transformers/transformer-executor.ts +171 -171
  100. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  101. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  102. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  103. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  104. package/src/decorators/validators/country-validator-registry.ts +64 -64
  105. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  106. package/src/decorators/validators/country-validators.ts +105 -105
  107. package/src/decorators/validators/validator-metadata.ts +59 -59
  108. package/src/dto/dto-types.ts +226 -226
  109. package/src/dto/filter-types.ts +141 -141
  110. package/src/dto/index.ts +95 -95
  111. package/src/dto/openapi/generators/base.ts +29 -29
  112. package/src/dto/openapi/generators/column.ts +37 -37
  113. package/src/dto/openapi/generators/dto.ts +91 -91
  114. package/src/dto/openapi/generators/filter.ts +75 -75
  115. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  116. package/src/dto/openapi/generators/pagination.ts +111 -111
  117. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  118. package/src/dto/openapi/index.ts +12 -12
  119. package/src/dto/openapi/types.ts +101 -101
  120. package/src/dto/openapi/utilities.ts +95 -95
  121. package/src/dto/pagination-utils.ts +150 -150
  122. package/src/dto/transform.ts +197 -197
  123. package/src/index.ts +86 -86
  124. package/src/orm/als.ts +58 -58
  125. package/src/orm/domain-event-bus.ts +119 -119
  126. package/src/orm/entity-context.ts +86 -86
  127. package/src/orm/entity-materializer.ts +159 -159
  128. package/src/orm/entity-meta.ts +97 -97
  129. package/src/orm/entity-registry.ts +39 -39
  130. package/src/orm/entity-relation-cache.ts +39 -39
  131. package/src/orm/execute.ts +194 -194
  132. package/src/orm/execution-context.ts +18 -18
  133. package/src/orm/hydration-context.ts +26 -26
  134. package/src/orm/hydration.ts +112 -112
  135. package/src/orm/identity-map.ts +61 -61
  136. package/src/orm/interceptor-pipeline.ts +16 -16
  137. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  138. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  139. package/src/orm/lazy-batch/has-many.ts +69 -69
  140. package/src/orm/lazy-batch/has-one.ts +68 -68
  141. package/src/orm/lazy-batch/shared.ts +125 -125
  142. package/src/orm/orm-session.ts +620 -620
  143. package/src/orm/orm.ts +34 -34
  144. package/src/orm/pooled-executor-factory.ts +121 -121
  145. package/src/orm/query-logger.ts +40 -40
  146. package/src/orm/relation-change-processor.ts +420 -420
  147. package/src/orm/relations/belongs-to.ts +143 -143
  148. package/src/orm/relations/has-many.ts +204 -204
  149. package/src/orm/relations/has-one.ts +174 -174
  150. package/src/orm/relations/many-to-many.ts +288 -288
  151. package/src/orm/relations/morph-many.ts +156 -156
  152. package/src/orm/relations/morph-one.ts +151 -151
  153. package/src/orm/relations/morph-to.ts +162 -162
  154. package/src/orm/runtime-types.ts +92 -92
  155. package/src/orm/save-graph.ts +561 -561
  156. package/src/orm/transaction-runner.ts +24 -24
  157. package/src/orm/unit-of-work.ts +426 -426
  158. package/src/query/index.ts +69 -69
  159. package/src/query/target.ts +46 -46
  160. package/src/query-builder/column-selector.ts +81 -81
  161. package/src/query-builder/delete.ts +141 -141
  162. package/src/query-builder/insert-query-state.ts +149 -149
  163. package/src/query-builder/insert.ts +117 -117
  164. package/src/query-builder/query-ast-service.ts +287 -287
  165. package/src/query-builder/query-resolution.ts +78 -78
  166. package/src/query-builder/raw-column-parser.ts +38 -38
  167. package/src/query-builder/relation-filter-utils.ts +153 -153
  168. package/src/query-builder/relation-join-planner.ts +10 -10
  169. package/src/query-builder/relation-manager.ts +87 -87
  170. package/src/query-builder/relation-projection-helper.ts +102 -102
  171. package/src/query-builder/relation-types.ts +9 -9
  172. package/src/query-builder/relation-utils.ts +15 -15
  173. package/src/query-builder/select/cte-facet.ts +40 -40
  174. package/src/query-builder/select/from-facet.ts +80 -80
  175. package/src/query-builder/select/join-facet.ts +62 -62
  176. package/src/query-builder/select/predicate-facet.ts +104 -104
  177. package/src/query-builder/select/projection-facet.ts +70 -70
  178. package/src/query-builder/select/relation-facet.ts +81 -81
  179. package/src/query-builder/select/setop-facet.ts +36 -36
  180. package/src/query-builder/select-helpers.ts +1 -1
  181. package/src/query-builder/select-query-builder-deps.ts +130 -130
  182. package/src/query-builder/select-query-state.ts +207 -207
  183. package/src/query-builder/update.ts +151 -151
  184. package/src/schema/column-types.ts +322 -322
  185. package/src/schema/table-guards.ts +37 -37
  186. package/src/schema/table.ts +281 -281
  187. package/src/tree/index.ts +13 -13
  188. package/src/tree/nested-set-strategy.ts +545 -545
  189. package/src/tree/tree-decorator.ts +397 -397
  190. package/src/tree/tree-manager.ts +754 -754
  191. package/src/tree/tree-query.ts +427 -427
  192. 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
+ });