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,435 @@
1
+ "use strict";
2
+ /**
3
+ * @Check decorator for the ORM — generates CHECK constraints without writing raw SQL.
4
+ *
5
+ * Supports ORM-native operator syntax that gets converted to SQL CHECK expressions.
6
+ *
7
+ * Usage Examples:
8
+ *
9
+ * // Column-level check with ORM operators
10
+ * @Check({ age: { $gte: 18 } })
11
+ * age: number;
12
+ *
13
+ * // Multiple column checks
14
+ * @Check({
15
+ * status: { $in: ['active', 'pending', 'draft'] },
16
+ * email: { $regex: '^[^@]+@[^@]+$' }
17
+ * })
18
+ * email: string;
19
+ *
20
+ * // Custom condition with message
21
+ * @Check({
22
+ * condition: (row) => row.startDate < row.endDate,
23
+ * message: 'Start date must be before end date'
24
+ * })
25
+ * startDate: Date;
26
+ *
27
+ * // Column-level check (explicit column)
28
+ * @CheckColumn('price', { $gte: 0 }, 'Price must be positive')
29
+ * @CheckColumn('quantity', { $gt: 0 }, 'Quantity must be greater than zero')
30
+ *
31
+ * // Table-level check (multiple columns)
32
+ * @CheckTable({
33
+ * condition: (row) => row.quantity * row.price >= row.minimumOrder,
34
+ * message: 'Total must meet minimum order amount'
35
+ * })
36
+ */
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.operatorToSQL = operatorToSQL;
39
+ exports.Check = Check;
40
+ exports.CheckColumn = CheckColumn;
41
+ exports.CheckTable = CheckTable;
42
+ exports.getCheckConstraints = getCheckConstraints;
43
+ exports.getTableCheckConstraints = getTableCheckConstraints;
44
+ exports.getAllCheckConstraints = getAllCheckConstraints;
45
+ exports.buildCheckConstraintSQL = buildCheckConstraintSQL;
46
+ exports.buildAllCheckConstraints = buildAllCheckConstraints;
47
+ exports.clearCheckConstraints = clearCheckConstraints;
48
+ // ==================== Metadata Keys ====================
49
+ const CHECK_CONSTRAINTS_KEY = '__orm_check_constraints__';
50
+ const TABLE_CHECK_CONSTRAINTS_KEY = '__orm_table_check_constraints__';
51
+ // ==================== SQL Generation ====================
52
+ /**
53
+ * Escape SQL string value for use in CHECK constraints
54
+ */
55
+ function escapeSQLValue(value) {
56
+ if (value === null)
57
+ return 'NULL';
58
+ if (typeof value === 'number')
59
+ return String(value);
60
+ if (typeof value === 'boolean')
61
+ return value ? 'TRUE' : 'FALSE';
62
+ if (typeof value === 'string')
63
+ return `'${value.replace(/'/g, "''")}'`;
64
+ if (Array.isArray(value)) {
65
+ return value.map((v) => escapeSQLValue(v)).join(', ');
66
+ }
67
+ // For objects, serialize as JSON
68
+ return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
69
+ }
70
+ /**
71
+ * Throw if the given operator is used with a non-Postgres dialect.
72
+ * These operators map directly to PostgreSQL jsonb/array operators
73
+ * (@>, <@, &&) that have no CHECK-constraint-safe equivalent elsewhere.
74
+ */
75
+ function assertPostgres(dialect, opName) {
76
+ if (dialect !== 'postgres') {
77
+ throw new Error(`Operator "${opName}" generates PostgreSQL-specific jsonb/array syntax and is not supported for the "${dialect}" dialect.`);
78
+ }
79
+ }
80
+ /**
81
+ * Build a case-insensitive LIKE expression appropriate for the target dialect.
82
+ *
83
+ * - PostgreSQL: native ILIKE / NOT ILIKE
84
+ * - SQLite: LIKE with COLLATE NOCASE (SQLite has no ILIKE keyword)
85
+ * - MySQL/MariaDB: plain LIKE — the default collation (e.g. utf8mb4_general_ci)
86
+ * is already case-insensitive, so no special-casing is required
87
+ * - MSSQL: plain LIKE — default (non-binary) collations are case-insensitive
88
+ * - Oracle: case-sensitive by default, so wrap both sides in UPPER()
89
+ */
90
+ function buildCaseInsensitiveLike(quotedField, value, dialect, negate) {
91
+ const escapedValue = escapeSQLValue(value);
92
+ switch (dialect) {
93
+ case 'postgres':
94
+ return `${quotedField} ${negate ? 'NOT ILIKE' : 'ILIKE'} ${escapedValue}`;
95
+ case 'sqlite':
96
+ return `${quotedField} ${negate ? 'NOT LIKE' : 'LIKE'} ${escapedValue} COLLATE NOCASE`;
97
+ case 'oracle':
98
+ return `UPPER(${quotedField}) ${negate ? 'NOT LIKE' : 'LIKE'} UPPER(${escapedValue})`;
99
+ case 'mysql':
100
+ case 'mariadb':
101
+ case 'mssql':
102
+ default:
103
+ // Case-insensitive by default collation on these dialects
104
+ return `${quotedField} ${negate ? 'NOT LIKE' : 'LIKE'} ${escapedValue}`;
105
+ }
106
+ }
107
+ /**
108
+ * Build a regex-match expression appropriate for the target dialect.
109
+ *
110
+ * - PostgreSQL: ~ / !~ (case-sensitive), ~* / !~* (case-insensitive)
111
+ * - MySQL/MariaDB: REGEXP / NOT REGEXP (MySQL's REGEXP is case-insensitive
112
+ * by default under the standard case-insensitive collations, so it is
113
+ * used for both $regex and $iRegexp)
114
+ * - Oracle: REGEXP_LIKE(field, pattern[, 'i']) function, wrapped with NOT for negation
115
+ * - SQLite: no REGEXP function is registered by this ORM's SQLite dialect
116
+ * (see src/dialects/sqlite/index.ts), so regex CHECK constraints would
117
+ * fail at runtime — throw a clear error instead of emitting invalid SQL
118
+ * - MSSQL: has no native regex support in T-SQL — throw a clear error
119
+ */
120
+ function buildRegexExpression(quotedField, value, dialect, opts) {
121
+ const escapedValue = escapeSQLValue(value);
122
+ const { negate, ci } = opts;
123
+ switch (dialect) {
124
+ case 'postgres': {
125
+ const op = ci ? (negate ? '!~*' : '~*') : negate ? '!~' : '~';
126
+ return `${quotedField} ${op} ${escapedValue}`;
127
+ }
128
+ case 'mysql':
129
+ case 'mariadb': {
130
+ const op = negate ? 'NOT REGEXP' : 'REGEXP';
131
+ return `${quotedField} ${op} ${escapedValue}`;
132
+ }
133
+ case 'oracle': {
134
+ const modifier = ci ? `, 'i'` : '';
135
+ const expr = `REGEXP_LIKE(${quotedField}, ${escapedValue}${modifier})`;
136
+ return negate ? `NOT ${expr}` : expr;
137
+ }
138
+ case 'sqlite':
139
+ throw new Error('Regex CHECK constraints are not supported for the "sqlite" dialect: no REGEXP function is registered.');
140
+ case 'mssql':
141
+ default:
142
+ throw new Error(`Regex CHECK constraints are not supported for the "${dialect}" dialect.`);
143
+ }
144
+ }
145
+ /**
146
+ * Convert ORM operator to SQL CHECK expression
147
+ * @param fieldName - The column name
148
+ * @param operator - The ORM operator object
149
+ * @param dialect - The target SQL dialect (defaults to 'postgres' for backwards compatibility)
150
+ * @returns SQL expression string
151
+ */
152
+ function operatorToSQL(fieldName, operator, dialect = 'postgres') {
153
+ const opKeys = Object.keys(operator);
154
+ const opKey = opKeys[0];
155
+ const value = operator[opKey];
156
+ // Wrap field name in quotes for safety
157
+ const quotedField = `"${fieldName}"`;
158
+ switch (opKey) {
159
+ case '$eq':
160
+ return `${quotedField} = ${escapeSQLValue(value)}`;
161
+ case '$ne':
162
+ return `${quotedField} <> ${escapeSQLValue(value)}`;
163
+ case '$gt':
164
+ return `${quotedField} > ${escapeSQLValue(value)}`;
165
+ case '$gte':
166
+ return `${quotedField} >= ${escapeSQLValue(value)}`;
167
+ case '$lt':
168
+ return `${quotedField} < ${escapeSQLValue(value)}`;
169
+ case '$lte':
170
+ return `${quotedField} <= ${escapeSQLValue(value)}`;
171
+ case '$in':
172
+ if (!Array.isArray(value) || value.length === 0) {
173
+ throw new Error('$in operator requires a non-empty array');
174
+ }
175
+ return `${quotedField} IN (${value.map((v) => escapeSQLValue(v)).join(', ')})`;
176
+ case '$notIn':
177
+ if (!Array.isArray(value) || value.length === 0) {
178
+ throw new Error('$notIn operator requires a non-empty array');
179
+ }
180
+ return `${quotedField} NOT IN (${value.map((v) => escapeSQLValue(v)).join(', ')})`;
181
+ case '$between':
182
+ if (!Array.isArray(value) || value.length !== 2) {
183
+ throw new Error('$between operator requires an array of exactly 2 values');
184
+ }
185
+ return `${quotedField} BETWEEN ${escapeSQLValue(value[0])} AND ${escapeSQLValue(value[1])}`;
186
+ case '$notBetween':
187
+ if (!Array.isArray(value) || value.length !== 2) {
188
+ throw new Error('$notBetween operator requires an array of exactly 2 values');
189
+ }
190
+ return `${quotedField} NOT BETWEEN ${escapeSQLValue(value[0])} AND ${escapeSQLValue(value[1])}`;
191
+ case '$like':
192
+ return `${quotedField} LIKE ${escapeSQLValue(value)}`;
193
+ case '$notLike':
194
+ return `${quotedField} NOT LIKE ${escapeSQLValue(value)}`;
195
+ case '$iLike':
196
+ return buildCaseInsensitiveLike(quotedField, value, dialect, false);
197
+ case '$notILike':
198
+ return buildCaseInsensitiveLike(quotedField, value, dialect, true);
199
+ case '$regex':
200
+ return buildRegexExpression(quotedField, value, dialect, { negate: false, ci: false });
201
+ case '$notRegexp':
202
+ return buildRegexExpression(quotedField, value, dialect, { negate: true, ci: false });
203
+ case '$iRegexp':
204
+ return buildRegexExpression(quotedField, value, dialect, { negate: false, ci: true });
205
+ case '$notIRegexp':
206
+ return buildRegexExpression(quotedField, value, dialect, { negate: true, ci: true });
207
+ case '$isNull':
208
+ return `${quotedField} IS NULL`;
209
+ case '$isNotNull':
210
+ return `${quotedField} IS NOT NULL`;
211
+ case '$contains':
212
+ // PostgreSQL jsonb/array containment operator — no equivalent elsewhere
213
+ assertPostgres(dialect, '$contains');
214
+ return `${quotedField} @> ${escapeSQLValue(value)}`;
215
+ case '$containedBy':
216
+ // PostgreSQL jsonb/array containment operator — no equivalent elsewhere
217
+ assertPostgres(dialect, '$containedBy');
218
+ return `${quotedField} <@ ${escapeSQLValue(value)}`;
219
+ case '$overlap':
220
+ // PostgreSQL array overlap operator — no equivalent elsewhere
221
+ assertPostgres(dialect, '$overlap');
222
+ return `${quotedField} && ${escapeSQLValue(value)}`;
223
+ default:
224
+ throw new Error(`Unsupported operator: ${opKey}`);
225
+ }
226
+ }
227
+ /**
228
+ * Generate a CHECK constraint name
229
+ */
230
+ function generateCheckName(tableName, fieldName) {
231
+ const timestamp = Date.now().toString(36).slice(-6);
232
+ if (fieldName) {
233
+ return `chk_${tableName}_${fieldName}_${timestamp}`;
234
+ }
235
+ return `chk_${tableName}_table_${timestamp}`;
236
+ }
237
+ /**
238
+ * Parse condition function to SQL (simple implementation)
239
+ * This handles common patterns for row-level checks
240
+ */
241
+ function conditionToSQL(condition, paramNames) {
242
+ // Get the function source code
243
+ const funcStr = condition.toString();
244
+ // Extract parameter names if not provided
245
+ const params = paramNames.length > 0 ? paramNames : ['row'];
246
+ // Simple pattern matching for common expressions
247
+ // This is a basic implementation - complex conditions may need manual SQL
248
+ // Handle simple comparisons: row.a < row.b
249
+ const simpleCompare = funcStr.match(/row\.(\w+)\s*([<>]=?)\s*row\.(\w+)/);
250
+ if (simpleCompare) {
251
+ return `"${simpleCompare[1]}" ${simpleCompare[2]} "${simpleCompare[3]}"`;
252
+ }
253
+ // Handle arithmetic: row.a * row.b >= row.c
254
+ const arithmeticCompare = funcStr.match(/row\.(\w+)\s*([*\/+-])\s*row\.(\w+)\s*(>=?|<=?|==|!=)\s*row\.(\w+)/);
255
+ if (arithmeticCompare) {
256
+ return `"${arithmeticCompare[1]}" ${arithmeticCompare[2]} "${arithmeticCompare[3]}" ${arithmeticCompare[4]} "${arithmeticCompare[5]}"`;
257
+ }
258
+ // Handle: row.field < value (constant)
259
+ const fieldValueCompare = funcStr.match(/row\.(\w+)\s*(>=?|<=?|==|!=)\s*(\d+)/);
260
+ if (fieldValueCompare) {
261
+ return `"${fieldValueCompare[1]}" ${fieldValueCompare[2]} ${fieldValueCompare[3]}`;
262
+ }
263
+ // Handle: value < row.field (constant)
264
+ const valueFieldCompare = funcStr.match(/(\d+)\s*(>=?|<=?|==|!=)\s*row\.(\w+)/);
265
+ if (valueFieldCompare) {
266
+ return `${valueFieldCompare[1]} ${valueFieldCompare[2]} "${valueFieldCompare[3]}"`;
267
+ }
268
+ // Fallback: for complex conditions, throw an error suggesting manual SQL
269
+ throw new Error('Complex CHECK conditions require manual SQL. Use @ColumnConstraint with type: "check" and explicit check string.');
270
+ }
271
+ // ==================== Decorators ====================
272
+ /**
273
+ * @Check decorator — attach CHECK constraints to a column or multiple columns.
274
+ *
275
+ * For column-level use (on class property):
276
+ * @Check({ age: { $gte: 18 } })
277
+ * age: number;
278
+ *
279
+ * For multiple checks:
280
+ * @Check({
281
+ * status: { $in: ['active', 'pending'] },
282
+ * email: { $regex: '^[^@]+@[^@]+$' }
283
+ * })
284
+ * email: string;
285
+ *
286
+ * For row-level condition:
287
+ * @Check({
288
+ * condition: (row) => row.startDate < row.endDate,
289
+ * message: 'Start date must be before end date'
290
+ * })
291
+ * startDate: Date;
292
+ */
293
+ function Check(options) {
294
+ return (target, propertyKey) => {
295
+ const fieldName = String(propertyKey);
296
+ if (!target[CHECK_CONSTRAINTS_KEY]) {
297
+ target[CHECK_CONSTRAINTS_KEY] = [];
298
+ }
299
+ const constraints = target[CHECK_CONSTRAINTS_KEY];
300
+ // Check if this is a row-level condition (condition function)
301
+ if ('condition' in options) {
302
+ const rowCheck = options;
303
+ constraints.push({
304
+ type: 'table',
305
+ name: '',
306
+ condition: rowCheck.condition,
307
+ message: rowCheck.message,
308
+ });
309
+ }
310
+ else {
311
+ // Field-level checks
312
+ for (const [field, operator] of Object.entries(options)) {
313
+ if ((typeof operator === 'object' && '$eq' in operator) || '$gte' in operator) {
314
+ // Skip if it's a special property, not a field check
315
+ if (field === 'condition' || field === 'message')
316
+ continue;
317
+ }
318
+ if (typeof operator === 'object' && operator !== null) {
319
+ constraints.push({
320
+ type: 'column',
321
+ name: '',
322
+ field: field,
323
+ operator: operator,
324
+ });
325
+ }
326
+ }
327
+ }
328
+ };
329
+ }
330
+ /**
331
+ * @CheckColumn decorator — explicit column-level CHECK constraint.
332
+ *
333
+ * @CheckColumn('price', { $gte: 0 }, 'Price must be positive')
334
+ * price: number;
335
+ */
336
+ function CheckColumn(column, operator, message) {
337
+ return (target, propertyKey) => {
338
+ if (!target[CHECK_CONSTRAINTS_KEY]) {
339
+ target[CHECK_CONSTRAINTS_KEY] = [];
340
+ }
341
+ target[CHECK_CONSTRAINTS_KEY].push({
342
+ type: 'column',
343
+ name: '',
344
+ field: column,
345
+ operator,
346
+ message,
347
+ });
348
+ };
349
+ }
350
+ /**
351
+ * @CheckTable decorator — table-level CHECK constraint with multi-column conditions.
352
+ *
353
+ * @CheckTable({
354
+ * condition: (row) => row.quantity * row.price >= row.minimumOrder,
355
+ * message: 'Total must meet minimum order amount'
356
+ * })
357
+ */
358
+ function CheckTable(options) {
359
+ return (target) => {
360
+ if (!target[TABLE_CHECK_CONSTRAINTS_KEY]) {
361
+ target[TABLE_CHECK_CONSTRAINTS_KEY] = [];
362
+ }
363
+ target[TABLE_CHECK_CONSTRAINTS_KEY].push({
364
+ type: 'table',
365
+ name: options.name || '',
366
+ condition: options.condition,
367
+ message: options.message,
368
+ });
369
+ };
370
+ }
371
+ // ==================== Helpers ====================
372
+ /**
373
+ * Get all CHECK constraints from a model class or instance
374
+ */
375
+ function getCheckConstraints(target) {
376
+ return target[CHECK_CONSTRAINTS_KEY] || [];
377
+ }
378
+ /**
379
+ * Get all table-level CHECK constraints from a model class
380
+ */
381
+ function getTableCheckConstraints(target) {
382
+ return target[TABLE_CHECK_CONSTRAINTS_KEY] || [];
383
+ }
384
+ /**
385
+ * Get all CHECK constraints (both column and table level)
386
+ */
387
+ function getAllCheckConstraints(target) {
388
+ return [...getCheckConstraints(target), ...getTableCheckConstraints(target)];
389
+ }
390
+ /**
391
+ * Build CREATE TABLE CHECK constraint SQL
392
+ */
393
+ function buildCheckConstraintSQL(tableName, constraint, dialect = 'postgres') {
394
+ const name = constraint.name || generateCheckName(tableName, constraint.field);
395
+ if (constraint.type === 'column' && constraint.operator && constraint.field) {
396
+ const sqlExpr = operatorToSQL(constraint.field, constraint.operator, dialect);
397
+ return `CONSTRAINT "${name}" CHECK (${sqlExpr})`;
398
+ }
399
+ if (constraint.type === 'table' && constraint.condition) {
400
+ // Extract field names from the model
401
+ const fields = extractFieldNames(constraint.condition);
402
+ const sqlExpr = conditionToSQL(constraint.condition, fields);
403
+ return `CONSTRAINT "${name}" CHECK (${sqlExpr})`;
404
+ }
405
+ // If sql is already provided (manual SQL), use it directly
406
+ if (constraint.sql) {
407
+ return `CONSTRAINT "${name}" CHECK (${constraint.sql})`;
408
+ }
409
+ throw new Error('Invalid check constraint: missing operator or condition');
410
+ }
411
+ /**
412
+ * Extract parameter names from a condition function
413
+ */
414
+ function extractFieldNames(condition) {
415
+ const funcStr = condition.toString();
416
+ const fieldMatches = funcStr.match(/row\.(\w+)/g);
417
+ if (!fieldMatches)
418
+ return [];
419
+ return [...new Set(fieldMatches.map((m) => m.replace('row.', '')))];
420
+ }
421
+ /**
422
+ * Build all CHECK constraints for a table
423
+ */
424
+ function buildAllCheckConstraints(tableName, constraints, dialect = 'postgres') {
425
+ return constraints
426
+ .filter((c) => c.type === 'column' || c.type === 'table')
427
+ .map((c) => buildCheckConstraintSQL(tableName, c, dialect));
428
+ }
429
+ /**
430
+ * Clear CHECK constraints (useful for testing)
431
+ */
432
+ function clearCheckConstraints(target) {
433
+ delete target[CHECK_CONSTRAINTS_KEY];
434
+ delete target[TABLE_CHECK_CONSTRAINTS_KEY];
435
+ }