ts-prorm-orm 1.1.0

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 (310) hide show
  1. package/CHANGELOG.md +1111 -0
  2. package/LICENSE +21 -0
  3. package/README.md +573 -0
  4. package/dist/audit/history-query.js +180 -0
  5. package/dist/audit/index.js +23 -0
  6. package/dist/audit/logger.js +236 -0
  7. package/dist/cache/cache-manager.js +84 -0
  8. package/dist/cache/index.js +16 -0
  9. package/dist/cache/redis-cluster-cache.js +557 -0
  10. package/dist/cli.js +2200 -0
  11. package/dist/compliance/audit-trail.js +68 -0
  12. package/dist/compliance/backup-verification.js +685 -0
  13. package/dist/compliance/breach-detector.js +505 -0
  14. package/dist/compliance/consent-record.js +227 -0
  15. package/dist/compliance/consent-versioning.js +331 -0
  16. package/dist/compliance/cross-border-log.js +530 -0
  17. package/dist/compliance/data-classifier.js +258 -0
  18. package/dist/compliance/data-lineage.js +565 -0
  19. package/dist/compliance/data-masker.js +303 -0
  20. package/dist/compliance/data-portability.js +265 -0
  21. package/dist/compliance/data-retention.js +195 -0
  22. package/dist/compliance/dsar-workflow.js +342 -0
  23. package/dist/compliance/field-encryption.js +223 -0
  24. package/dist/compliance/immutable-record.js +148 -0
  25. package/dist/compliance/index.js +260 -0
  26. package/dist/compliance/privacy-impact-assessment.js +364 -0
  27. package/dist/compliance/pseudonymization.js +269 -0
  28. package/dist/compliance/query-firewall.js +1182 -0
  29. package/dist/compliance/rate-limiter.js +580 -0
  30. package/dist/compliance/right-to-erasure.js +117 -0
  31. package/dist/compliance/row-level-security.js +246 -0
  32. package/dist/compliance/security-decorator.js +574 -0
  33. package/dist/compliance/security-monitor.js +525 -0
  34. package/dist/compliance/sensitive-data-discovery.js +476 -0
  35. package/dist/compliance/session-isolation.js +472 -0
  36. package/dist/compliance/tls-enforcer.js +108 -0
  37. package/dist/compliance/worm-storage.js +712 -0
  38. package/dist/connection-manager.js +198 -0
  39. package/dist/connection-pool.js +519 -0
  40. package/dist/decorators/audit.js +136 -0
  41. package/dist/decorators/belongs-to-many.js +115 -0
  42. package/dist/decorators/belongs-to.js +115 -0
  43. package/dist/decorators/check.js +435 -0
  44. package/dist/decorators/collate.js +329 -0
  45. package/dist/decorators/comment.js +205 -0
  46. package/dist/decorators/database-settings.js +236 -0
  47. package/dist/decorators/default.js +244 -0
  48. package/dist/decorators/encryption.js +235 -0
  49. package/dist/decorators/engine.js +97 -0
  50. package/dist/decorators/fk-constraints.js +594 -0
  51. package/dist/decorators/foreign-table.js +136 -0
  52. package/dist/decorators/generated.js +274 -0
  53. package/dist/decorators/has-many.js +127 -0
  54. package/dist/decorators/has-one.js +116 -0
  55. package/dist/decorators/hstore.js +129 -0
  56. package/dist/decorators/index.js +355 -0
  57. package/dist/decorators/json-column.js +83 -0
  58. package/dist/decorators/jsonb.js +101 -0
  59. package/dist/decorators/orm-decorators.js +425 -0
  60. package/dist/decorators/permissions.js +294 -0
  61. package/dist/decorators/procedure.js +210 -0
  62. package/dist/decorators/query-options.js +556 -0
  63. package/dist/decorators/range.js +167 -0
  64. package/dist/decorators/set-column.js +83 -0
  65. package/dist/decorators/spatial.js +114 -0
  66. package/dist/decorators/storage.js +580 -0
  67. package/dist/decorators/timezone.js +512 -0
  68. package/dist/decorators/trigger.js +90 -0
  69. package/dist/decorators/uuid.js +135 -0
  70. package/dist/decorators/view.js +258 -0
  71. package/dist/diagrams/chen-diagram.js +354 -0
  72. package/dist/diagrams/class-diagram.js +384 -0
  73. package/dist/diagrams/dependency-diagram.js +432 -0
  74. package/dist/diagrams/er-diagram.js +605 -0
  75. package/dist/diagrams/flow-diagram.js +394 -0
  76. package/dist/diagrams/gantt-diagram.js +411 -0
  77. package/dist/diagrams/index-diagram.js +353 -0
  78. package/dist/diagrams/index.js +184 -0
  79. package/dist/diagrams/migration-diagram.js +316 -0
  80. package/dist/diagrams/model-diagram.js +616 -0
  81. package/dist/diagrams/package-diagram.js +376 -0
  82. package/dist/diagrams/palette.js +85 -0
  83. package/dist/diagrams/relational-diagram.js +455 -0
  84. package/dist/diagrams/schemadoc-diagram.js +309 -0
  85. package/dist/diagrams/sequence-diagram.js +307 -0
  86. package/dist/diagrams/state-diagram.js +344 -0
  87. package/dist/diagrams/svg-dom.js +111 -0
  88. package/dist/diagrams/tree-diagram.js +250 -0
  89. package/dist/dialects/clickhouse/index.js +1742 -0
  90. package/dist/dialects/cockroachdb/index.js +4677 -0
  91. package/dist/dialects/cratedb/index.js +441 -0
  92. package/dist/dialects/databricks/index.js +517 -0
  93. package/dist/dialects/db2/index.js +2225 -0
  94. package/dist/dialects/dialect.js +720 -0
  95. package/dist/dialects/duckdb/index.js +2014 -0
  96. package/dist/dialects/exasol/index.js +357 -0
  97. package/dist/dialects/firebird/index.js +495 -0
  98. package/dist/dialects/greenplum/index.js +457 -0
  99. package/dist/dialects/hana/index.js +1741 -0
  100. package/dist/dialects/mariadb/index.js +3590 -0
  101. package/dist/dialects/mssql/index.js +2296 -0
  102. package/dist/dialects/mysql/index.js +4058 -0
  103. package/dist/dialects/oracle/index.js +2999 -0
  104. package/dist/dialects/postgres/index.js +5319 -0
  105. package/dist/dialects/query-stream-helper.js +152 -0
  106. package/dist/dialects/questdb/index.js +433 -0
  107. package/dist/dialects/redshift/index.js +2277 -0
  108. package/dist/dialects/singlestore/index.js +354 -0
  109. package/dist/dialects/snowflake/index.js +2082 -0
  110. package/dist/dialects/spanner/index.js +1768 -0
  111. package/dist/dialects/sqlite/index.js +3382 -0
  112. package/dist/dialects/tidb/index.js +377 -0
  113. package/dist/dialects/timescaledb/index.js +164 -0
  114. package/dist/dialects/trino/index.js +420 -0
  115. package/dist/dialects/turso/index.js +227 -0
  116. package/dist/dialects/vertica/index.js +317 -0
  117. package/dist/dialects/yugabytedb/index.js +424 -0
  118. package/dist/errors/index.js +472 -0
  119. package/dist/errors/utils.js +340 -0
  120. package/dist/errors.js +21 -0
  121. package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
  122. package/dist/extensions/catalog/mssql-features.js +147 -0
  123. package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
  124. package/dist/extensions/catalog/oracle-db2-features.js +196 -0
  125. package/dist/extensions/catalog/postgres-extensions.js +516 -0
  126. package/dist/extensions/index.js +71 -0
  127. package/dist/extensions/types.js +13 -0
  128. package/dist/foreign-data.js +173 -0
  129. package/dist/hooks/hooks-manager.js +350 -0
  130. package/dist/hooks/index.js +37 -0
  131. package/dist/index.js +463 -0
  132. package/dist/logging.js +335 -0
  133. package/dist/migrations/index.js +40 -0
  134. package/dist/migrations/migration.js +196 -0
  135. package/dist/migrations/migrator.js +411 -0
  136. package/dist/migrations/prormmigration.js +275 -0
  137. package/dist/migrations/query-interface.js +435 -0
  138. package/dist/migrations/seeder.js +353 -0
  139. package/dist/models/associations.js +852 -0
  140. package/dist/models/constraints.js +288 -0
  141. package/dist/models/data-types.js +2518 -0
  142. package/dist/models/decorators.js +445 -0
  143. package/dist/models/index.js +33 -0
  144. package/dist/models/indexes.js +531 -0
  145. package/dist/models/methods.js +382 -0
  146. package/dist/models/model-manager.js +103 -0
  147. package/dist/models/model.js +5349 -0
  148. package/dist/models/operators.js +67 -0
  149. package/dist/models/scopes.js +189 -0
  150. package/dist/models/typescript-types.js +26 -0
  151. package/dist/nosql/aerospike/index.js +205 -0
  152. package/dist/nosql/allegrograph/index.js +169 -0
  153. package/dist/nosql/arangodb/index.js +364 -0
  154. package/dist/nosql/azure-blob/index.js +206 -0
  155. package/dist/nosql/beanstalkd/index.js +231 -0
  156. package/dist/nosql/beequeue/index.js +210 -0
  157. package/dist/nosql/bigchaindb/index.js +195 -0
  158. package/dist/nosql/bigtable/index.js +224 -0
  159. package/dist/nosql/blazegraph/index.js +173 -0
  160. package/dist/nosql/bullmq/index.js +191 -0
  161. package/dist/nosql/cassandra/index.js +174 -0
  162. package/dist/nosql/chroma/index.js +190 -0
  163. package/dist/nosql/cloudflare-kv/index.js +220 -0
  164. package/dist/nosql/coherence/index.js +200 -0
  165. package/dist/nosql/cosmosdb/index.js +157 -0
  166. package/dist/nosql/couchbase/index.js +213 -0
  167. package/dist/nosql/dax/index.js +212 -0
  168. package/dist/nosql/deno-kv/index.js +206 -0
  169. package/dist/nosql/dgraph/index.js +171 -0
  170. package/dist/nosql/doris/index.js +169 -0
  171. package/dist/nosql/druid/index.js +162 -0
  172. package/dist/nosql/dynamodb/index.js +937 -0
  173. package/dist/nosql/elasticsearch/index.js +377 -0
  174. package/dist/nosql/etcd/index.js +502 -0
  175. package/dist/nosql/eventhubs/index.js +213 -0
  176. package/dist/nosql/eventstore/index.js +254 -0
  177. package/dist/nosql/faunadb/index.js +188 -0
  178. package/dist/nosql/firestore/index.js +177 -0
  179. package/dist/nosql/fluree/index.js +148 -0
  180. package/dist/nosql/fuseki/index.js +170 -0
  181. package/dist/nosql/gcs/index.js +172 -0
  182. package/dist/nosql/gearman/index.js +160 -0
  183. package/dist/nosql/geode/index.js +196 -0
  184. package/dist/nosql/graphdb/index.js +169 -0
  185. package/dist/nosql/graylog/index.js +188 -0
  186. package/dist/nosql/gridgain/index.js +171 -0
  187. package/dist/nosql/hazelcast/index.js +162 -0
  188. package/dist/nosql/hbase/index.js +230 -0
  189. package/dist/nosql/ignite/index.js +173 -0
  190. package/dist/nosql/immudb/index.js +184 -0
  191. package/dist/nosql/index.js +232 -0
  192. package/dist/nosql/infinispan/index.js +200 -0
  193. package/dist/nosql/influxdb/index.js +0 -0
  194. package/dist/nosql/kafka/index.js +234 -0
  195. package/dist/nosql/keyspaces/index.js +189 -0
  196. package/dist/nosql/kinesis/index.js +253 -0
  197. package/dist/nosql/leveldb/index.js +153 -0
  198. package/dist/nosql/lmdb/index.js +160 -0
  199. package/dist/nosql/loki/index.js +201 -0
  200. package/dist/nosql/marklogic/index.js +204 -0
  201. package/dist/nosql/materialize/index.js +145 -0
  202. package/dist/nosql/meilisearch/index.js +154 -0
  203. package/dist/nosql/memcached/index.js +223 -0
  204. package/dist/nosql/milvus/index.js +410 -0
  205. package/dist/nosql/minio/index.js +265 -0
  206. package/dist/nosql/momento/index.js +179 -0
  207. package/dist/nosql/mongodb/index.js +461 -0
  208. package/dist/nosql/nats/index.js +247 -0
  209. package/dist/nosql/nedb/index.js +164 -0
  210. package/dist/nosql/neo4j/index.js +450 -0
  211. package/dist/nosql/neptune/index.js +470 -0
  212. package/dist/nosql/nsq/index.js +200 -0
  213. package/dist/nosql/opensearch/index.js +186 -0
  214. package/dist/nosql/orientdb/index.js +175 -0
  215. package/dist/nosql/papertrail/index.js +200 -0
  216. package/dist/nosql/pinecone/index.js +0 -0
  217. package/dist/nosql/pinot/index.js +133 -0
  218. package/dist/nosql/pouchdb/index.js +172 -0
  219. package/dist/nosql/prometheus/index.js +174 -0
  220. package/dist/nosql/provendb/index.js +147 -0
  221. package/dist/nosql/pubsub/index.js +187 -0
  222. package/dist/nosql/pulsar/index.js +232 -0
  223. package/dist/nosql/qdrant/index.js +295 -0
  224. package/dist/nosql/qldb/index.js +192 -0
  225. package/dist/nosql/r2/index.js +281 -0
  226. package/dist/nosql/rabbitmq/index.js +237 -0
  227. package/dist/nosql/ravendb/index.js +175 -0
  228. package/dist/nosql/redis/index.js +607 -0
  229. package/dist/nosql/redpanda/index.js +237 -0
  230. package/dist/nosql/resque/index.js +203 -0
  231. package/dist/nosql/rethinkdb/index.js +232 -0
  232. package/dist/nosql/rocksdb/index.js +152 -0
  233. package/dist/nosql/rockset/index.js +126 -0
  234. package/dist/nosql/s3/index.js +298 -0
  235. package/dist/nosql/scylladb/index.js +178 -0
  236. package/dist/nosql/signoz/index.js +227 -0
  237. package/dist/nosql/sns/index.js +201 -0
  238. package/dist/nosql/solr/index.js +231 -0
  239. package/dist/nosql/splunk/index.js +227 -0
  240. package/dist/nosql/sqs/index.js +246 -0
  241. package/dist/nosql/stardog/index.js +169 -0
  242. package/dist/nosql/starrocks/index.js +170 -0
  243. package/dist/nosql/store.js +2 -0
  244. package/dist/nosql/sumologic/index.js +213 -0
  245. package/dist/nosql/surrealdb/index.js +179 -0
  246. package/dist/nosql/terminusdb/index.js +151 -0
  247. package/dist/nosql/tigergraph/index.js +469 -0
  248. package/dist/nosql/typesense/index.js +148 -0
  249. package/dist/nosql/unqlite/index.js +157 -0
  250. package/dist/nosql/upstash/index.js +167 -0
  251. package/dist/nosql/vercel-kv/index.js +166 -0
  252. package/dist/nosql/victoriametrics/index.js +213 -0
  253. package/dist/nosql/virtuoso/index.js +169 -0
  254. package/dist/nosql/weaviate/index.js +276 -0
  255. package/dist/operators/index.js +108 -0
  256. package/dist/operators.js +2690 -0
  257. package/dist/prisma-migrate/index.js +38 -0
  258. package/dist/prisma-migrate/migration-generator.js +125 -0
  259. package/dist/prisma-migrate/model-generator.js +172 -0
  260. package/dist/prisma-migrate/relations.js +100 -0
  261. package/dist/prisma-migrate/schema-parser.js +167 -0
  262. package/dist/prisma-migrate/type-mapper.js +40 -0
  263. package/dist/prorm.js +6832 -0
  264. package/dist/query-builders/cte-builder.js +80 -0
  265. package/dist/query-builders/functions/aggregate.js +390 -0
  266. package/dist/query-builders/functions/conditional.js +503 -0
  267. package/dist/query-builders/functions/datetime.js +695 -0
  268. package/dist/query-builders/functions/fulltext.js +439 -0
  269. package/dist/query-builders/functions/index.js +93 -0
  270. package/dist/query-builders/functions/json.js +427 -0
  271. package/dist/query-builders/functions/math.js +399 -0
  272. package/dist/query-builders/functions/string.js +518 -0
  273. package/dist/query-builders/functions/window.js +328 -0
  274. package/dist/query-builders/include-builder.js +323 -0
  275. package/dist/query-builders/index-expression-builder.js +242 -0
  276. package/dist/query-builders/index.js +161 -0
  277. package/dist/query-builders/insert-builder.js +164 -0
  278. package/dist/query-builders/model-helpers.js +38 -0
  279. package/dist/query-builders/order-limit-builder.js +239 -0
  280. package/dist/query-builders/sql-compiler.js +2040 -0
  281. package/dist/query-builders/subquery-builder.js +152 -0
  282. package/dist/query-builders/update-builder.js +182 -0
  283. package/dist/query-builders/view-builder.js +218 -0
  284. package/dist/query-builders/where-builder.js +1532 -0
  285. package/dist/query-interface.js +622 -0
  286. package/dist/query-optimizers/batch-optimizer.js +426 -0
  287. package/dist/query-optimizers/explain-plans.js +415 -0
  288. package/dist/query-optimizers/index.js +51 -0
  289. package/dist/query-optimizers/prepared-statement-cache.js +423 -0
  290. package/dist/query-optimizers/query-hints.js +438 -0
  291. package/dist/query-optimizers/query-optimizer.js +278 -0
  292. package/dist/query-optimizers/slow-query-logger.js +271 -0
  293. package/dist/replica-manager.js +507 -0
  294. package/dist/schema/index.js +15 -0
  295. package/dist/schema/migration-generator.js +374 -0
  296. package/dist/schema/schema-differ.js +549 -0
  297. package/dist/schema/types.js +6 -0
  298. package/dist/sql-constants.js +300 -0
  299. package/dist/sqlite-advanced.js +1047 -0
  300. package/dist/streams/index.js +12 -0
  301. package/dist/streams/transforms.js +178 -0
  302. package/dist/transaction.js +456 -0
  303. package/dist/types/index.js +180 -0
  304. package/dist/user-management.js +127 -0
  305. package/dist/utils/date.js +300 -0
  306. package/dist/utils/index.js +1079 -0
  307. package/dist/utils/string.js +161 -0
  308. package/dist/validators/index.js +19 -0
  309. package/dist/validators/validator.js +911 -0
  310. package/package.json +190 -0
@@ -0,0 +1,2690 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Cast = exports.SortDirection = exports.Operators = exports.Op = void 0;
4
+ exports.isColumnReference = isColumnReference;
5
+ exports.isColOperator = isColOperator;
6
+ exports.isOperator = isOperator;
7
+ exports.getOperatorString = getOperatorString;
8
+ exports.operatorToWhereKey = operatorToWhereKey;
9
+ exports.where = where;
10
+ exports.and = and;
11
+ exports.or = or;
12
+ exports.not = not;
13
+ exports.eq = eq;
14
+ exports.ne = ne;
15
+ exports.gt = gt;
16
+ exports.gte = gte;
17
+ exports.lt = lt;
18
+ exports.lte = lte;
19
+ exports.like = like;
20
+ exports.notLike = notLike;
21
+ exports.inOp = inOp;
22
+ exports.in = inOp;
23
+ exports.notIn = notIn;
24
+ exports.between = between;
25
+ exports.notBetween = notBetween;
26
+ exports.isNull = isNull;
27
+ exports.isNotNull = isNotNull;
28
+ exports.notExists = notExists;
29
+ exports.exists = exists;
30
+ exports.notInSubquery = notInSubquery;
31
+ exports.inSubquery = inSubquery;
32
+ exports.antiJoin = antiJoin;
33
+ exports.exclude = exclude;
34
+ exports.iLike = iLike;
35
+ exports.notILike = notILike;
36
+ exports.regexp = regexp;
37
+ exports.notRegexp = notRegexp;
38
+ exports.iRegexp = iRegexp;
39
+ exports.notIRegexp = notIRegexp;
40
+ exports.contains = contains;
41
+ exports.overlap = overlap;
42
+ exports.arrayContains = arrayContains;
43
+ exports.arrayContainedBy = arrayContainedBy;
44
+ exports.arrayOverlaps = arrayOverlaps;
45
+ exports.arrayAny = arrayAny;
46
+ exports.arrayAll = arrayAll;
47
+ exports.startsWith = startsWith;
48
+ exports.notStartsWith = notStartsWith;
49
+ exports.endsWith = endsWith;
50
+ exports.notEndsWith = notEndsWith;
51
+ exports.substring = substring;
52
+ exports.notSubstring = notSubstring;
53
+ exports.anyOp = anyOp;
54
+ exports.any = anyOp;
55
+ exports.allOp = allOp;
56
+ exports.all = allOp;
57
+ exports.match = match;
58
+ exports.isOrderExpression = isOrderExpression;
59
+ exports.asc = asc;
60
+ exports.desc = desc;
61
+ exports.random = random;
62
+ exports.matchAgainst = matchAgainst;
63
+ exports.matchFulltext = matchFulltext;
64
+ exports.toTsvector = toTsvector;
65
+ exports.toTsquery = toTsquery;
66
+ exports.plainToTsquery = plainToTsquery;
67
+ exports.phraseToTsquery = phraseToTsquery;
68
+ exports.contained = contained;
69
+ exports.jsonKey = jsonKey;
70
+ exports.json = json;
71
+ exports.jsonContains = jsonContains;
72
+ exports.jsonHasKey = jsonHasKey;
73
+ exports.jsonConcat = jsonConcat;
74
+ exports.jsonDelete = jsonDelete;
75
+ exports.jsonDeletePath = jsonDeletePath;
76
+ exports.jsonPathExists = jsonPathExists;
77
+ exports.jsonPathQuery = jsonPathQuery;
78
+ exports.fn = fn;
79
+ exports.col = col;
80
+ exports.literal = literal;
81
+ exports.literalExpr = literalExpr;
82
+ exports.isFnExpression = isFnExpression;
83
+ exports.isColExpression = isColExpression;
84
+ exports.isLiteralExpression = isLiteralExpression;
85
+ exports.isRawLiteral = isRawLiteral;
86
+ exports.isCastExpression = isCastExpression;
87
+ exports.isExtractExpression = isExtractExpression;
88
+ exports.isConvExpression = isConvExpression;
89
+ exports.cast = cast;
90
+ exports.extract = extract;
91
+ exports.conv = conv;
92
+ exports.SUM = SUM;
93
+ exports.AVG = AVG;
94
+ exports.COUNT = COUNT;
95
+ exports.MAX = MAX;
96
+ exports.MIN = MIN;
97
+ /**
98
+ * Check if a value is a column reference
99
+ */
100
+ function isColumnReference(value) {
101
+ return (value !== null && typeof value === 'object' && '$col' in value && typeof value.$col === 'string');
102
+ }
103
+ // Symbol-based operators for type safety
104
+ exports.Op = {
105
+ // Logical operators
106
+ and: Symbol('and'),
107
+ or: Symbol('or'),
108
+ not: Symbol('not'),
109
+ // Comparison operators
110
+ eq: Symbol('eq'),
111
+ is: Symbol('is'),
112
+ ne: Symbol('ne'),
113
+ gt: Symbol('gt'),
114
+ gte: Symbol('gte'),
115
+ lt: Symbol('lt'),
116
+ lte: Symbol('lte'),
117
+ // String/LIKE operators as Symbols (standard Prorm API)
118
+ like: Symbol('like'),
119
+ notLike: Symbol('notLike'),
120
+ iLike: Symbol('iLike'),
121
+ notILike: Symbol('notILike'),
122
+ startsWith: Symbol('startsWith'),
123
+ notStartsWith: Symbol('notStartsWith'),
124
+ endsWith: Symbol('endsWith'),
125
+ notEndsWith: Symbol('notEndsWith'),
126
+ substring: Symbol('substring'),
127
+ notSubstring: Symbol('notSubstring'),
128
+ // Column reference as Symbol
129
+ col: Symbol('col'),
130
+ in: Symbol('in'),
131
+ notIn: Symbol('notIn'),
132
+ between: Symbol('between'),
133
+ notBetween: Symbol('notBetween'),
134
+ isNull: Symbol('isNull'),
135
+ isNotNull: Symbol('isNotNull'),
136
+ exists: Symbol('exists'),
137
+ notExists: Symbol('notExists'),
138
+ // Additional operators
139
+ regexp: Symbol('regexp'),
140
+ notRegexp: Symbol('notRegexp'),
141
+ iRegexp: Symbol('iRegexp'),
142
+ notIRegexp: Symbol('notIRegexp'),
143
+ any: Symbol('any'),
144
+ all: Symbol('all'),
145
+ containsKey: Symbol('containsKey'),
146
+ containsKeyPath: Symbol('containsKeyPath'),
147
+ containsPath: Symbol('containsPath'),
148
+ strictLeft: Symbol('strictLeft'),
149
+ strictRight: Symbol('strictRight'),
150
+ noExtendRight: Symbol('noExtendRight'),
151
+ noExtendLeft: Symbol('noExtendLeft'),
152
+ adj: Symbol('adj'),
153
+ notAdj: Symbol('notAdj'),
154
+ // PostgreSQL Array operators
155
+ /**
156
+ * Array contains - checks if an array contains all specified elements
157
+ * PostgreSQL: @>
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * // Find products where tags contains both 'electronics' and 'sale'
162
+ * Product.findAll({ where: { tags: { [Op.arrayContains]: ['electronics', 'sale'] } } })
163
+ * // SQL: WHERE tags @> ARRAY['electronics', 'sale']
164
+ * ```
165
+ */
166
+ arrayContains: Symbol('arrayContains'),
167
+ /**
168
+ * Array contained by - checks if an array is contained by the specified elements
169
+ * PostgreSQL: <@
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * // Find products where tags is contained by ['electronics', 'sale', 'new']
174
+ * Product.findAll({ where: { tags: { [Op.arrayContainedBy]: ['electronics', 'sale'] } } })
175
+ * // SQL: WHERE tags <@ ARRAY['electronics', 'sale']
176
+ * ```
177
+ */
178
+ arrayContainedBy: Symbol('arrayContainedBy'),
179
+ /**
180
+ * Array overlaps - checks if arrays have common elements
181
+ * PostgreSQL: &&
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * // Find products where tags overlaps with ['electronics', 'sale']
186
+ * Product.findAll({ where: { tags: { [Op.arrayOverlaps]: ['electronics', 'sale'] } } })
187
+ * // SQL: WHERE tags && ARRAY['electronics', 'sale']
188
+ * ```
189
+ */
190
+ arrayOverlaps: Symbol('arrayOverlaps'),
191
+ /**
192
+ * Array ANY - checks if any element matches the condition
193
+ * PostgreSQL: ANY
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * // Find products where any tag equals 'electronics'
198
+ * Product.findAll({ where: { tags: { [Op.arrayAny]: 'electronics' } } })
199
+ * // SQL: WHERE 'electronics' = ANY(tags)
200
+ * ```
201
+ */
202
+ arrayAny: Symbol('arrayAny'),
203
+ /**
204
+ * Array ALL - checks if all elements match the condition
205
+ * PostgreSQL: ALL
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * // Find products where all prices are greater than 100
210
+ * Product.findAll({ where: { prices: { [Op.arrayAll]: { [Op.gt]: 100 } } } })
211
+ * // SQL: WHERE 100 > ALL(prices)
212
+ * ```
213
+ */
214
+ arrayAll: Symbol('arrayAll'),
215
+ // Search/LIKE operators for pattern matching
216
+ /**
217
+ * Starts with - checks if a string starts with the given value
218
+ * SQL: LIKE 'value%'
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * // Find users whose name starts with 'John'
223
+ * User.findAll({ where: { name: { [Op.startsWith]: 'John' } } })
224
+ * // SQL: WHERE name LIKE 'John%'
225
+ * ```
226
+ */
227
+ // Note: startsWith, notStartsWith, endsWith, notEndsWith, substring, notSubstring
228
+ // are now methods that return clause objects, not symbols
229
+ /**
230
+ * Ends with - checks if a string ends with the given value
231
+ * SQL: LIKE '%value'
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * // Find users whose email ends with '@gmail.com'
236
+ * User.findAll({ where: { email: { [Op.endsWith]: '@gmail.com' } } })
237
+ * // SQL: WHERE email LIKE '%@gmail.com'
238
+ * ```
239
+ */
240
+ // Note: endsWith, notEndsWith, substring, notSubstring are now methods
241
+ // JSON operators for JSON/JSONB columns
242
+ /**
243
+ * JSON contains - checks if JSON document contains the specified value
244
+ * PostgreSQL: @>
245
+ * MySQL: JSON_CONTAINS
246
+ * SQLite: JSON_EXTRACT
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * // Find users where preferences contains { theme: 'dark' }
251
+ * User.findAll({ where: { preferences: { [Op.contains]: { theme: 'dark' } } } })
252
+ * ```
253
+ */
254
+ contains: Symbol('contains'),
255
+ /**
256
+ * JSON is contained in - checks if the value is contained in the JSON column
257
+ * PostgreSQL: <@
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * // Find users where settings is contained in the specified JSON
262
+ * User.findAll({ where: { settings: { [Op.containedBy]: { theme: 'dark', lang: 'en' } } } })
263
+ * ```
264
+ */
265
+ containedBy: Symbol('containedBy'),
266
+ /**
267
+ * JSON key exists - checks if a key (or array index) exists in a JSON object/array
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * // Find users where 'role' key exists in data column
272
+ * User.findAll({ where: { data: { [Op.keyExists]: 'role' } } })
273
+ *
274
+ * // Find users where array index 0 exists
275
+ * User.findAll({ where: { tags: { [Op.keyExists]: '0' } } })
276
+ * ```
277
+ */
278
+ keyExists: Symbol('keyExists'),
279
+ /**
280
+ * JSON overlap - checks if JSON arrays overlap (have common elements)
281
+ * PostgreSQL: &&
282
+ *
283
+ * @example
284
+ * ```typescript
285
+ * // Find users where tags overlaps with ['admin', 'vip']
286
+ * User.findAll({ where: { tags: { [Op.overlap]: ['admin', 'vip'] } } })
287
+ * ```
288
+ */
289
+ overlap: Symbol('overlap'),
290
+ /**
291
+ * JSON key access - access a specific key in a JSON column
292
+ * PostgreSQL: ->> for text, -> for object
293
+ * MySQL: JSON_EXTRACT
294
+ * SQLite: JSON_EXTRACT
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * // Find users where data->>'name' equals 'John'
299
+ * User.findAll({ where: { data: { [Op.key]: 'name' } } })
300
+ * // For nested: { field: { [Op.key]: 'profile.name' } }
301
+ * ```
302
+ */
303
+ // Note: key is now a method, not a symbol
304
+ /**
305
+ * Column reference - reference another column in a WHERE clause
306
+ * Used for comparing one column to another
307
+ *
308
+ * @example
309
+ * ```typescript
310
+ * // Find orders where quantity equals available stock
311
+ * Order.findAll({ where: { quantity: { [Op.col]: 'available_stock' } } })
312
+ * ```
313
+ */
314
+ // Note: col is now a method, not a symbol
315
+ // Full-text search operators (use methods instead of symbols for query building)
316
+ match: Symbol('match'),
317
+ // Legacy JSON operator
318
+ $json: Symbol('json'),
319
+ /**
320
+ * JSON path explicit operator - explicit JSON path with field and path separated
321
+ * Convenience method combining field and path in one call
322
+ *
323
+ * @example
324
+ * ```typescript
325
+ * // Query with field and path separated
326
+ * User.findAll({
327
+ * where: {
328
+ * data: { [Op.jsonPath('settings', 'theme')]: 'dark' }
329
+ * }
330
+ * })
331
+ * // SQLite/MySQL: WHERE json_extract(data, '$.settings.theme') = 'dark'
332
+ * // PostgreSQL: WHERE data->>'settings'->>'theme' = 'dark'
333
+ *
334
+ * // Check if path exists (no value comparison)
335
+ * User.findAll({
336
+ * where: {
337
+ * data: { [Op.jsonPath('settings', 'theme')]: { [Op.ne]: null } }
338
+ * }
339
+ * })
340
+ * ```
341
+ */
342
+ jsonPath: Symbol('jsonPath'),
343
+ /**
344
+ * JSON contains - checks if a JSON column contains a specific value or object
345
+ * PostgreSQL: @> (contains), MySQL: JSON_CONTAINS, SQLite: json_each
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * // Find users where preferences contains { theme: 'dark' }
350
+ * User.findAll({ where: { preferences: { [Op.jsonContains]: { theme: 'dark' } } } })
351
+ * // SQL: WHERE preferences @> '{"theme":"dark"}' (PostgreSQL)
352
+ * // SQL: WHERE JSON_CONTAINS(preferences, '{"theme":"dark"}') (MySQL)
353
+ * ```
354
+ */
355
+ jsonContains: Symbol('jsonContains'),
356
+ /**
357
+ * JSON has key - checks if a JSON column has a specific key
358
+ * PostgreSQL: ? (jsonb_exists), MySQL: JSON_CONTAINS_PATH, SQLite: json_each
359
+ *
360
+ * @example
361
+ * ```typescript
362
+ * // Find users where data column has 'role' key
363
+ * User.findAll({ where: { data: { [Op.jsonHasKey]: 'role' } } })
364
+ * // SQL: WHERE data ? 'role' (PostgreSQL)
365
+ * // SQL: WHERE JSON_CONTAINS_PATH(data, 'one', '$.role') (MySQL)
366
+ * ```
367
+ */
368
+ jsonHasKey: Symbol('jsonHasKey'),
369
+ /**
370
+ * JSONB extract - extracts a JSON object field (returns JSON)
371
+ * PostgreSQL: -> operator
372
+ *
373
+ * @example
374
+ * ```typescript
375
+ * // Extract field as JSON: data -> 'key'
376
+ * User.findAll({ where: { data: { [Op.jsonbExtract]: { path: 'key' } } } })
377
+ * // SQL: WHERE data -> 'key'
378
+ * ```
379
+ */
380
+ jsonbExtract: Symbol('jsonbExtract'),
381
+ /**
382
+ * JSONB extract text - extracts a JSON object field as text (returns text)
383
+ * PostgreSQL: ->> operator
384
+ *
385
+ * @example
386
+ * ```typescript
387
+ * // Extract field as text: data ->> 'key'
388
+ * User.findAll({ where: { data: { [Op.jsonbExtractText]: { path: 'key', value: 'someValue' } } } })
389
+ * // SQL: WHERE data ->> 'key' = 'someValue'
390
+ * ```
391
+ */
392
+ jsonbExtractText: Symbol('jsonbExtractText'),
393
+ /**
394
+ * JSONB extract path - extracts JSON by path (returns JSON)
395
+ * PostgreSQL: #> operator
396
+ *
397
+ * @example
398
+ * ```typescript
399
+ * // Extract nested path as JSON: data #> '{a, b}'
400
+ * User.findAll({ where: { data: { [Op.jsonbExtractPath]: { path: ['a', 'b'] } } } })
401
+ * // SQL: WHERE data #> '{a, b}'
402
+ * ```
403
+ */
404
+ jsonbExtractPath: Symbol('jsonbExtractPath'),
405
+ /**
406
+ * JSONB extract path text - extracts JSON by path as text (returns text)
407
+ * PostgreSQL: #>> operator
408
+ *
409
+ * @example
410
+ * ```typescript
411
+ * // Extract nested path as text: data #>> '{a, b}'
412
+ * User.findAll({ where: { data: { [Op.jsonbExtractPathText]: { path: ['a', 'b'], value: 'someValue' } } } })
413
+ * // SQL: WHERE data #>> '{a, b}' = 'someValue'
414
+ * ```
415
+ */
416
+ jsonbExtractPathText: Symbol('jsonbExtractPathText'),
417
+ /**
418
+ * JSONB concatenation - concatenates two JSONB values
419
+ * PostgreSQL: || operator
420
+ *
421
+ * @example
422
+ * ```typescript
423
+ * // Concatenate JSONB values: data || '{"key": "value"}'
424
+ * User.findAll({ where: { data: { [Op.jsonConcat]: { key: 'value' } } } })
425
+ * // SQL: WHERE data || '{"key": "value"}'
426
+ * ```
427
+ */
428
+ jsonConcat: Symbol('jsonConcat'),
429
+ /**
430
+ * JSONB delete key - deletes a key from JSONB object
431
+ * PostgreSQL: - operator
432
+ *
433
+ * @example
434
+ * ```typescript
435
+ * // Delete key from JSONB: data - 'key'
436
+ * User.findAll({ where: { data: { [Op.jsonDelete]: 'key' } } })
437
+ * // SQL: WHERE data - 'key'
438
+ * ```
439
+ */
440
+ jsonDelete: Symbol('jsonDelete'),
441
+ /**
442
+ * JSONB delete by path - deletes a key from JSONB by path
443
+ * PostgreSQL: #- operator
444
+ *
445
+ * @example
446
+ * ```typescript
447
+ * // Delete by path: data #- '{a, b}'
448
+ * User.findAll({ where: { data: { [Op.jsonDeletePath]: ['a', 'b'] } } })
449
+ * // SQL: WHERE data #- '{a, b}'
450
+ * ```
451
+ */
452
+ jsonDeletePath: Symbol('jsonDeletePath'),
453
+ /**
454
+ * JSON path exists - checks if a JSON path exists and returns boolean
455
+ * PostgreSQL: @? operator
456
+ *
457
+ * @example
458
+ * ```typescript
459
+ * // Check if path exists: data @? '$.key'
460
+ * User.findAll({ where: { data: { [Op.jsonPathExists]: '$.key' } } })
461
+ * // SQL: WHERE data @? '$.key'
462
+ * ```
463
+ */
464
+ jsonPathExists: Symbol('jsonPathExists'),
465
+ /**
466
+ * JSON path query - evaluates JSON path and returns result
467
+ * PostgreSQL: @@ operator
468
+ *
469
+ * @example
470
+ * ```typescript
471
+ * // Query JSON path: data @@ '$.key'
472
+ * User.findAll({ where: { data: { [Op.jsonPathQuery]: '$.key' } } })
473
+ * // SQL: WHERE data @@ '$.key'
474
+ * ```
475
+ */
476
+ jsonPathQuery: Symbol('jsonPathQuery'),
477
+ /**
478
+ * JSON type of - returns the type of a JSON value
479
+ * PostgreSQL: json_typeof function
480
+ *
481
+ * @example
482
+ * ```typescript
483
+ * // Get JSON type: json_typeof(data)
484
+ * User.findAll({ where: { data: { [Op.jsonTypeOf]: 'object' } } })
485
+ * // SQL: WHERE json_typeof(data) = 'object'
486
+ * ```
487
+ */
488
+ jsonTypeOf: Symbol('jsonTypeOf'),
489
+ /**
490
+ * Create a JSON column path reference for querying
491
+ * Returns a Symbol that can be used as a computed property key
492
+ *
493
+ * @example
494
+ * ```typescript
495
+ * // Using as computed property key (recommended)
496
+ * User.findAll({
497
+ * where: {
498
+ * settings: { [Op.json('$.theme')]: 'dark' }
499
+ * }
500
+ * })
501
+ * // SQL: WHERE json_extract(settings, '$.theme') = 'dark'
502
+ *
503
+ * // Using with path prefix
504
+ * User.findAll({
505
+ * where: {
506
+ * data: { [Op.json('address.city')]: 'NYC' }
507
+ * }
508
+ * })
509
+ * // SQL: WHERE json_extract(data, '$.address.city') = 'NYC'
510
+ * ```
511
+ */
512
+ json(path) {
513
+ // Use Symbol.for to create a consistent symbol that can be retrieved later
514
+ // The path is encoded in the symbol description
515
+ return Symbol.for(`json:${path}`);
516
+ },
517
+ /**
518
+ * Create a JSON key path query
519
+ * Useful for accessing specific keys in a JSON/JSONB column
520
+ *
521
+ * @example
522
+ * ```typescript
523
+ * // Access JSON key 'name' from data column
524
+ * User.findAll({
525
+ * where: {
526
+ * data: { [Op.key]: 'name' }
527
+ * }
528
+ * })
529
+ *
530
+ * // Compare JSON key to a value
531
+ * User.findAll({
532
+ * where: {
533
+ * data: { [Op.key]: { path: 'name', value: 'John' } }
534
+ * }
535
+ * })
536
+ *
537
+ * // Nested key access
538
+ * User.findAll({
539
+ * where: {
540
+ * data: { [Op.key]: { path: 'profile.settings.theme', value: 'dark' } }
541
+ * }
542
+ * })
543
+ * ```
544
+ */
545
+ key(pathOrOptions) {
546
+ if (typeof pathOrOptions === 'string') {
547
+ return { $key: pathOrOptions };
548
+ }
549
+ return { $key: pathOrOptions };
550
+ },
551
+ /**
552
+ * Create a raw SQL literal
553
+ * Useful for embedding raw SQL expressions like NOW(), CURRENT_TIMESTAMP, etc.
554
+ *
555
+ * @example
556
+ * ```typescript
557
+ * // Use with update to set current timestamp
558
+ * User.update({ lastLogin: Op.literal('NOW()') }, { where: { ... } })
559
+ *
560
+ * // Use in where clause to compare with current time
561
+ * User.findAll({
562
+ * where: {
563
+ * createdAt: { [Op.lt]: Op.literal('NOW()') }
564
+ * }
565
+ * })
566
+ *
567
+ * // Compare column to a literal value
568
+ * User.findAll({
569
+ * where: {
570
+ * updatedAt: { [Op.gt]: Op.literal('createdAt') }
571
+ * }
572
+ * })
573
+ *
574
+ * // Use with Op.where
575
+ * User.findAll({ where: Op.where(Op.col('created_at'), '>', Op.literal('NOW()')) })
576
+ * ```
577
+ */
578
+ literal(value) {
579
+ return { $literal: value };
580
+ },
581
+ /**
582
+ * Create a CAST expression for type casting
583
+ * SQL: CAST(value AS type)
584
+ *
585
+ * @example
586
+ * ```typescript
587
+ * // CAST('2023-01-01' AS DATE)
588
+ * User.findAll({
589
+ * attributes: [[Op.cast('2023-01-01', 'DATE'), 'dateOnly']]
590
+ * })
591
+ *
592
+ * // CAST(column AS INTEGER)
593
+ * User.findAll({
594
+ * attributes: [[Op.cast(col('createdAt'), 'INTEGER'), 'dateInt']]
595
+ * })
596
+ *
597
+ * // CAST to DECIMAL
598
+ * User.findAll({
599
+ * attributes: [[Op.cast(col('price'), 'DECIMAL(10,2)'), 'priceDecimal']]
600
+ * })
601
+ * ```
602
+ */
603
+ cast(value, type) {
604
+ return cast(value, type);
605
+ },
606
+ /**
607
+ * Create an EXTRACT expression for extracting date parts
608
+ * SQL: EXTRACT(part FROM field)
609
+ *
610
+ * @example
611
+ * ```typescript
612
+ * // EXTRACT(YEAR FROM createdAt)
613
+ * User.findAll({
614
+ * attributes: [[Op.extract('createdAt', 'year'), 'year']]
615
+ * })
616
+ *
617
+ * // EXTRACT(MONTH FROM order_date)
618
+ * User.findAll({
619
+ * attributes: [[Op.extract('orderDate', 'month'), 'month']]
620
+ * })
621
+ *
622
+ * // EXTRACT(HOUR FROM timestamp)
623
+ * User.findAll({
624
+ * attributes: [[Op.extract('createdAt', 'hour'), 'hour']]
625
+ * })
626
+ * ```
627
+ */
628
+ extract(field, part) {
629
+ return extract(field, part);
630
+ },
631
+ /**
632
+ * Create a CONVERT expression for type conversion
633
+ * SQL: CONVERT(value, type) for MySQL, CAST for other dialects
634
+ *
635
+ * @example
636
+ * ```typescript
637
+ * // CONVERT(value, type)
638
+ * User.findAll({
639
+ * attributes: [[Op.conv(col('value'), null, 'CHAR'), 'strValue']]
640
+ * })
641
+ *
642
+ * // MySQL charset conversion
643
+ * User.findAll({
644
+ * attributes: [[Op.conv(col('name'), null, 'utf8mb4'), 'utf8Name']]
645
+ * })
646
+ * ```
647
+ */
648
+ conv(value, from, to) {
649
+ return conv(value, from, to);
650
+ },
651
+ /**
652
+ * Create a where clause with a column reference
653
+ * Supports both simple equality (2 args) and explicit operator (3 args)
654
+ *
655
+ * @example
656
+ * ```typescript
657
+ * // Simple equality (2 args) - defaults to =
658
+ * Op.where(Op.col('username'), 'john')
659
+ * // => { $where: { $col: 'username', $eq: 'john' } }
660
+ *
661
+ * // Compare column to a value with explicit operator
662
+ * Op.where(Op.col('user.id'), '=', 1)
663
+ * // => { $where: { $col: 'user.id', $eq: 1 } }
664
+ *
665
+ * // Compare column to another column
666
+ * Op.where(Op.col('balance'), '>', Op.col('credit_limit'))
667
+ * // => { $where: { $col: 'balance', $gt: { $col: 'credit_limit' } } }
668
+ *
669
+ * // Using with Op.eq symbol
670
+ * Op.where(Op.col('user.id'), Op.eq, 1)
671
+ * // => { $where: { $col: 'user.id', $eq: 1 } }
672
+ *
673
+ * // Using with findAll
674
+ * User.findAll({ where: Op.where(Op.col('username'), 'john') })
675
+ * User.findAll({ where: Op.where('status', 'active') })
676
+ * ```
677
+ */
678
+ where(column, operator, value) {
679
+ // Handle 2-argument version: Op.where(column, value) - defaults to equality
680
+ if (value === undefined) {
681
+ const compareValue = operator;
682
+ // If column is a string (column name), return simple equality
683
+ if (typeof column === 'string') {
684
+ return { [column]: compareValue };
685
+ }
686
+ // If column is a column reference, use $where with equality
687
+ return {
688
+ $where: {
689
+ $col: column.$col,
690
+ $eq: compareValue,
691
+ },
692
+ };
693
+ }
694
+ // 3-argument version: Op.where(column, operator, value)
695
+ // Convert string operator to operator key
696
+ let operatorKey;
697
+ const op = operator;
698
+ if (typeof op === 'symbol') {
699
+ operatorKey = operatorToWhereKey(op);
700
+ }
701
+ else {
702
+ // Map string operators to their $ format
703
+ switch (op) {
704
+ case '=':
705
+ case '==':
706
+ operatorKey = '$eq';
707
+ break;
708
+ case '!=':
709
+ case '<>':
710
+ operatorKey = '$ne';
711
+ break;
712
+ case '>':
713
+ operatorKey = '$gt';
714
+ break;
715
+ case '>=':
716
+ operatorKey = '$gte';
717
+ break;
718
+ case '<':
719
+ operatorKey = '$lt';
720
+ break;
721
+ case '<=':
722
+ operatorKey = '$lte';
723
+ break;
724
+ case 'like':
725
+ operatorKey = '$like';
726
+ break;
727
+ case 'not like':
728
+ case 'not_like':
729
+ operatorKey = '$notLike';
730
+ break;
731
+ case 'in':
732
+ operatorKey = '$in';
733
+ break;
734
+ case 'not in':
735
+ case 'not_in':
736
+ operatorKey = '$notIn';
737
+ break;
738
+ case 'between':
739
+ operatorKey = '$between';
740
+ break;
741
+ case 'not between':
742
+ case 'not_between':
743
+ operatorKey = '$notBetween';
744
+ break;
745
+ case 'is null':
746
+ case 'is_null':
747
+ operatorKey = '$isNull';
748
+ break;
749
+ case 'is not null':
750
+ case 'is_not_null':
751
+ operatorKey = '$isNotNull';
752
+ break;
753
+ default:
754
+ operatorKey = `$${op}`;
755
+ }
756
+ }
757
+ // Handle string column name
758
+ if (typeof column === 'string') {
759
+ return {
760
+ [column]: {
761
+ [operatorKey]: value,
762
+ },
763
+ };
764
+ }
765
+ return {
766
+ $where: {
767
+ $col: column.$col,
768
+ [operatorKey]: value,
769
+ },
770
+ };
771
+ },
772
+ /**
773
+ * Create an ascending order expression
774
+ * SQL: ORDER BY field ASC
775
+ *
776
+ * @example
777
+ * ```typescript
778
+ * // Order by name ascending
779
+ * User.findAll({ order: [Op.asc('name')] })
780
+ * // SQL: ORDER BY name ASC
781
+ *
782
+ * // Multiple order conditions
783
+ * User.findAll({ order: [Op.asc('name'), Op.desc('createdAt')] })
784
+ * ```
785
+ */
786
+ asc(field) {
787
+ return asc(field);
788
+ },
789
+ /**
790
+ * Create a descending order expression
791
+ * SQL: ORDER BY field DESC
792
+ *
793
+ * @example
794
+ * ```typescript
795
+ * // Order by createdAt descending (newest first)
796
+ * User.findAll({ order: [Op.desc('createdAt')] })
797
+ * // SQL: ORDER BY createdAt DESC
798
+ *
799
+ * // Combined with asc
800
+ * User.findAll({ order: [Op.asc('name'), Op.desc('createdAt')] })
801
+ * ```
802
+ */
803
+ desc(field) {
804
+ return desc(field);
805
+ },
806
+ /**
807
+ * Create a random order expression
808
+ * SQL: ORDER BY RANDOM() (SQLite/PostgreSQL) or ORDER BY RAND() (MySQL)
809
+ *
810
+ * @example
811
+ * ```typescript
812
+ * // Random ordering (useful for sampling)
813
+ * User.findAll({ order: [Op.random()] })
814
+ * // SQLite: ORDER BY RANDOM()
815
+ * // MySQL: ORDER BY RAND()
816
+ * // PostgreSQL: ORDER BY RANDOM()
817
+ *
818
+ * // With limit for random sample
819
+ * User.findAll({ order: [Op.random()], limit: 5 })
820
+ * ```
821
+ */
822
+ random() {
823
+ return random();
824
+ },
825
+ /**
826
+ * Check if a value is not null
827
+ * Returns true if the value is not null or undefined
828
+ * Useful as a predicate for filtering arrays
829
+ *
830
+ * @example
831
+ * ```typescript
832
+ * // Filter out null values from an array
833
+ * const values = [1, null, 2, undefined, 3];
834
+ * const notNullValues = values.filter(Op.isNotNull);
835
+ * // => [1, 2, 3]
836
+ *
837
+ * // Using in array filter with objects
838
+ * const users = [{ name: 'John', age: null }, { name: 'Jane', age: 25 }];
839
+ * const withAge = users.filter(u => Op.isNotNull(u.age));
840
+ * // => [{ name: 'Jane', age: 25 }]
841
+ * ```
842
+ */
843
+ isNotNullPredicate(value) {
844
+ return value !== null && value !== undefined;
845
+ },
846
+ /**
847
+ * Create a full-text search MATCH AGAINST condition (MySQL)
848
+ * SQL: MATCH(columns) AGAINST(searchTerm [IN NATURAL LANGUAGE MODE | IN BOOLEAN MODE])
849
+ *
850
+ * @example
851
+ * ```typescript
852
+ * // Natural language mode search
853
+ * Article.findAll({
854
+ * where: {
855
+ * [Op.matchAgainst(['title', 'body'])]: 'database'
856
+ * }
857
+ * })
858
+ * // SQL: WHERE MATCH(title, body) AGAINST('database' IN NATURAL LANGUAGE MODE)
859
+ *
860
+ * // Boolean mode search
861
+ * Article.findAll({
862
+ * where: {
863
+ * [Op.matchAgainst(['title', 'body'], { mode: 'boolean' })]: '+mysql -oracle'
864
+ * }
865
+ * })
866
+ * // SQL: WHERE MATCH(title, body) AGAINST('+mysql -oracle' IN BOOLEAN MODE)
867
+ * ```
868
+ */
869
+ matchAgainst(columns, options) {
870
+ const cols = Array.isArray(columns) ? columns : [columns];
871
+ return {
872
+ $match: {
873
+ columns: cols,
874
+ mode: options?.mode || 'natural',
875
+ },
876
+ };
877
+ },
878
+ /**
879
+ * Create a full-text search condition (alias for matchAgainst with explicit mode)
880
+ * SQL: MATCH(columns) AGAINST(searchTerm IN NATURAL LANGUAGE MODE)
881
+ *
882
+ * @example
883
+ * ```typescript
884
+ * Article.findAll({
885
+ * where: {
886
+ * [Op.matchFulltext(['title', 'body'])]: 'database'
887
+ * }
888
+ * })
889
+ * // SQL: WHERE MATCH(title, body) AGAINST('database' IN NATURAL LANGUAGE MODE)
890
+ * ```
891
+ */
892
+ matchFulltext(columns) {
893
+ const cols = Array.isArray(columns) ? columns : [columns];
894
+ return {
895
+ $match: {
896
+ columns: cols,
897
+ mode: 'natural',
898
+ },
899
+ };
900
+ },
901
+ /**
902
+ * Create a PostgreSQL tsvector expression
903
+ * SQL: to_tsvector(config, column)
904
+ *
905
+ * @example
906
+ * ```typescript
907
+ * Article.findAll({
908
+ * where: {
909
+ * [Op.toTsvector('title')]: { $tsquery: 'database' }
910
+ * }
911
+ * })
912
+ * // SQL: WHERE to_tsvector('english', title) @@ to_tsquery('english', 'database')
913
+ * ```
914
+ */
915
+ toTsvector(column, config = 'english') {
916
+ return {
917
+ $tsvector: {
918
+ column,
919
+ config,
920
+ },
921
+ };
922
+ },
923
+ /**
924
+ * Create a PostgreSQL tsquery expression
925
+ * SQL: to_tsquery(config, query)
926
+ *
927
+ * @example
928
+ * ```typescript
929
+ * Article.findAll({
930
+ * where: {
931
+ * body: { [Op.toTsquery('database')]: true }
932
+ * }
933
+ * })
934
+ * // SQL: WHERE body @@ to_tsquery('english', 'database')
935
+ * ```
936
+ */
937
+ toTsquery(query, config = 'english') {
938
+ return {
939
+ $tsquery: {
940
+ query,
941
+ config,
942
+ },
943
+ };
944
+ },
945
+ // ============================================
946
+ // Spatial/Geometric operators
947
+ // ============================================
948
+ /**
949
+ * ST_Distance - calculate distance between two geometries
950
+ * MySQL: ST_Distance(geom1, geom2)
951
+ * PostgreSQL: ST_Distance(geom1, geom2) - for geometry, ST_Distance(geog1, geog2) for geography
952
+ *
953
+ * @example
954
+ * ```typescript
955
+ * // Find locations within 100 meters of a point
956
+ * Location.findAll({
957
+ * where: {
958
+ * location: { [Op.stDWithin]: { from: 'POINT(0 0)', distance: 100, srid: 4326 } }
959
+ * }
960
+ * })
961
+ * ```
962
+ */
963
+ stDistance: Symbol('stDistance'),
964
+ /**
965
+ * ST_Within - check if geometry A is within geometry B
966
+ * MySQL: ST_Within(geom1, geom2)
967
+ * PostgreSQL: ST_Within(geom1, geom2)
968
+ *
969
+ * @example
970
+ * ```typescript
971
+ * // Find locations within a polygon
972
+ * Location.findAll({
973
+ * where: {
974
+ * location: { [Op.stWithin]: { geometry: 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', srid: 4326 } }
975
+ * }
976
+ * })
977
+ * ```
978
+ */
979
+ stWithin: Symbol('stWithin'),
980
+ /**
981
+ * ST_Contains - check if geometry A contains geometry B
982
+ * MySQL: ST_Contains(geom1, geom2)
983
+ * PostgreSQL: ST_Contains(geom1, geom2)
984
+ *
985
+ * @example
986
+ * ```typescript
987
+ * // Find areas that contain a point
988
+ * Area.findAll({
989
+ * where: {
990
+ * boundary: { [Op.stContains]: { geometry: 'POINT(5 5)', srid: 4326 } }
991
+ * }
992
+ * })
993
+ * ```
994
+ */
995
+ stContains: Symbol('stContains'),
996
+ /**
997
+ * ST_Intersects - check if two geometries intersect
998
+ * MySQL: ST_Intersects(geom1, geom2)
999
+ * PostgreSQL: ST_Intersects(geom1, geom2)
1000
+ *
1001
+ * @example
1002
+ * ```typescript
1003
+ * // Find locations that intersect with a polygon
1004
+ * Location.findAll({
1005
+ * where: {
1006
+ * location: { [Op.stIntersects]: { geometry: 'POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', srid: 4326 } }
1007
+ * }
1008
+ * })
1009
+ * ```
1010
+ */
1011
+ stIntersects: Symbol('stIntersects'),
1012
+ /**
1013
+ * ST_DWithin - check if geometries are within a given distance
1014
+ * PostgreSQL: ST_DWithin (for both geometry and geography)
1015
+ * MySQL 8.0+: ST_Distance_Sphere or ST_DWithin (with care)
1016
+ *
1017
+ * @example
1018
+ * ```typescript
1019
+ * // Find locations within 1km of a point
1020
+ * Location.findAll({
1021
+ * where: {
1022
+ * location: { [Op.stDWithin]: { from: 'POINT(-122.4194 37.7749)', distance: 1000, srid: 4326 } }
1023
+ * }
1024
+ * })
1025
+ * ```
1026
+ */
1027
+ stDWithin: Symbol('stDWithin'),
1028
+ /**
1029
+ * ST_Crosses - check if two geometries cross
1030
+ * MySQL: ST_Crosses(geom1, geom2)
1031
+ * PostgreSQL: ST_Crosses(geom1, geom2)
1032
+ */
1033
+ stCrosses: Symbol('stCrosses'),
1034
+ /**
1035
+ * ST_Overlaps - check if two geometries overlap
1036
+ * MySQL: ST_Overlaps(geom1, geom2)
1037
+ * PostgreSQL: ST_Overlaps(geom1, geom2)
1038
+ */
1039
+ stOverlaps: Symbol('stOverlaps'),
1040
+ /**
1041
+ * ST_Touches - check if two geometries touch
1042
+ * MySQL: ST_Touches(geom1, geom2)
1043
+ * PostgreSQL: ST_Touches(geom1, geom2)
1044
+ */
1045
+ stTouches: Symbol('stTouches'),
1046
+ /**
1047
+ * ST_Equals - check if two geometries are equal
1048
+ * MySQL: ST_Equals(geom1, geom2)
1049
+ * PostgreSQL: ST_Equals(geom1, geom2)
1050
+ */
1051
+ stEquals: Symbol('stEquals'),
1052
+ /**
1053
+ * ST_IsValid - check if a geometry is valid
1054
+ * MySQL: ST_IsValid(geom)
1055
+ * PostgreSQL: ST_IsValid(geom)
1056
+ */
1057
+ stIsValid: Symbol('stIsValid'),
1058
+ };
1059
+ /**
1060
+ * Check if a value is a column reference or needs column reference handling
1061
+ */
1062
+ function isColOperator(value) {
1063
+ return isColumnReference(value);
1064
+ }
1065
+ // Alias for backward compatibility and convenience
1066
+ exports.Operators = exports.Op;
1067
+ /**
1068
+ * Check if a value is a symbol operator
1069
+ */
1070
+ function isOperator(value) {
1071
+ return typeof value === 'symbol';
1072
+ }
1073
+ /**
1074
+ * Get the string representation of an operator symbol
1075
+ */
1076
+ function getOperatorString(op) {
1077
+ const opStr = op.description?.replace(/^\(.*?\)\s*/, '') || '';
1078
+ switch (opStr) {
1079
+ case 'and':
1080
+ return '$and';
1081
+ case 'or':
1082
+ return '$or';
1083
+ case 'not':
1084
+ return '$not';
1085
+ case 'eq':
1086
+ return '$eq';
1087
+ case 'ne':
1088
+ return '$ne';
1089
+ case 'gt':
1090
+ return '$gt';
1091
+ case 'gte':
1092
+ return '$gte';
1093
+ case 'lt':
1094
+ return '$lt';
1095
+ case 'lte':
1096
+ return '$lte';
1097
+ case 'like':
1098
+ return '$like';
1099
+ case 'notLike':
1100
+ return '$notLike';
1101
+ case 'iLike':
1102
+ return '$iLike';
1103
+ case 'notILike':
1104
+ return '$notILike';
1105
+ case 'in':
1106
+ return '$in';
1107
+ case 'notIn':
1108
+ return '$notIn';
1109
+ case 'between':
1110
+ return '$between';
1111
+ case 'notBetween':
1112
+ return '$notBetween';
1113
+ case 'isNull':
1114
+ return '$isNull';
1115
+ case 'isNotNull':
1116
+ return '$isNotNull';
1117
+ case 'startsWith':
1118
+ return '$startsWith';
1119
+ case 'notStartsWith':
1120
+ return '$notStartsWith';
1121
+ case 'endsWith':
1122
+ return '$endsWith';
1123
+ case 'notEndsWith':
1124
+ return '$notEndsWith';
1125
+ case 'substring':
1126
+ return '$substring';
1127
+ case 'notSubstring':
1128
+ return '$notSubstring';
1129
+ default:
1130
+ return `$${opStr}`;
1131
+ }
1132
+ }
1133
+ /**
1134
+ * Convert operator symbol to where clause key
1135
+ */
1136
+ function operatorToWhereKey(op) {
1137
+ if (op === exports.Op.and)
1138
+ return '$and';
1139
+ if (op === exports.Op.or)
1140
+ return '$or';
1141
+ if (op === exports.Op.not)
1142
+ return '$not';
1143
+ if (op === exports.Op.eq)
1144
+ return '$eq';
1145
+ if (op === exports.Op.is)
1146
+ return '$is';
1147
+ if (op === exports.Op.ne)
1148
+ return '$ne';
1149
+ if (op === exports.Op.gt)
1150
+ return '$gt';
1151
+ if (op === exports.Op.gte)
1152
+ return '$gte';
1153
+ if (op === exports.Op.lt)
1154
+ return '$lt';
1155
+ if (op === exports.Op.lte)
1156
+ return '$lte';
1157
+ if (op === exports.Op.like)
1158
+ return '$like';
1159
+ if (op === exports.Op.notLike)
1160
+ return '$notLike';
1161
+ if (op === exports.Op.iLike)
1162
+ return '$iLike';
1163
+ if (op === exports.Op.notILike)
1164
+ return '$notILike';
1165
+ if (op === exports.Op.startsWith)
1166
+ return '$startsWith';
1167
+ if (op === exports.Op.notStartsWith)
1168
+ return '$notStartsWith';
1169
+ if (op === exports.Op.endsWith)
1170
+ return '$endsWith';
1171
+ if (op === exports.Op.notEndsWith)
1172
+ return '$notEndsWith';
1173
+ if (op === exports.Op.substring)
1174
+ return '$substring';
1175
+ if (op === exports.Op.notSubstring)
1176
+ return '$notSubstring';
1177
+ if (op === exports.Op.col)
1178
+ return '$col';
1179
+ if (op === exports.Op.in)
1180
+ return '$in';
1181
+ if (op === exports.Op.notIn)
1182
+ return '$notIn';
1183
+ if (op === exports.Op.between)
1184
+ return '$between';
1185
+ if (op === exports.Op.notBetween)
1186
+ return '$notBetween';
1187
+ if (op === exports.Op.isNull)
1188
+ return '$isNull';
1189
+ if (op === exports.Op.isNotNull)
1190
+ return '$isNotNull';
1191
+ if (op === exports.Op.exists)
1192
+ return '$exists';
1193
+ if (op === exports.Op.notExists)
1194
+ return '$notExists';
1195
+ if (op === exports.Op.regexp)
1196
+ return '$regexp';
1197
+ if (op === exports.Op.notRegexp)
1198
+ return '$notRegexp';
1199
+ if (op === exports.Op.iRegexp)
1200
+ return '$iRegexp';
1201
+ if (op === exports.Op.notIRegexp)
1202
+ return '$notIRegexp';
1203
+ if (op === exports.Op.any)
1204
+ return '$any';
1205
+ if (op === exports.Op.all)
1206
+ return '$all';
1207
+ if (op === exports.Op.contains)
1208
+ return '$contains';
1209
+ if (op === exports.Op.containedBy)
1210
+ return '$containedBy';
1211
+ if (op === exports.Op.overlap)
1212
+ return '$overlap';
1213
+ if (op === exports.Op.containsKey)
1214
+ return '$containsKey';
1215
+ if (op === exports.Op.containsKeyPath)
1216
+ return '$containsKeyPath';
1217
+ if (op === exports.Op.containsPath)
1218
+ return '$containsPath';
1219
+ if (op === exports.Op.arrayContains)
1220
+ return '$arrayContains';
1221
+ if (op === exports.Op.arrayContainedBy)
1222
+ return '$arrayContainedBy';
1223
+ if (op === exports.Op.arrayOverlaps)
1224
+ return '$arrayOverlaps';
1225
+ if (op === exports.Op.arrayAny)
1226
+ return '$arrayAny';
1227
+ if (op === exports.Op.arrayAll)
1228
+ return '$arrayAll';
1229
+ if (op === exports.Op.keyExists)
1230
+ return '$keyExists';
1231
+ if (op === exports.Op.key)
1232
+ return '$key';
1233
+ if (op === exports.Op.col)
1234
+ return '$col';
1235
+ if (op === exports.Op.json)
1236
+ return '$json';
1237
+ if (op === exports.Op.strictLeft)
1238
+ return '$strictLeft';
1239
+ if (op === exports.Op.strictRight)
1240
+ return '$strictRight';
1241
+ if (op === exports.Op.noExtendRight)
1242
+ return '$noExtendRight';
1243
+ if (op === exports.Op.noExtendLeft)
1244
+ return '$noExtendLeft';
1245
+ if (op === exports.Op.adj)
1246
+ return '$adj';
1247
+ if (op === exports.Op.notAdj)
1248
+ return '$notAdj';
1249
+ if (op === exports.Op.json)
1250
+ return '$json';
1251
+ if (op === exports.Op.jsonPath)
1252
+ return '$jsonPath';
1253
+ if (op === exports.Op.jsonContains)
1254
+ return '$jsonContains';
1255
+ if (op === exports.Op.jsonHasKey)
1256
+ return '$jsonHasKey';
1257
+ if (op === exports.Op.jsonConcat)
1258
+ return '$jsonConcat';
1259
+ if (op === exports.Op.jsonDelete)
1260
+ return '$jsonDelete';
1261
+ if (op === exports.Op.jsonDeletePath)
1262
+ return '$jsonDeletePath';
1263
+ if (op === exports.Op.jsonPathExists)
1264
+ return '$jsonPathExists';
1265
+ if (op === exports.Op.jsonPathQuery)
1266
+ return '$jsonPathQuery';
1267
+ if (op === exports.Op.jsonTypeOf)
1268
+ return '$jsonTypeOf';
1269
+ // JSONB extract operators
1270
+ if (op === exports.Op.jsonbExtract)
1271
+ return '$jsonbExtract';
1272
+ if (op === exports.Op.jsonbExtractText)
1273
+ return '$jsonbExtractText';
1274
+ if (op === exports.Op.jsonbExtractPath)
1275
+ return '$jsonbExtractPath';
1276
+ if (op === exports.Op.jsonbExtractPathText)
1277
+ return '$jsonbExtractPathText';
1278
+ // Spatial operators
1279
+ if (op === exports.Op.stDistance)
1280
+ return '$stDistance';
1281
+ if (op === exports.Op.stWithin)
1282
+ return '$stWithin';
1283
+ if (op === exports.Op.stContains)
1284
+ return '$stContains';
1285
+ if (op === exports.Op.stIntersects)
1286
+ return '$stIntersects';
1287
+ if (op === exports.Op.stDWithin)
1288
+ return '$stDWithin';
1289
+ if (op === exports.Op.stCrosses)
1290
+ return '$stCrosses';
1291
+ if (op === exports.Op.stOverlaps)
1292
+ return '$stOverlaps';
1293
+ if (op === exports.Op.stTouches)
1294
+ return '$stTouches';
1295
+ if (op === exports.Op.stEquals)
1296
+ return '$stEquals';
1297
+ if (op === exports.Op.stIsValid)
1298
+ return '$stIsValid';
1299
+ // Full-text search operators
1300
+ if (op === exports.Op.match)
1301
+ return '$match';
1302
+ if (op === exports.Op.matchFulltext)
1303
+ return '$match';
1304
+ if (op === exports.Op.toTsvector)
1305
+ return '$tsvector';
1306
+ if (op === exports.Op.toTsquery)
1307
+ return '$tsquery';
1308
+ // Note: startsWith, notStartsWith, endsWith, notEndsWith, substring, notSubstring
1309
+ // are now methods that return clause objects directly
1310
+ // Default - use symbol description, preserving its original casing so that
1311
+ // camelCase operator names normalize to matching camelCase `$key`s.
1312
+ const desc = op.description || '';
1313
+ const key = desc.match(/\((.*?)\)/)?.[1] || desc;
1314
+ return `$${key}`;
1315
+ }
1316
+ function where(columnOrCondition, operator, value) {
1317
+ // Handle the case where it's called with a single object argument
1318
+ if (typeof columnOrCondition === 'object' && columnOrCondition !== null) {
1319
+ const condition = columnOrCondition;
1320
+ // Handle nested OR: { OR: [...] } => { $or: [...] }
1321
+ if ('OR' in condition) {
1322
+ return { $or: condition.OR };
1323
+ }
1324
+ // Handle nested AND: { AND: [...] } => { $and: [...] }
1325
+ if ('AND' in condition) {
1326
+ return { $and: condition.AND };
1327
+ }
1328
+ // Handle combined OR/AND in a single object (e.g., { OR: [...], AND: [...] })
1329
+ const result = {};
1330
+ if ('$or' in condition) {
1331
+ result.$or = condition.$or;
1332
+ }
1333
+ if ('$and' in condition) {
1334
+ result.$and = condition.$and;
1335
+ }
1336
+ if ('$not' in condition) {
1337
+ result.$not = condition.$not;
1338
+ }
1339
+ // Merge any additional field conditions
1340
+ for (const key of Object.keys(condition)) {
1341
+ if (key !== 'OR' && key !== 'AND') {
1342
+ result[key] = condition[key];
1343
+ }
1344
+ }
1345
+ // If the result only has field conditions (no $or/$and/$not), return as-is
1346
+ // The WhereBuilder already handles these formats
1347
+ if (Object.keys(result).length === 0) {
1348
+ return condition;
1349
+ }
1350
+ return result;
1351
+ }
1352
+ // Handle three-argument form: where(column, operator, value)
1353
+ const column = columnOrCondition;
1354
+ return {
1355
+ [column]: {
1356
+ [operatorToWhereKey(operator)]: value,
1357
+ },
1358
+ };
1359
+ }
1360
+ /**
1361
+ * Helper to create AND conditions
1362
+ */
1363
+ function and(...conditions) {
1364
+ return { $and: conditions };
1365
+ }
1366
+ /**
1367
+ * Helper to create OR conditions
1368
+ */
1369
+ function or(...conditions) {
1370
+ return { $or: conditions };
1371
+ }
1372
+ /**
1373
+ * Helper to create NOT conditions
1374
+ */
1375
+ function not(condition) {
1376
+ return { $not: condition };
1377
+ }
1378
+ /**
1379
+ * Helper to create equality condition
1380
+ */
1381
+ function eq(column, value) {
1382
+ return where(column, exports.Op.eq, value);
1383
+ }
1384
+ /**
1385
+ * Helper to create not equal condition
1386
+ */
1387
+ function ne(column, value) {
1388
+ return where(column, exports.Op.ne, value);
1389
+ }
1390
+ /**
1391
+ * Helper to create greater than condition
1392
+ */
1393
+ function gt(column, value) {
1394
+ return where(column, exports.Op.gt, value);
1395
+ }
1396
+ /**
1397
+ * Helper to create greater than or equal condition
1398
+ */
1399
+ function gte(column, value) {
1400
+ return where(column, exports.Op.gte, value);
1401
+ }
1402
+ /**
1403
+ * Helper to create less than condition
1404
+ */
1405
+ function lt(column, value) {
1406
+ return where(column, exports.Op.lt, value);
1407
+ }
1408
+ /**
1409
+ * Helper to create less than or equal condition
1410
+ */
1411
+ function lte(column, value) {
1412
+ return where(column, exports.Op.lte, value);
1413
+ }
1414
+ /**
1415
+ * Helper to create LIKE condition
1416
+ * Wraps the value with % on both sides for contains matching
1417
+ */
1418
+ function like(column, value) {
1419
+ return { [column]: { $like: `%${value}%` } };
1420
+ }
1421
+ /**
1422
+ * Helper to create NOT LIKE condition
1423
+ */
1424
+ function notLike(column, value) {
1425
+ return { [column]: { $notLike: `%${value}%` } };
1426
+ }
1427
+ /**
1428
+ * Helper to create IN condition
1429
+ */
1430
+ function inOp(column, values) {
1431
+ return where(column, exports.Op.in, values);
1432
+ }
1433
+ /**
1434
+ * Helper to create NOT IN condition
1435
+ */
1436
+ function notIn(column, values) {
1437
+ return where(column, exports.Op.notIn, values);
1438
+ }
1439
+ /**
1440
+ * Helper to create BETWEEN condition
1441
+ */
1442
+ function between(column, start, end) {
1443
+ return where(column, exports.Op.between, [start, end]);
1444
+ }
1445
+ /**
1446
+ * Helper to create NOT BETWEEN condition
1447
+ */
1448
+ function notBetween(column, start, end) {
1449
+ return where(column, exports.Op.notBetween, [start, end]);
1450
+ }
1451
+ /**
1452
+ * Helper to create IS NULL condition
1453
+ */
1454
+ function isNull(column) {
1455
+ return where(column, exports.Op.isNull, true);
1456
+ }
1457
+ /**
1458
+ * Helper to create IS NOT NULL condition
1459
+ */
1460
+ function isNotNull(column) {
1461
+ return where(column, exports.Op.isNotNull, true);
1462
+ }
1463
+ /**
1464
+ * Helper to create NOT EXISTS condition for anti-joins
1465
+ * Returns records where the subquery returns no rows
1466
+ *
1467
+ * @example
1468
+ * // Find users with no orders
1469
+ * User.findAll({
1470
+ * where: notExists('SELECT 1 FROM orders WHERE orders.user_id = users.id')
1471
+ * })
1472
+ */
1473
+ function notExists(subquery) {
1474
+ return { $notExists: typeof subquery === 'string' ? subquery : subquery };
1475
+ }
1476
+ /**
1477
+ * Helper to create EXISTS condition
1478
+ * Returns records where the subquery returns at least one row
1479
+ *
1480
+ * @example
1481
+ * // Find users with at least one order
1482
+ * User.findAll({
1483
+ * where: exists('SELECT 1 FROM orders WHERE orders.user_id = users.id')
1484
+ * })
1485
+ */
1486
+ function exists(subquery) {
1487
+ return { $exists: typeof subquery === 'string' ? subquery : subquery };
1488
+ }
1489
+ /**
1490
+ * Helper to create NOT IN with subquery condition for anti-joins
1491
+ * Returns records where the column value is not in the subquery result
1492
+ *
1493
+ * @example
1494
+ * // Find users not in the banned users list
1495
+ * User.findAll({
1496
+ * where: notInSubquery('id', 'SELECT user_id FROM banned_users')
1497
+ * })
1498
+ */
1499
+ function notInSubquery(column, subquery) {
1500
+ const subqueryValue = typeof subquery === 'string' ? subquery : subquery.$literal;
1501
+ return { [column]: { $notIn: subqueryValue } };
1502
+ }
1503
+ /**
1504
+ * Helper to create IN with subquery condition
1505
+ * Returns records where the column value is in the subquery result
1506
+ *
1507
+ * @example
1508
+ * // Find users that have placed orders
1509
+ * User.findAll({
1510
+ * where: inSubquery('id', 'SELECT user_id FROM orders')
1511
+ * })
1512
+ */
1513
+ function inSubquery(column, subquery) {
1514
+ const subqueryValue = typeof subquery === 'string' ? subquery : subquery.$literal;
1515
+ return { [column]: { $in: subqueryValue } };
1516
+ }
1517
+ /**
1518
+ * Helper to create an anti-join condition using LEFT JOIN WHERE NULL pattern
1519
+ * This is useful for finding records that don't have related records
1520
+ *
1521
+ * @example
1522
+ * // Find users with no orders - using LEFT JOIN WHERE NULL pattern
1523
+ * User.findAll({
1524
+ * include: [{
1525
+ * model: Order,
1526
+ * where: { userId: { [Op.col]: 'User.id' } },
1527
+ * antiJoin: true
1528
+ * }]
1529
+ * })
1530
+ */
1531
+ function antiJoin(model, options) {
1532
+ return {
1533
+ model,
1534
+ antiJoin: true,
1535
+ ...options,
1536
+ };
1537
+ }
1538
+ /**
1539
+ * Helper to create an exclude condition (alias for antiJoin)
1540
+ * Returns records that are NOT in the specified model/table
1541
+ *
1542
+ * @example
1543
+ * // Find users not in the banned users table
1544
+ * User.findAll({
1545
+ * include: [{ model: BannedUser, exclude: true }]
1546
+ * })
1547
+ */
1548
+ function exclude(model, options) {
1549
+ return {
1550
+ model,
1551
+ exclude: true,
1552
+ ...options,
1553
+ };
1554
+ }
1555
+ /**
1556
+ * Helper to create ILIKE (case-insensitive LIKE) condition
1557
+ */
1558
+ function iLike(column, value) {
1559
+ return { [column]: { $iLike: `%${value}%` } };
1560
+ }
1561
+ /**
1562
+ * Helper to create NOT ILIKE (case-insensitive NOT LIKE) condition
1563
+ */
1564
+ function notILike(column, value) {
1565
+ return { [column]: { $notILike: `%${value}%` } };
1566
+ }
1567
+ /**
1568
+ * Helper to create REGEXP condition
1569
+ */
1570
+ function regexp(column, pattern) {
1571
+ return where(column, exports.Op.regexp, pattern);
1572
+ }
1573
+ /**
1574
+ * Helper to create NOT REGEXP condition
1575
+ */
1576
+ function notRegexp(column, pattern) {
1577
+ return where(column, exports.Op.notRegexp, pattern);
1578
+ }
1579
+ /**
1580
+ * Helper to create IREGEXP (case-insensitive REGEXP) condition
1581
+ */
1582
+ function iRegexp(column, pattern) {
1583
+ return where(column, exports.Op.iRegexp, pattern);
1584
+ }
1585
+ /**
1586
+ * Helper to create NOT IREGEXP (case-insensitive NOT REGEXP) condition
1587
+ */
1588
+ function notIRegexp(column, pattern) {
1589
+ return where(column, exports.Op.notIRegexp, pattern);
1590
+ }
1591
+ /**
1592
+ * Helper to create CONTAINS condition (for arrays/JSON)
1593
+ */
1594
+ function contains(column, value) {
1595
+ return where(column, exports.Op.contains, value);
1596
+ }
1597
+ /**
1598
+ * Helper to create OVERLAP condition (for arrays)
1599
+ */
1600
+ function overlap(column, value) {
1601
+ return where(column, exports.Op.overlap, value);
1602
+ }
1603
+ /**
1604
+ * Helper to create array contains condition
1605
+ * PostgreSQL: @> operator
1606
+ *
1607
+ * @example
1608
+ * ```typescript
1609
+ * // Find products where tags contains both 'electronics' and 'sale'
1610
+ * Product.findAll({ where: arrayContains('tags', ['electronics', 'sale']) })
1611
+ * // SQL: WHERE tags @> ARRAY['electronics', 'sale']
1612
+ * ```
1613
+ */
1614
+ function arrayContains(column, value) {
1615
+ return where(column, exports.Op.arrayContains, value);
1616
+ }
1617
+ /**
1618
+ * Helper to create array contained by condition
1619
+ * PostgreSQL: <@ operator
1620
+ *
1621
+ * @example
1622
+ * ```typescript
1623
+ * // Find products where tags is contained by ['electronics', 'sale']
1624
+ * Product.findAll({ where: arrayContainedBy('tags', ['electronics', 'sale']) })
1625
+ * // SQL: WHERE tags <@ ARRAY['electronics', 'sale']
1626
+ * ```
1627
+ */
1628
+ function arrayContainedBy(column, value) {
1629
+ return where(column, exports.Op.arrayContainedBy, value);
1630
+ }
1631
+ /**
1632
+ * Helper to create array overlaps condition
1633
+ * PostgreSQL: && operator
1634
+ *
1635
+ * @example
1636
+ * ```typescript
1637
+ * // Find products where tags overlaps with ['electronics', 'sale']
1638
+ * Product.findAll({ where: arrayOverlaps('tags', ['electronics', 'sale']) })
1639
+ * // SQL: WHERE tags && ARRAY['electronics', 'sale']
1640
+ * ```
1641
+ */
1642
+ function arrayOverlaps(column, value) {
1643
+ return where(column, exports.Op.arrayOverlaps, value);
1644
+ }
1645
+ /**
1646
+ * Helper to create array ANY condition
1647
+ * PostgreSQL: = ANY() operator
1648
+ *
1649
+ * @example
1650
+ * ```typescript
1651
+ * // Find products where any tag equals 'electronics'
1652
+ * Product.findAll({ where: arrayAny('tags', 'electronics') })
1653
+ * // SQL: WHERE 'electronics' = ANY(tags)
1654
+ * ```
1655
+ */
1656
+ function arrayAny(column, value) {
1657
+ return where(column, exports.Op.arrayAny, value);
1658
+ }
1659
+ /**
1660
+ * Helper to create array ALL condition
1661
+ * PostgreSQL: ALL() operator
1662
+ *
1663
+ * @example
1664
+ * ```typescript
1665
+ * // Find products where all prices are greater than 100
1666
+ * Product.findAll({ where: arrayAll('prices', 100) })
1667
+ * // SQL: WHERE 100 > ALL(prices)
1668
+ * ```
1669
+ */
1670
+ function arrayAll(column, value) {
1671
+ return where(column, exports.Op.arrayAll, value);
1672
+ }
1673
+ /**
1674
+ * Helper to create startsWith condition
1675
+ * Creates a LIKE 'value%' pattern
1676
+ *
1677
+ * @example
1678
+ * ```typescript
1679
+ * // Find users whose name starts with 'John'
1680
+ * User.findAll({ where: startsWith('name', 'John') })
1681
+ * // SQL: WHERE name LIKE 'John%'
1682
+ * ```
1683
+ */
1684
+ function startsWith(column, value) {
1685
+ return { [column]: { $like: `${value}%` } };
1686
+ }
1687
+ /**
1688
+ * Helper to create notStartsWith condition
1689
+ * Creates a NOT LIKE 'value%' pattern
1690
+ */
1691
+ function notStartsWith(column, value) {
1692
+ return { [column]: { $notLike: `${value}%` } };
1693
+ }
1694
+ /**
1695
+ * Helper to create endsWith condition
1696
+ * Creates a LIKE '%value' pattern
1697
+ *
1698
+ * @example
1699
+ * ```typescript
1700
+ * // Find users whose email ends with '@gmail.com'
1701
+ * User.findAll({ where: endsWith('email', '@gmail.com') })
1702
+ * // SQL: WHERE email LIKE '%@gmail.com'
1703
+ * ```
1704
+ */
1705
+ function endsWith(column, value) {
1706
+ return { [column]: { $like: `%${value}` } };
1707
+ }
1708
+ /**
1709
+ * Helper to create notEndsWith condition
1710
+ * Creates a NOT LIKE '%value' pattern
1711
+ */
1712
+ function notEndsWith(column, value) {
1713
+ return { [column]: { $notLike: `%${value}` } };
1714
+ }
1715
+ /**
1716
+ * Helper to create substring/contains condition
1717
+ * Creates a LIKE '%value%' pattern
1718
+ *
1719
+ * @example
1720
+ * ```typescript
1721
+ * // Find users whose name contains 'john'
1722
+ * User.findAll({ where: substring('name', 'john') })
1723
+ * // SQL: WHERE name LIKE '%john%'
1724
+ * ```
1725
+ */
1726
+ function substring(column, value) {
1727
+ return { [column]: { $like: `%${value}%` } };
1728
+ }
1729
+ /**
1730
+ * Helper to create notSubstring condition
1731
+ * Creates a NOT LIKE '%value%' pattern
1732
+ */
1733
+ function notSubstring(column, value) {
1734
+ return { [column]: { $notLike: `%${value}%` } };
1735
+ }
1736
+ /**
1737
+ * Helper to create ANY condition for array queries
1738
+ * PostgreSQL: column = ANY(array_column)
1739
+ *
1740
+ * @example
1741
+ * ```typescript
1742
+ * // Find users where role matches any value in the roles array
1743
+ * User.findAll({ where: { roles: { [Op.any]: ['admin', 'moderator'] } } })
1744
+ * ```
1745
+ */
1746
+ function anyOp(column, values) {
1747
+ return where(column, exports.Op.any, values);
1748
+ }
1749
+ /**
1750
+ * Helper to create ALL condition for array queries
1751
+ * PostgreSQL: column = ALL(array_column)
1752
+ *
1753
+ * @example
1754
+ * ```typescript
1755
+ * // Find users where role matches all values in the roles array
1756
+ * User.findAll({ where: { roles: { [Op.all]: ['admin', 'moderator'] } } })
1757
+ * ```
1758
+ */
1759
+ function allOp(column, values) {
1760
+ return where(column, exports.Op.all, values);
1761
+ }
1762
+ /**
1763
+ * Helper to create MATCH condition for full-text search
1764
+ * Database-specific: uses MATCH AGAINST in MySQL, @@ in PostgreSQL
1765
+ *
1766
+ * @example
1767
+ * ```typescript
1768
+ * // Full-text search
1769
+ * User.findAll({ where: match('bio', 'javascript programming') })
1770
+ * // MySQL: WHERE MATCH(bio) AGAINST('javascript programming')
1771
+ * // PostgreSQL: WHERE bio @@ to_tsquery('javascript')
1772
+ * ```
1773
+ */
1774
+ function match(column, pattern) {
1775
+ return { [column]: { $match: pattern } };
1776
+ }
1777
+ // ==================== Order Helper Functions ====================
1778
+ /**
1779
+ * Sort direction for `ORDER BY` clauses.
1780
+ *
1781
+ * Companion enum for the string-literal union that `OrderExpression.direction`
1782
+ * (and other order-direction fields across the public API) accepts. Passing the raw
1783
+ * string literals `'ASC'` / `'DESC'` continues to work unchanged — the enum is purely
1784
+ * an additive, opt-in convenience. Re-exported here from `./types` so it can be
1785
+ * imported from either module.
1786
+ *
1787
+ * @example
1788
+ * ```typescript
1789
+ * // Using raw string literals (still supported)
1790
+ * User.findAll({ order: [['name', 'ASC']] });
1791
+ *
1792
+ * // Using the SortDirection enum
1793
+ * import { SortDirection } from 'prorm';
1794
+ * User.findAll({ order: [['name', SortDirection.ASC]] });
1795
+ * ```
1796
+ */
1797
+ var types_1 = require("./types");
1798
+ Object.defineProperty(exports, "SortDirection", { enumerable: true, get: function () { return types_1.SortDirection; } });
1799
+ /**
1800
+ * Check if a value is an order expression
1801
+ */
1802
+ function isOrderExpression(value) {
1803
+ return value && value.__type === 'order';
1804
+ }
1805
+ /**
1806
+ * Create an ascending order expression
1807
+ * SQL: ORDER BY field ASC
1808
+ *
1809
+ * @example
1810
+ * ```typescript
1811
+ * // Order by name ascending
1812
+ * User.findAll({ order: [asc('name')] })
1813
+ * // SQL: ORDER BY name ASC
1814
+ *
1815
+ * // Multiple order conditions
1816
+ * User.findAll({ order: [asc('name'), desc('createdAt')] })
1817
+ *
1818
+ * // Equivalent using the SortDirection enum directly (no helper function)
1819
+ * import { SortDirection } from 'prorm';
1820
+ * User.findAll({ order: [['name', SortDirection.ASC]] })
1821
+ * ```
1822
+ */
1823
+ function asc(field) {
1824
+ return {
1825
+ __type: 'order',
1826
+ field,
1827
+ direction: 'ASC',
1828
+ };
1829
+ }
1830
+ /**
1831
+ * Create a descending order expression
1832
+ * SQL: ORDER BY field DESC
1833
+ *
1834
+ * @example
1835
+ * ```typescript
1836
+ * // Order by createdAt descending (newest first)
1837
+ * User.findAll({ order: [desc('createdAt')] })
1838
+ * // SQL: ORDER BY createdAt DESC
1839
+ *
1840
+ * // Combined with asc
1841
+ * User.findAll({ order: [asc('name'), desc('createdAt')] })
1842
+ * ```
1843
+ */
1844
+ function desc(field) {
1845
+ return {
1846
+ __type: 'order',
1847
+ field,
1848
+ direction: 'DESC',
1849
+ };
1850
+ }
1851
+ /**
1852
+ * Create a random order expression
1853
+ * SQL: ORDER BY RANDOM() (SQLite/PostgreSQL) or ORDER BY RAND() (MySQL)
1854
+ *
1855
+ * @example
1856
+ * ```typescript
1857
+ * // Random ordering (useful for sampling)
1858
+ * User.findAll({ order: [random()] })
1859
+ * // SQLite: ORDER BY RANDOM()
1860
+ * // MySQL: ORDER BY RAND()
1861
+ * // PostgreSQL: ORDER BY RANDOM()
1862
+ *
1863
+ * // With limit for random sample
1864
+ * User.findAll({ order: [random()], limit: 5 })
1865
+ * ```
1866
+ */
1867
+ function random() {
1868
+ return {
1869
+ __type: 'order',
1870
+ field: '',
1871
+ direction: 'RANDOM',
1872
+ };
1873
+ }
1874
+ /**
1875
+ * Create a full-text search MATCH AGAINST condition (MySQL)
1876
+ * SQL: MATCH(columns) AGAINST(searchTerm [IN NATURAL LANGUAGE MODE | IN BOOLEAN MODE])
1877
+ *
1878
+ * @example
1879
+ * ```typescript
1880
+ * // Natural language mode search
1881
+ * import { matchAgainst } from 'orm';
1882
+ * Article.findAll({
1883
+ * where: {
1884
+ * [matchAgainst(['title', 'body'])]: 'database'
1885
+ * }
1886
+ * })
1887
+ * // SQL: WHERE MATCH(title, body) AGAINST('database' IN NATURAL LANGUAGE MODE)
1888
+ *
1889
+ * // Boolean mode search
1890
+ * Article.findAll({
1891
+ * where: {
1892
+ * [matchAgainst(['title', 'body'], { mode: 'boolean' })]: '+mysql -oracle'
1893
+ * }
1894
+ * })
1895
+ * // SQL: WHERE MATCH(title, body) AGAINST('+mysql -oracle' IN BOOLEAN MODE)
1896
+ * ```
1897
+ */
1898
+ function matchAgainst(columns, options) {
1899
+ const cols = Array.isArray(columns) ? columns : [columns];
1900
+ return {
1901
+ $match: {
1902
+ columns: cols,
1903
+ mode: options?.mode || 'natural',
1904
+ },
1905
+ };
1906
+ }
1907
+ /**
1908
+ * Create an explicit full-text search condition (alias for natural language mode)
1909
+ * SQL: MATCH(columns) AGAINST(searchTerm IN NATURAL LANGUAGE MODE)
1910
+ *
1911
+ * @example
1912
+ * ```typescript
1913
+ * import { matchFulltext } from 'orm';
1914
+ * Article.findAll({
1915
+ * where: {
1916
+ * [matchFulltext(['title', 'body'])]: 'database'
1917
+ * }
1918
+ * })
1919
+ * // SQL: WHERE MATCH(title, body) AGAINST('database' IN NATURAL LANGUAGE MODE)
1920
+ * ```
1921
+ */
1922
+ function matchFulltext(columns) {
1923
+ const cols = Array.isArray(columns) ? columns : [columns];
1924
+ return {
1925
+ $match: {
1926
+ columns: cols,
1927
+ mode: 'natural',
1928
+ },
1929
+ };
1930
+ }
1931
+ /**
1932
+ * Create a PostgreSQL to_tsvector expression
1933
+ * SQL: to_tsvector(config, column)
1934
+ *
1935
+ * @example
1936
+ * ```typescript
1937
+ * import { toTsvector } from 'orm';
1938
+ * // Using with to_tsquery
1939
+ * Article.findAll({
1940
+ * where: {
1941
+ * [toTsvector('title')]: { $tsquery: 'database' }
1942
+ * }
1943
+ * })
1944
+ * // SQL: WHERE to_tsvector('english', title) @@ to_tsquery('english', 'database')
1945
+ * ```
1946
+ */
1947
+ function toTsvector(column, config) {
1948
+ return {
1949
+ $tsvector: {
1950
+ column,
1951
+ config: config || 'english',
1952
+ },
1953
+ };
1954
+ }
1955
+ /**
1956
+ * Create a PostgreSQL to_tsquery expression
1957
+ * SQL: to_tsquery(config, query)
1958
+ *
1959
+ * @example
1960
+ * ```typescript
1961
+ * import { toTsquery } from 'orm';
1962
+ * Article.findAll({
1963
+ * where: {
1964
+ * body: { [toTsquery('database')]: true }
1965
+ * }
1966
+ * })
1967
+ * // SQL: WHERE body @@ to_tsquery('english', 'database')
1968
+ * ```
1969
+ */
1970
+ function toTsquery(query, config) {
1971
+ return {
1972
+ $tsquery: {
1973
+ query,
1974
+ config: config || 'english',
1975
+ },
1976
+ };
1977
+ }
1978
+ /**
1979
+ * Create a PostgreSQL plainto_tsquery expression (for phrase searching)
1980
+ * SQL: plainto_tsquery(config, query)
1981
+ *
1982
+ * @example
1983
+ * ```typescript
1984
+ * import { plainToTsquery } from 'orm';
1985
+ * Article.findAll({
1986
+ * where: {
1987
+ * body: { [plainToTsquery('database systems')]: true }
1988
+ * }
1989
+ * })
1990
+ * // SQL: WHERE body @@ plainto_tsquery('english', 'database systems')
1991
+ * ```
1992
+ */
1993
+ function plainToTsquery(query, config) {
1994
+ return {
1995
+ $plainTsquery: {
1996
+ query,
1997
+ config: config || 'english',
1998
+ },
1999
+ };
2000
+ }
2001
+ /**
2002
+ * Create a PostgreSQL phraseto_tsquery expression (for exact phrase matching)
2003
+ * SQL: phraseto_tsquery(config, query)
2004
+ *
2005
+ * @example
2006
+ * ```typescript
2007
+ * import { phraseToTsquery } from 'orm';
2008
+ * Article.findAll({
2009
+ * where: {
2010
+ * body: { [phraseToTsquery('database')]: true }
2011
+ * }
2012
+ * })
2013
+ * // SQL: WHERE body @@ phraseto_tsquery('english', 'database')
2014
+ * ```
2015
+ */
2016
+ function phraseToTsquery(query, config) {
2017
+ return {
2018
+ $phraseTsquery: {
2019
+ query,
2020
+ config: config || 'english',
2021
+ },
2022
+ };
2023
+ }
2024
+ // Default export
2025
+ exports.default = exports.Op;
2026
+ /**
2027
+ * Helper to create CONTAINED condition (for JSON/arrays)
2028
+ * Checks if the JSON column is contained in the specified value
2029
+ */
2030
+ function contained(column, value) {
2031
+ return where(column, exports.Op.containedBy, value);
2032
+ }
2033
+ /**
2034
+ * Helper to create JSON key access condition
2035
+ * Access a specific key in a JSON column for comparison
2036
+ */
2037
+ function jsonKey(column, path, value) {
2038
+ if (value !== undefined) {
2039
+ return {
2040
+ [column]: {
2041
+ $key: { path, value },
2042
+ },
2043
+ };
2044
+ }
2045
+ return where(column, exports.Op.key, path);
2046
+ }
2047
+ /**
2048
+ * Helper to create JSON path extraction condition
2049
+ * Used for querying JSON/JSONB columns with path-based extraction
2050
+ *
2051
+ * @example
2052
+ * ```typescript
2053
+ * // Simple JSON path query
2054
+ * User.findAll({ where: json('settings', 'theme', 'dark') })
2055
+ * // SQL: WHERE json_extract(settings, '$.theme') = 'dark'
2056
+ *
2057
+ * // Nested path
2058
+ * User.findAll({ where: json('data', 'address.city', 'NYC') })
2059
+ * // SQL: WHERE json_extract(data, '$.address.city') = 'NYC'
2060
+ *
2061
+ * // With dot notation path (auto-prepended with $)
2062
+ * User.findAll({ where: json('preferences', 'notifications.enabled', true) })
2063
+ * // SQL: WHERE json_extract(preferences, '$.notifications.enabled') = true
2064
+ *
2065
+ * // Using with Op.json symbol directly in object
2066
+ * User.findAll({
2067
+ * where: {
2068
+ * settings: { [Op.json('theme')]: 'dark' }
2069
+ * }
2070
+ * })
2071
+ * // SQL: WHERE json_extract(settings, '$.theme') = 'dark'
2072
+ * ```
2073
+ */
2074
+ function json(column, path, value) {
2075
+ // Normalize path: prepend $ if not already present
2076
+ const normalizedPath = path.startsWith('$.') ? path : `$.${path}`;
2077
+ if (value !== undefined) {
2078
+ return {
2079
+ [column]: {
2080
+ $json: { path: normalizedPath, value },
2081
+ },
2082
+ };
2083
+ }
2084
+ // Just return the JSON extraction without comparison
2085
+ return {
2086
+ [column]: {
2087
+ $json: normalizedPath,
2088
+ },
2089
+ };
2090
+ }
2091
+ /**
2092
+ * Helper to create JSON contains condition
2093
+ * Checks if JSON column contains a specific value or object
2094
+ *
2095
+ * @example
2096
+ * ```typescript
2097
+ * // Find users where preferences contains { theme: 'dark' }
2098
+ * User.findAll({ where: { preferences: { [Op.jsonContains]: { theme: 'dark' } } } })
2099
+ * // SQL (PostgreSQL): WHERE preferences @> '{"theme":"dark"}'
2100
+ * // SQL (MySQL): WHERE JSON_CONTAINS(preferences, '{"theme":"dark"}')
2101
+ *
2102
+ * // Check if JSON array contains a value
2103
+ * User.findAll({ where: { tags: { [Op.jsonContains]: 'admin' } } })
2104
+ * ```
2105
+ */
2106
+ function jsonContains(column, value) {
2107
+ return {
2108
+ [column]: {
2109
+ $jsonContains: value,
2110
+ },
2111
+ };
2112
+ }
2113
+ /**
2114
+ * Helper to create JSON has key condition
2115
+ * Checks if JSON column has a specific key
2116
+ *
2117
+ * @example
2118
+ * ```typescript
2119
+ * // Find users where data column has 'role' key
2120
+ * User.findAll({ where: { data: { [Op.jsonHasKey]: 'role' } } })
2121
+ * // SQL (PostgreSQL): WHERE data ? 'role'
2122
+ * // SQL (MySQL): WHERE JSON_CONTAINS_PATH(data, 'one', '$.role')
2123
+ *
2124
+ * // Nested key
2125
+ * User.findAll({ where: { data: { [Op.jsonHasKey]: 'settings.theme' } } })
2126
+ * ```
2127
+ */
2128
+ function jsonHasKey(column, key) {
2129
+ return {
2130
+ [column]: {
2131
+ $jsonHasKey: key,
2132
+ },
2133
+ };
2134
+ }
2135
+ /**
2136
+ * Helper to create JSONB concatenation condition
2137
+ * Concatenates two JSONB values (PostgreSQL || operator)
2138
+ *
2139
+ * @example
2140
+ * ```typescript
2141
+ * // Concatenate JSONB values
2142
+ * User.findAll({ where: jsonConcat('data', { key: 'value' }) })
2143
+ * // SQL: WHERE data || '{"key": "value"}'
2144
+ * ```
2145
+ */
2146
+ function jsonConcat(column, jsonValue) {
2147
+ return {
2148
+ [column]: {
2149
+ $jsonConcat: jsonValue,
2150
+ },
2151
+ };
2152
+ }
2153
+ /**
2154
+ * Helper to create JSONB delete key condition
2155
+ * Deletes a key from JSONB object (PostgreSQL - operator)
2156
+ *
2157
+ * @example
2158
+ * ```typescript
2159
+ * // Delete key from JSONB
2160
+ * User.findAll({ where: jsonDelete('data', 'key') })
2161
+ * // SQL: WHERE data - 'key'
2162
+ * ```
2163
+ */
2164
+ function jsonDelete(column, key) {
2165
+ return {
2166
+ [column]: {
2167
+ $jsonDelete: key,
2168
+ },
2169
+ };
2170
+ }
2171
+ /**
2172
+ * Helper to create JSONB delete by path condition
2173
+ * Deletes a key from JSONB by path (PostgreSQL #- operator)
2174
+ *
2175
+ * @example
2176
+ * ```typescript
2177
+ * // Delete by path
2178
+ * User.findAll({ where: jsonDeletePath('data', ['a', 'b']) })
2179
+ * // SQL: WHERE data #- '{a, b}'
2180
+ * ```
2181
+ */
2182
+ function jsonDeletePath(column, path) {
2183
+ return {
2184
+ [column]: {
2185
+ $jsonDeletePath: path,
2186
+ },
2187
+ };
2188
+ }
2189
+ /**
2190
+ * Helper to create JSON path exists condition
2191
+ * Checks if a JSON path exists and returns boolean (PostgreSQL @? operator)
2192
+ *
2193
+ * @example
2194
+ * ```typescript
2195
+ * // Check if path exists
2196
+ * User.findAll({ where: jsonPathExists('data', '$.key') })
2197
+ * // SQL: WHERE data @? '$.key'
2198
+ * ```
2199
+ */
2200
+ function jsonPathExists(column, path) {
2201
+ return {
2202
+ [column]: {
2203
+ $jsonPathExists: path,
2204
+ },
2205
+ };
2206
+ }
2207
+ /**
2208
+ * Helper to create JSON path query condition
2209
+ * Evaluates JSON path and returns result (PostgreSQL @@ operator)
2210
+ *
2211
+ * @example
2212
+ * ```typescript
2213
+ * // Query JSON path
2214
+ * User.findAll({ where: jsonPathQuery('data', '$.key') })
2215
+ * // SQL: WHERE data @@ '$.key'
2216
+ * ```
2217
+ */
2218
+ function jsonPathQuery(column, path) {
2219
+ return {
2220
+ [column]: {
2221
+ $jsonPathQuery: path,
2222
+ },
2223
+ };
2224
+ }
2225
+ /**
2226
+ * Create a SQL function expression
2227
+ * @param fnName - The SQL function name (e.g., 'COUNT', 'SUM', 'AVG', 'MAX', 'MIN')
2228
+ * @param args - Function arguments (can include col() expressions)
2229
+ */
2230
+ function fn(fnName, ...args) {
2231
+ return {
2232
+ __type: 'fn',
2233
+ fn: fnName.toUpperCase(),
2234
+ args,
2235
+ };
2236
+ }
2237
+ /**
2238
+ * Create a column reference for use in SQL functions
2239
+ * @param colName - The column name (supports 'table.column' format)
2240
+ *
2241
+ * @example
2242
+ * ```typescript
2243
+ * col('id') // -> "id"
2244
+ * col('user.id') // -> "user.id"
2245
+ * col('users.name') // -> "users.name"
2246
+ * ```
2247
+ */
2248
+ function col(colName) {
2249
+ return {
2250
+ __type: 'col',
2251
+ col: colName,
2252
+ };
2253
+ }
2254
+ /**
2255
+ * Create a literal SQL expression (inserted as-is into the query)
2256
+ * Returns { $literal: value } format for raw SQL expressions
2257
+ * Use with caution - this can lead to SQL injection if user input is included
2258
+ *
2259
+ * @example
2260
+ * ```typescript
2261
+ * // Use with update to set current timestamp
2262
+ * User.update({ lastLogin: literal('NOW()') }, { where: { ... } })
2263
+ *
2264
+ * // Use in where clause to compare with current time
2265
+ * User.findAll({
2266
+ * where: {
2267
+ * createdAt: { [Op.lt]: literal('NOW()') }
2268
+ * }
2269
+ * })
2270
+ *
2271
+ * // Compare column to a literal value
2272
+ * User.findAll({
2273
+ * where: {
2274
+ * updatedAt: { [Op.gt]: literal('createdAt') }
2275
+ * }
2276
+ * })
2277
+ * ```
2278
+ */
2279
+ function literal(value) {
2280
+ return { $literal: value };
2281
+ }
2282
+ /**
2283
+ * Create a literal SQL expression (inserted as-is into the query)
2284
+ * Returns { __type: 'literal', sql: string } format
2285
+ * Use with caution - this can lead to SQL injection if user input is included
2286
+ *
2287
+ * @example
2288
+ * ```typescript
2289
+ * literalExpr('COUNT(*)') // -> { __type: 'literal', sql: 'COUNT(*)' }
2290
+ * literalExpr('NOW()') // -> { __type: 'literal', sql: 'NOW()' }
2291
+ * literalExpr('1 + 1') // -> { __type: 'literal', sql: '1 + 1' }
2292
+ * ```
2293
+ */
2294
+ function literalExpr(sql) {
2295
+ return {
2296
+ __type: 'literal',
2297
+ sql,
2298
+ };
2299
+ }
2300
+ /**
2301
+ * Check if a value is a function expression
2302
+ */
2303
+ function isFnExpression(value) {
2304
+ return value && value.__type === 'fn';
2305
+ }
2306
+ /**
2307
+ * Check if a value is a column expression
2308
+ */
2309
+ function isColExpression(value) {
2310
+ return value && value.__type === 'col';
2311
+ }
2312
+ /**
2313
+ * Check if a value is a literal expression
2314
+ * Supports both { $literal: value } and { __type: 'literal', sql } formats
2315
+ */
2316
+ function isLiteralExpression(value) {
2317
+ return value && (value.__type === 'literal' || '$literal' in value);
2318
+ }
2319
+ /**
2320
+ * Check if a value is a raw SQL literal ({ $literal: value } format)
2321
+ */
2322
+ function isRawLiteral(value) {
2323
+ return (value !== null &&
2324
+ typeof value === 'object' &&
2325
+ '$literal' in value &&
2326
+ typeof value.$literal === 'string');
2327
+ }
2328
+ /**
2329
+ * Check if a value is a CAST expression
2330
+ */
2331
+ function isCastExpression(value) {
2332
+ return value && value.__type === 'cast';
2333
+ }
2334
+ /**
2335
+ * Check if a value is an EXTRACT expression
2336
+ */
2337
+ function isExtractExpression(value) {
2338
+ return value && value.__type === 'extract';
2339
+ }
2340
+ /**
2341
+ * Check if a value is a CONVERT expression
2342
+ */
2343
+ function isConvExpression(value) {
2344
+ return value && value.__type === 'conv';
2345
+ }
2346
+ /**
2347
+ * Create a CAST expression for type casting
2348
+ * SQL: CAST(value AS type)
2349
+ *
2350
+ * @example
2351
+ * ```typescript
2352
+ * // CAST('2023-01-01' AS DATE)
2353
+ * User.findAll({
2354
+ * attributes: [[cast('2023-01-01', 'DATE'), 'dateOnly']]
2355
+ * })
2356
+ *
2357
+ * // CAST(column AS INTEGER)
2358
+ * User.findAll({
2359
+ * attributes: [[cast(col('amount'), 'INTEGER'), 'amountInt']]
2360
+ * })
2361
+ *
2362
+ * // CAST with different types
2363
+ * cast(col('price'), 'DECIMAL(10,2)')
2364
+ * cast(col('text'), 'CHAR(100)')
2365
+ * cast(col('json_data'), 'JSON')
2366
+ * ```
2367
+ */
2368
+ function cast(value, type) {
2369
+ return {
2370
+ __type: 'cast',
2371
+ value,
2372
+ type: type.toUpperCase(),
2373
+ };
2374
+ }
2375
+ /**
2376
+ * Type coercion helpers for common SQL types
2377
+ * Provides convenience methods for casting values to specific types
2378
+ *
2379
+ * @example
2380
+ * ```typescript
2381
+ * import { Cast, col } from 'orm';
2382
+ *
2383
+ * // Cast.string - CAST(value AS VARCHAR)
2384
+ * User.findAll({
2385
+ * attributes: [[Cast.string(col('age')), 'ageStr']]
2386
+ * })
2387
+ *
2388
+ * // Cast.integer - CAST(value AS INTEGER)
2389
+ * User.findAll({
2390
+ * attributes: [[Cast.integer(col('price')), 'priceInt']]
2391
+ * })
2392
+ *
2393
+ * // Cast.date - CAST(value AS DATE)
2394
+ * User.findAll({
2395
+ * attributes: [[Cast.date(col('timestamp')), 'dateOnly']]
2396
+ * })
2397
+ *
2398
+ * // Cast.boolean - CAST(value AS BOOLEAN)
2399
+ * User.findAll({
2400
+ * attributes: [[Cast.boolean(col('flag')), 'flagBool']]
2401
+ * })
2402
+ *
2403
+ * // Cast.float - CAST(value AS FLOAT)
2404
+ * User.findAll({
2405
+ * attributes: [[Cast.float(col('amount')), 'amountFloat']]
2406
+ * })
2407
+ *
2408
+ * // Cast.decimal - CAST(value AS DECIMAL)
2409
+ * User.findAll({
2410
+ * attributes: [[Cast.decimal(col('price'), 10, 2), 'priceDecimal']]
2411
+ * })
2412
+ *
2413
+ * // Cast.json - CAST(value AS JSON)
2414
+ * User.findAll({
2415
+ * attributes: [[Cast.json(col('data')), 'dataJson']]
2416
+ * })
2417
+ * ```
2418
+ */
2419
+ exports.Cast = {
2420
+ /**
2421
+ * Cast to VARCHAR/string type
2422
+ */
2423
+ string(value) {
2424
+ return cast(value, 'VARCHAR');
2425
+ },
2426
+ /**
2427
+ * Cast to CHAR type
2428
+ */
2429
+ char(value, length) {
2430
+ return cast(value, length ? `CHAR(${length})` : 'CHAR');
2431
+ },
2432
+ /**
2433
+ * Cast to TEXT type
2434
+ */
2435
+ text(value) {
2436
+ return cast(value, 'TEXT');
2437
+ },
2438
+ /**
2439
+ * Cast to INTEGER type
2440
+ */
2441
+ integer(value) {
2442
+ return cast(value, 'INTEGER');
2443
+ },
2444
+ /**
2445
+ * Cast to BIGINT type
2446
+ */
2447
+ bigint(value) {
2448
+ return cast(value, 'BIGINT');
2449
+ },
2450
+ /**
2451
+ * Cast to FLOAT type
2452
+ */
2453
+ float(value) {
2454
+ return cast(value, 'FLOAT');
2455
+ },
2456
+ /**
2457
+ * Cast to DOUBLE type
2458
+ */
2459
+ double(value) {
2460
+ return cast(value, 'DOUBLE');
2461
+ },
2462
+ /**
2463
+ * Cast to DECIMAL type
2464
+ */
2465
+ decimal(value, precision, scale) {
2466
+ const type = precision ? `DECIMAL(${precision}${scale ? ', ' + scale : ''})` : 'DECIMAL';
2467
+ return cast(value, type);
2468
+ },
2469
+ /**
2470
+ * Cast to BOOLEAN type
2471
+ */
2472
+ boolean(value) {
2473
+ return cast(value, 'BOOLEAN');
2474
+ },
2475
+ /**
2476
+ * Cast to DATE type
2477
+ */
2478
+ date(value) {
2479
+ return cast(value, 'DATE');
2480
+ },
2481
+ /**
2482
+ * Cast to DATETIME type
2483
+ */
2484
+ datetime(value) {
2485
+ return cast(value, 'DATETIME');
2486
+ },
2487
+ /**
2488
+ * Cast to TIME type
2489
+ */
2490
+ time(value) {
2491
+ return cast(value, 'TIME');
2492
+ },
2493
+ /**
2494
+ * Cast to TIMESTAMP type
2495
+ */
2496
+ timestamp(value) {
2497
+ return cast(value, 'TIMESTAMP');
2498
+ },
2499
+ /**
2500
+ * Cast to JSON type
2501
+ */
2502
+ json(value) {
2503
+ return cast(value, 'JSON');
2504
+ },
2505
+ /**
2506
+ * Cast to BLOB type
2507
+ */
2508
+ blob(value) {
2509
+ return cast(value, 'BLOB');
2510
+ },
2511
+ /**
2512
+ * Cast to UUID type
2513
+ */
2514
+ uuid(value) {
2515
+ return cast(value, 'UUID');
2516
+ },
2517
+ /**
2518
+ * Cast to specific type
2519
+ */
2520
+ valueOf(value, type) {
2521
+ return cast(value, type);
2522
+ },
2523
+ };
2524
+ /**
2525
+ * Create an EXTRACT expression for extracting date parts
2526
+ * SQL: EXTRACT(part FROM field)
2527
+ *
2528
+ * @example
2529
+ * ```typescript
2530
+ * // EXTRACT(YEAR FROM createdAt)
2531
+ * User.findAll({
2532
+ * attributes: [[extract('createdAt', 'year'), 'year']]
2533
+ * })
2534
+ *
2535
+ * // EXTRACT(MONTH FROM order_date)
2536
+ * User.findAll({
2537
+ * attributes: [[extract(col('order_date'), 'month'), 'month']]
2538
+ * })
2539
+ *
2540
+ * // Use in WHERE clause
2541
+ * User.findAll({
2542
+ * where: {
2543
+ * [extract('createdAt', 'year')]: 2024
2544
+ * }
2545
+ * })
2546
+ *
2547
+ * // Available parts: year, month, day, hour, minute, second
2548
+ * ```
2549
+ */
2550
+ function extract(field, part) {
2551
+ return {
2552
+ __type: 'extract',
2553
+ field,
2554
+ part: part.toLowerCase(),
2555
+ };
2556
+ }
2557
+ /**
2558
+ * Create a CONVERT expression for type conversion
2559
+ * SQL: CONVERT(value, type) for MySQL, or CAST for other dialects
2560
+ *
2561
+ * @example
2562
+ * ```typescript
2563
+ * // CONVERT(value, to_type)
2564
+ * User.findAll({
2565
+ * attributes: [[conv(col('value'), null, 'CHAR'), 'strValue']]
2566
+ * })
2567
+ *
2568
+ * // CONVERT with charset (MySQL)
2569
+ * conv(col('name'), null, 'CHAR(255) CHARACTER SET utf8mb4')
2570
+ *
2571
+ * // Base type conversion
2572
+ * conv(col('text'), 'CHAR', 'INTEGER')
2573
+ * ```
2574
+ */
2575
+ function conv(value, from, to) {
2576
+ return {
2577
+ __type: 'conv',
2578
+ value,
2579
+ from: from ?? null,
2580
+ to: to.toUpperCase(),
2581
+ };
2582
+ }
2583
+ // ==================== Aggregate Function Helpers ====================
2584
+ /**
2585
+ * Create a SUM aggregate function expression
2586
+ * SQL: SUM(column)
2587
+ *
2588
+ * @example
2589
+ * ```typescript
2590
+ * // Sum all scores
2591
+ * User.findAll({
2592
+ * attributes: [[SUM(col('score')), 'totalScore']]
2593
+ * })
2594
+ *
2595
+ * // Sum with condition
2596
+ * User.findAll({
2597
+ * attributes: [[SUM(col('amount')), 'totalAmount']],
2598
+ * where: { status: 'completed' }
2599
+ * })
2600
+ * ```
2601
+ */
2602
+ function SUM(column) {
2603
+ return fn('SUM', column);
2604
+ }
2605
+ /**
2606
+ * Create an AVG aggregate function expression
2607
+ * SQL: AVG(column)
2608
+ *
2609
+ * @example
2610
+ * ```typescript
2611
+ * // Average score
2612
+ * User.findAll({
2613
+ * attributes: [[AVG(col('score')), 'averageScore']]
2614
+ * })
2615
+ *
2616
+ * // Average with group by
2617
+ * User.findAll({
2618
+ * attributes: ['department', [AVG(col('salary')), 'avgSalary']],
2619
+ * group: ['department']
2620
+ * })
2621
+ * ```
2622
+ */
2623
+ function AVG(column) {
2624
+ return fn('AVG', column);
2625
+ }
2626
+ /**
2627
+ * Create a COUNT aggregate function expression
2628
+ * SQL: COUNT(column)
2629
+ *
2630
+ * @example
2631
+ * ```typescript
2632
+ * // Count all records
2633
+ * User.findAll({
2634
+ * attributes: [[COUNT(col('id')), 'totalCount']]
2635
+ * })
2636
+ *
2637
+ * // Count distinct values
2638
+ * User.findAll({
2639
+ * attributes: [[COUNT(col('email')), 'uniqueEmails']]
2640
+ * })
2641
+ *
2642
+ * // Count with DISTINCT
2643
+ * User.findAll({
2644
+ * attributes: [[COUNT(fn('DISTINCT', col('role'))), 'uniqueRoles']]
2645
+ * })
2646
+ * ```
2647
+ */
2648
+ function COUNT(column) {
2649
+ return fn('COUNT', column);
2650
+ }
2651
+ /**
2652
+ * Create a MAX aggregate function expression
2653
+ * SQL: MAX(column)
2654
+ *
2655
+ * @example
2656
+ * ```typescript
2657
+ * // Maximum score
2658
+ * User.findAll({
2659
+ * attributes: [[MAX(col('score')), 'highestScore']]
2660
+ * })
2661
+ *
2662
+ * // Latest date
2663
+ * User.findAll({
2664
+ * attributes: [[MAX(col('createdAt')), 'latestDate']]
2665
+ * })
2666
+ * ```
2667
+ */
2668
+ function MAX(column) {
2669
+ return fn('MAX', column);
2670
+ }
2671
+ /**
2672
+ * Create a MIN aggregate function expression
2673
+ * SQL: MIN(column)
2674
+ *
2675
+ * @example
2676
+ * ```typescript
2677
+ * // Minimum score
2678
+ * User.findAll({
2679
+ * attributes: [[MIN(col('score')), 'lowestScore']]
2680
+ * })
2681
+ *
2682
+ * // Earliest date
2683
+ * User.findAll({
2684
+ * attributes: [[MIN(col('createdAt')), 'earliestDate']]
2685
+ * })
2686
+ * ```
2687
+ */
2688
+ function MIN(column) {
2689
+ return fn('MIN', column);
2690
+ }