ts-prorm-orm 1.1.0 → 1.2.1

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 (460) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +194 -72
  3. package/dist/audit/history-query.d.ts +35 -0
  4. package/dist/audit/index.d.ts +16 -0
  5. package/dist/audit/logger.d.ts +126 -0
  6. package/dist/cache/cache-manager.d.ts +341 -0
  7. package/dist/cache/index.d.ts +8 -0
  8. package/dist/cache/redis-cluster-cache.d.ts +164 -0
  9. package/dist/cache/redis-cluster-cache.js +19 -36
  10. package/dist/cli/cloud.d.ts +55 -0
  11. package/dist/cli/cloud.js +318 -0
  12. package/dist/cli/commands/auth.d.ts +2 -0
  13. package/dist/cli/commands/auth.js +345 -0
  14. package/dist/cli/commands/billing.d.ts +2 -0
  15. package/dist/cli/commands/billing.js +135 -0
  16. package/dist/cli/commands/code.d.ts +2 -0
  17. package/dist/cli/commands/code.js +168 -0
  18. package/dist/cli/commands/config.d.ts +2 -0
  19. package/dist/cli/commands/config.js +154 -0
  20. package/dist/cli/commands/db.d.ts +2 -0
  21. package/dist/cli/commands/db.js +189 -0
  22. package/dist/cli/commands/index.d.ts +1 -0
  23. package/dist/cli/commands/index.js +22 -0
  24. package/dist/cli/commands/keys.d.ts +2 -0
  25. package/dist/cli/commands/keys.js +100 -0
  26. package/dist/cli/commands/misc.d.ts +2 -0
  27. package/dist/cli/commands/misc.js +282 -0
  28. package/dist/cli/commands/pull.d.ts +2 -0
  29. package/dist/cli/commands/pull.js +306 -0
  30. package/dist/cli/commands/runners.d.ts +2 -0
  31. package/dist/cli/commands/runners.js +218 -0
  32. package/dist/cli/commands/scaffold.d.ts +2 -0
  33. package/dist/cli/commands/scaffold.js +383 -0
  34. package/dist/cli/commands/secrets.d.ts +2 -0
  35. package/dist/cli/commands/secrets.js +162 -0
  36. package/dist/cli/commands/store.d.ts +2 -0
  37. package/dist/cli/commands/store.js +274 -0
  38. package/dist/cli/commands/system.d.ts +10 -0
  39. package/dist/cli/commands/system.js +169 -0
  40. package/dist/cli/commands/teams.d.ts +2 -0
  41. package/dist/cli/commands/teams.js +189 -0
  42. package/dist/cli/commands/user.d.ts +2 -0
  43. package/dist/cli/commands/user.js +171 -0
  44. package/dist/cli/framework.d.ts +103 -0
  45. package/dist/cli/framework.js +436 -0
  46. package/dist/cli/io.d.ts +26 -0
  47. package/dist/cli/io.js +181 -0
  48. package/dist/cli/keychain.d.ts +10 -0
  49. package/dist/cli/keychain.js +101 -0
  50. package/dist/cli/prompt.d.ts +20 -0
  51. package/dist/cli/prompt.js +111 -0
  52. package/dist/cli.d.ts +214 -0
  53. package/dist/cli.js +72 -5
  54. package/dist/compliance/audit-trail.d.ts +66 -0
  55. package/dist/compliance/backup-verification.d.ts +279 -0
  56. package/dist/compliance/breach-detector.d.ts +197 -0
  57. package/dist/compliance/consent-record.d.ts +107 -0
  58. package/dist/compliance/consent-versioning.d.ts +169 -0
  59. package/dist/compliance/cross-border-log.d.ts +254 -0
  60. package/dist/compliance/data-classifier.d.ts +127 -0
  61. package/dist/compliance/data-lineage.d.ts +268 -0
  62. package/dist/compliance/data-masker.d.ts +110 -0
  63. package/dist/compliance/data-portability.d.ts +98 -0
  64. package/dist/compliance/data-retention.d.ts +108 -0
  65. package/dist/compliance/dsar-workflow.d.ts +175 -0
  66. package/dist/compliance/field-encryption.d.ts +84 -0
  67. package/dist/compliance/immutable-record.d.ts +84 -0
  68. package/dist/compliance/index.d.ts +93 -0
  69. package/dist/compliance/privacy-impact-assessment.d.ts +196 -0
  70. package/dist/compliance/pseudonymization.d.ts +96 -0
  71. package/dist/compliance/query-firewall.d.ts +661 -0
  72. package/dist/compliance/rate-limiter.d.ts +203 -0
  73. package/dist/compliance/right-to-erasure.d.ts +96 -0
  74. package/dist/compliance/row-level-security.d.ts +152 -0
  75. package/dist/compliance/security-decorator.d.ts +388 -0
  76. package/dist/compliance/security-monitor.d.ts +170 -0
  77. package/dist/compliance/sensitive-data-discovery.d.ts +151 -0
  78. package/dist/compliance/session-isolation.d.ts +286 -0
  79. package/dist/compliance/tls-enforcer.d.ts +51 -0
  80. package/dist/compliance/worm-storage.d.ts +225 -0
  81. package/dist/connection-manager.d.ts +130 -0
  82. package/dist/connection-pool.d.ts +311 -0
  83. package/dist/decorators/audit.d.ts +49 -0
  84. package/dist/decorators/check.d.ts +218 -0
  85. package/dist/decorators/collate.d.ts +346 -0
  86. package/dist/decorators/comment.d.ts +51 -0
  87. package/dist/decorators/database-settings.d.ts +193 -0
  88. package/dist/decorators/default.d.ts +148 -0
  89. package/dist/decorators/encryption.d.ts +175 -0
  90. package/dist/decorators/engine.d.ts +97 -0
  91. package/dist/decorators/fk-constraints.d.ts +387 -0
  92. package/dist/decorators/foreign-table.d.ts +57 -0
  93. package/dist/decorators/generated.d.ts +232 -0
  94. package/dist/decorators/hstore.d.ts +96 -0
  95. package/dist/decorators/index.d.ts +57 -0
  96. package/dist/decorators/index.js +14 -30
  97. package/dist/decorators/json-column.d.ts +47 -0
  98. package/dist/decorators/jsonb.d.ts +83 -0
  99. package/dist/decorators/orm-decorators.d.ts +274 -0
  100. package/dist/decorators/permissions.d.ts +192 -0
  101. package/dist/decorators/procedure.d.ts +168 -0
  102. package/dist/decorators/procedure.js +31 -7
  103. package/dist/decorators/query-options.d.ts +527 -0
  104. package/dist/decorators/range.d.ts +122 -0
  105. package/dist/decorators/set-column.d.ts +43 -0
  106. package/dist/decorators/spatial.d.ts +60 -0
  107. package/dist/decorators/storage.d.ts +561 -0
  108. package/dist/decorators/timezone.d.ts +319 -0
  109. package/dist/decorators/trigger.d.ts +50 -0
  110. package/dist/decorators/uuid.d.ts +102 -0
  111. package/dist/decorators/view.d.ts +213 -0
  112. package/dist/diagrams/chen-diagram.d.ts +70 -0
  113. package/dist/diagrams/chen-diagram.js +33 -31
  114. package/dist/diagrams/class-diagram.d.ts +78 -0
  115. package/dist/diagrams/class-diagram.js +23 -27
  116. package/dist/diagrams/dependency-diagram.d.ts +82 -0
  117. package/dist/diagrams/dependency-diagram.js +18 -9
  118. package/dist/diagrams/er-diagram.d.ts +82 -0
  119. package/dist/diagrams/er-diagram.js +174 -231
  120. package/dist/diagrams/flow-diagram.d.ts +70 -0
  121. package/dist/diagrams/flow-diagram.js +23 -18
  122. package/dist/diagrams/gantt-diagram.d.ts +67 -0
  123. package/dist/diagrams/gantt-diagram.js +20 -18
  124. package/dist/diagrams/index-diagram.d.ts +61 -0
  125. package/dist/diagrams/index-diagram.js +36 -31
  126. package/dist/diagrams/index.d.ts +155 -0
  127. package/dist/diagrams/index.js +7 -1
  128. package/dist/diagrams/migration-diagram.d.ts +67 -0
  129. package/dist/diagrams/migration-diagram.js +45 -57
  130. package/dist/diagrams/model-diagram.d.ts +85 -0
  131. package/dist/diagrams/model-diagram.js +225 -287
  132. package/dist/diagrams/package-diagram.d.ts +69 -0
  133. package/dist/diagrams/package-diagram.js +30 -32
  134. package/dist/diagrams/palette.d.ts +138 -0
  135. package/dist/diagrams/palette.js +134 -25
  136. package/dist/diagrams/relational-diagram.d.ts +82 -0
  137. package/dist/diagrams/relational-diagram.js +51 -59
  138. package/dist/diagrams/schemadoc-diagram.d.ts +57 -0
  139. package/dist/diagrams/schemadoc-diagram.js +36 -28
  140. package/dist/diagrams/sequence-diagram.d.ts +61 -0
  141. package/dist/diagrams/sequence-diagram.js +20 -11
  142. package/dist/diagrams/state-diagram.d.ts +85 -0
  143. package/dist/diagrams/state-diagram.js +21 -17
  144. package/dist/diagrams/svg-dom.d.ts +62 -0
  145. package/dist/diagrams/svg-dom.js +11 -0
  146. package/dist/diagrams/tree-diagram.d.ts +47 -0
  147. package/dist/diagrams/tree-diagram.js +19 -10
  148. package/dist/dialects/clickhouse/index.d.ts +660 -0
  149. package/dist/dialects/clickhouse/index.js +107 -2
  150. package/dist/dialects/cockroachdb/index.d.ts +1708 -0
  151. package/dist/dialects/cockroachdb/index.js +139 -10
  152. package/dist/dialects/cratedb/index.d.ts +262 -0
  153. package/dist/dialects/cratedb/index.js +148 -0
  154. package/dist/dialects/databricks/index.d.ts +397 -0
  155. package/dist/dialects/databricks/index.js +233 -11
  156. package/dist/dialects/db2/index.d.ts +729 -0
  157. package/dist/dialects/db2/index.js +152 -2
  158. package/dist/dialects/dialect.d.ts +1653 -0
  159. package/dist/dialects/duckdb/index.d.ts +440 -0
  160. package/dist/dialects/duckdb/index.js +21 -37
  161. package/dist/dialects/exasol/index.d.ts +197 -0
  162. package/dist/dialects/exasol/index.js +14 -0
  163. package/dist/dialects/firebird/index.d.ts +286 -0
  164. package/dist/dialects/firebird/index.js +135 -0
  165. package/dist/dialects/greenplum/index.d.ts +336 -0
  166. package/dist/dialects/greenplum/index.js +168 -0
  167. package/dist/dialects/hana/index.d.ts +512 -0
  168. package/dist/dialects/hana/index.js +17 -35
  169. package/dist/dialects/mariadb/index.d.ts +1241 -0
  170. package/dist/dialects/mariadb/index.js +181 -43
  171. package/dist/dialects/mssql/index.d.ts +729 -0
  172. package/dist/dialects/mssql/index.js +76 -7
  173. package/dist/dialects/mysql/index.d.ts +1233 -0
  174. package/dist/dialects/mysql/index.js +87 -19
  175. package/dist/dialects/oracle/index.d.ts +1222 -0
  176. package/dist/dialects/oracle/index.js +210 -9
  177. package/dist/dialects/postgres/index.d.ts +1598 -0
  178. package/dist/dialects/postgres/index.js +34 -13
  179. package/dist/dialects/query-stream-helper.d.ts +58 -0
  180. package/dist/dialects/questdb/index.d.ts +430 -0
  181. package/dist/dialects/questdb/index.js +292 -0
  182. package/dist/dialects/redshift/index.d.ts +933 -0
  183. package/dist/dialects/redshift/index.js +17 -5
  184. package/dist/dialects/singlestore/index.d.ts +233 -0
  185. package/dist/dialects/singlestore/index.js +53 -0
  186. package/dist/dialects/snowflake/index.d.ts +727 -0
  187. package/dist/dialects/snowflake/index.js +154 -5
  188. package/dist/dialects/spanner/index.d.ts +660 -0
  189. package/dist/dialects/spanner/index.js +43 -21
  190. package/dist/dialects/sqlite/index.d.ts +1133 -0
  191. package/dist/dialects/sqlite/index.js +168 -30
  192. package/dist/dialects/tidb/index.d.ts +226 -0
  193. package/dist/dialects/tidb/index.js +10 -0
  194. package/dist/dialects/timescaledb/index.d.ts +286 -0
  195. package/dist/dialects/timescaledb/index.js +179 -0
  196. package/dist/dialects/trino/index.d.ts +230 -0
  197. package/dist/dialects/trino/index.js +85 -10
  198. package/dist/dialects/turso/index.d.ts +98 -0
  199. package/dist/dialects/turso/index.js +10 -0
  200. package/dist/dialects/vertica/index.d.ts +321 -0
  201. package/dist/dialects/vertica/index.js +161 -0
  202. package/dist/dialects/yugabytedb/index.d.ts +276 -0
  203. package/dist/dialects/yugabytedb/index.js +121 -0
  204. package/dist/errors/index.d.ts +378 -0
  205. package/dist/errors/index.js +29 -1
  206. package/dist/errors/utils.d.ts +53 -0
  207. package/dist/errors.d.ts +5 -0
  208. package/dist/extensions/catalog/cloud-warehouse-features.d.ts +27 -0
  209. package/dist/extensions/catalog/mssql-features.d.ts +20 -0
  210. package/dist/extensions/catalog/mysql-mariadb-plugins.d.ts +18 -0
  211. package/dist/extensions/catalog/oracle-db2-features.d.ts +17 -0
  212. package/dist/extensions/catalog/postgres-extensions.d.ts +15 -0
  213. package/dist/extensions/index.d.ts +34 -0
  214. package/dist/extensions/types.d.ts +43 -0
  215. package/dist/external-fields/adapters.d.ts +50 -0
  216. package/dist/external-fields/adapters.js +227 -0
  217. package/dist/external-fields/decorator.d.ts +77 -0
  218. package/dist/external-fields/decorator.js +80 -0
  219. package/dist/external-fields/index.d.ts +10 -0
  220. package/dist/external-fields/index.js +19 -0
  221. package/dist/external-fields/runtime.d.ts +39 -0
  222. package/dist/external-fields/runtime.js +221 -0
  223. package/dist/foreign-data.d.ts +168 -0
  224. package/dist/graph/base-graph-dialect.d.ts +250 -0
  225. package/dist/graph/base-graph-dialect.js +539 -0
  226. package/dist/graph/dgraph/index.d.ts +65 -0
  227. package/dist/graph/dgraph/index.js +559 -0
  228. package/dist/graph/gremlin/index.d.ts +59 -0
  229. package/dist/graph/gremlin/index.js +378 -0
  230. package/dist/graph/index.d.ts +60 -0
  231. package/dist/graph/index.js +67 -0
  232. package/dist/graph/neo4j/index.d.ts +50 -0
  233. package/dist/graph/neo4j/index.js +462 -0
  234. package/dist/graph/types.d.ts +213 -0
  235. package/dist/graph/types.js +234 -0
  236. package/dist/hooks/hooks-manager.d.ts +189 -0
  237. package/dist/hooks/index.d.ts +8 -0
  238. package/dist/index.d.ts +713 -0
  239. package/dist/index.js +36 -4
  240. package/dist/logging.d.ts +183 -0
  241. package/dist/migrations/index.d.ts +9 -0
  242. package/dist/migrations/migration.d.ts +143 -0
  243. package/dist/migrations/migrator.d.ts +199 -0
  244. package/dist/migrations/prormmigration.d.ts +131 -0
  245. package/dist/migrations/query-interface.d.ts +335 -0
  246. package/dist/migrations/seeder.d.ts +167 -0
  247. package/dist/models/associations.d.ts +131 -0
  248. package/dist/models/associations.js +7 -2
  249. package/dist/models/constraints.d.ts +175 -0
  250. package/dist/models/data-types.d.ts +1656 -0
  251. package/dist/models/decorators.d.ts +328 -0
  252. package/dist/models/decorators.js +61 -82
  253. package/dist/models/eager-load.d.ts +86 -0
  254. package/dist/models/eager-load.js +369 -0
  255. package/dist/models/index.d.ts +13 -0
  256. package/dist/models/indexes.d.ts +325 -0
  257. package/dist/models/methods.d.ts +419 -0
  258. package/dist/models/model-manager.d.ts +76 -0
  259. package/dist/models/model.d.ts +820 -0
  260. package/dist/models/model.js +15 -101
  261. package/dist/models/operators.d.ts +29 -0
  262. package/dist/models/scopes.d.ts +71 -0
  263. package/dist/models/typescript-types.d.ts +177 -0
  264. package/dist/nosql/aerospike/index.d.ts +126 -0
  265. package/dist/nosql/allegrograph/index.d.ts +86 -0
  266. package/dist/nosql/arangodb/index.d.ts +285 -0
  267. package/dist/nosql/arangodb/index.js +274 -2
  268. package/dist/nosql/arangodb/where.d.ts +38 -0
  269. package/dist/nosql/arangodb/where.js +94 -0
  270. package/dist/nosql/azure-blob/index.d.ts +104 -0
  271. package/dist/nosql/beanstalkd/index.d.ts +88 -0
  272. package/dist/nosql/beequeue/index.d.ts +99 -0
  273. package/dist/nosql/bigchaindb/index.d.ts +113 -0
  274. package/dist/nosql/bigtable/index.d.ts +144 -0
  275. package/dist/nosql/blazegraph/index.d.ts +88 -0
  276. package/dist/nosql/bullmq/index.d.ts +83 -0
  277. package/dist/nosql/cassandra/index.d.ts +119 -0
  278. package/dist/nosql/chroma/index.d.ts +151 -0
  279. package/dist/nosql/cloudflare-kv/index.d.ts +120 -0
  280. package/dist/nosql/coherence/index.d.ts +91 -0
  281. package/dist/nosql/cosmosdb/index.d.ts +78 -0
  282. package/dist/nosql/couchbase/index.d.ts +103 -0
  283. package/dist/nosql/dax/index.d.ts +118 -0
  284. package/dist/nosql/deno-kv/index.d.ts +109 -0
  285. package/dist/nosql/dgraph/index.d.ts +80 -0
  286. package/dist/nosql/doris/index.d.ts +88 -0
  287. package/dist/nosql/druid/index.d.ts +95 -0
  288. package/dist/nosql/dynamodb/index.d.ts +643 -0
  289. package/dist/nosql/dynamodb/index.js +423 -0
  290. package/dist/nosql/elasticsearch/index.d.ts +239 -0
  291. package/dist/nosql/etcd/index.d.ts +243 -0
  292. package/dist/nosql/eventhubs/index.d.ts +89 -0
  293. package/dist/nosql/eventstore/index.d.ts +102 -0
  294. package/dist/nosql/faunadb/index.d.ts +141 -0
  295. package/dist/nosql/faunadb/index.js +329 -0
  296. package/dist/nosql/firestore/index.d.ts +67 -0
  297. package/dist/nosql/fluree/index.d.ts +88 -0
  298. package/dist/nosql/fuseki/index.d.ts +94 -0
  299. package/dist/nosql/gcs/index.d.ts +62 -0
  300. package/dist/nosql/gearman/index.d.ts +77 -0
  301. package/dist/nosql/geode/index.d.ts +93 -0
  302. package/dist/nosql/graphdb/index.d.ts +86 -0
  303. package/dist/nosql/graylog/index.d.ts +118 -0
  304. package/dist/nosql/gridgain/index.d.ts +78 -0
  305. package/dist/nosql/hazelcast/index.d.ts +73 -0
  306. package/dist/nosql/hbase/index.d.ts +123 -0
  307. package/dist/nosql/ignite/index.d.ts +80 -0
  308. package/dist/nosql/immudb/index.d.ts +131 -0
  309. package/dist/nosql/index.d.ts +215 -0
  310. package/dist/nosql/infinispan/index.d.ts +95 -0
  311. package/dist/nosql/influxdb/flux-compiler.d.ts +23 -0
  312. package/dist/nosql/influxdb/flux-compiler.js +170 -0
  313. package/dist/nosql/influxdb/index.d.ts +300 -0
  314. package/dist/nosql/influxdb/index.js +0 -0
  315. package/dist/nosql/kafka/index.d.ts +113 -0
  316. package/dist/nosql/keyspaces/index.d.ts +122 -0
  317. package/dist/nosql/kinesis/index.d.ts +121 -0
  318. package/dist/nosql/leveldb/index.d.ts +112 -0
  319. package/dist/nosql/leveldb/index.js +103 -1
  320. package/dist/nosql/lmdb/index.d.ts +76 -0
  321. package/dist/nosql/loki/index.d.ts +126 -0
  322. package/dist/nosql/marklogic/index.d.ts +76 -0
  323. package/dist/nosql/materialize/index.d.ts +91 -0
  324. package/dist/nosql/meilisearch/index.d.ts +76 -0
  325. package/dist/nosql/memcached/index.d.ts +138 -0
  326. package/dist/nosql/milvus/index.d.ts +167 -0
  327. package/dist/nosql/minio/index.d.ts +122 -0
  328. package/dist/nosql/momento/index.d.ts +99 -0
  329. package/dist/nosql/mongodb/index.d.ts +265 -0
  330. package/dist/nosql/mongodb/index.js +139 -0
  331. package/dist/nosql/mongodb/where.d.ts +8 -0
  332. package/dist/nosql/mongodb/where.js +181 -0
  333. package/dist/nosql/nats/index.d.ts +139 -0
  334. package/dist/nosql/nedb/index.d.ts +79 -0
  335. package/dist/nosql/neo4j/index.d.ts +217 -0
  336. package/dist/nosql/neo4j/index.js +26 -14
  337. package/dist/nosql/neptune/index.d.ts +234 -0
  338. package/dist/nosql/neptune/index.js +23 -9
  339. package/dist/nosql/nsq/index.d.ts +93 -0
  340. package/dist/nosql/opensearch/index.d.ts +97 -0
  341. package/dist/nosql/orientdb/index.d.ts +72 -0
  342. package/dist/nosql/papertrail/index.d.ts +120 -0
  343. package/dist/nosql/pinecone/index.d.ts +148 -0
  344. package/dist/nosql/pinot/index.d.ts +90 -0
  345. package/dist/nosql/pouchdb/index.d.ts +95 -0
  346. package/dist/nosql/prometheus/index.d.ts +112 -0
  347. package/dist/nosql/provendb/index.d.ts +143 -0
  348. package/dist/nosql/provendb/index.js +262 -0
  349. package/dist/nosql/pubsub/index.d.ts +83 -0
  350. package/dist/nosql/pulsar/index.d.ts +105 -0
  351. package/dist/nosql/qdrant/index.d.ts +231 -0
  352. package/dist/nosql/qldb/index.d.ts +182 -0
  353. package/dist/nosql/qldb/index.js +302 -1
  354. package/dist/nosql/r2/index.d.ts +160 -0
  355. package/dist/nosql/rabbitmq/index.d.ts +131 -0
  356. package/dist/nosql/ravendb/index.d.ts +138 -0
  357. package/dist/nosql/ravendb/index.js +323 -9
  358. package/dist/nosql/ravendb/where.d.ts +38 -0
  359. package/dist/nosql/ravendb/where.js +94 -0
  360. package/dist/nosql/redis/index.d.ts +267 -0
  361. package/dist/nosql/redis/index.js +19 -5
  362. package/dist/nosql/redpanda/index.d.ts +117 -0
  363. package/dist/nosql/resque/index.d.ts +91 -0
  364. package/dist/nosql/rethinkdb/index.d.ts +194 -0
  365. package/dist/nosql/rethinkdb/index.js +290 -0
  366. package/dist/nosql/rethinkdb/where.d.ts +38 -0
  367. package/dist/nosql/rethinkdb/where.js +94 -0
  368. package/dist/nosql/rocksdb/document-layer.d.ts +162 -0
  369. package/dist/nosql/rocksdb/document-layer.js +338 -0
  370. package/dist/nosql/rocksdb/index.d.ts +112 -0
  371. package/dist/nosql/rocksdb/index.js +103 -1
  372. package/dist/nosql/rocksdb/where-match.d.ts +21 -0
  373. package/dist/nosql/rocksdb/where-match.js +238 -0
  374. package/dist/nosql/rockset/index.d.ts +84 -0
  375. package/dist/nosql/s3/index.d.ts +170 -0
  376. package/dist/nosql/scylladb/index.d.ts +117 -0
  377. package/dist/nosql/signoz/index.d.ts +116 -0
  378. package/dist/nosql/sns/index.d.ts +99 -0
  379. package/dist/nosql/solr/index.d.ts +129 -0
  380. package/dist/nosql/splunk/index.d.ts +125 -0
  381. package/dist/nosql/sqs/index.d.ts +116 -0
  382. package/dist/nosql/stardog/index.d.ts +86 -0
  383. package/dist/nosql/starrocks/index.d.ts +89 -0
  384. package/dist/nosql/store.d.ts +23 -0
  385. package/dist/nosql/sumologic/index.d.ts +119 -0
  386. package/dist/nosql/surrealdb/index.d.ts +156 -0
  387. package/dist/nosql/surrealdb/index.js +174 -1
  388. package/dist/nosql/surrealdb/query-compiler.d.ts +26 -0
  389. package/dist/nosql/surrealdb/query-compiler.js +193 -0
  390. package/dist/nosql/terminusdb/index.d.ts +95 -0
  391. package/dist/nosql/tigergraph/index.d.ts +314 -0
  392. package/dist/nosql/typesense/index.d.ts +82 -0
  393. package/dist/nosql/unqlite/index.d.ts +68 -0
  394. package/dist/nosql/upstash/index.d.ts +80 -0
  395. package/dist/nosql/vercel-kv/index.d.ts +82 -0
  396. package/dist/nosql/victoriametrics/index.d.ts +129 -0
  397. package/dist/nosql/virtuoso/index.d.ts +87 -0
  398. package/dist/nosql/weaviate/index.d.ts +196 -0
  399. package/dist/operators/index.d.ts +7 -0
  400. package/dist/operators.d.ts +2947 -0
  401. package/dist/operators.js +46 -1
  402. package/dist/prisma-migrate/index.d.ts +28 -0
  403. package/dist/prisma-migrate/migration-generator.d.ts +21 -0
  404. package/dist/prisma-migrate/model-generator.d.ts +12 -0
  405. package/dist/prisma-migrate/relations.d.ts +28 -0
  406. package/dist/prisma-migrate/schema-parser.d.ts +42 -0
  407. package/dist/prisma-migrate/type-mapper.d.ts +20 -0
  408. package/dist/prorm.d.ts +2093 -0
  409. package/dist/prorm.js +721 -302
  410. package/dist/query-builders/cte-builder.d.ts +104 -0
  411. package/dist/query-builders/functions/aggregate.d.ts +199 -0
  412. package/dist/query-builders/functions/conditional.d.ts +195 -0
  413. package/dist/query-builders/functions/datetime.d.ts +202 -0
  414. package/dist/query-builders/functions/fulltext.d.ts +220 -0
  415. package/dist/query-builders/functions/index.d.ts +17 -0
  416. package/dist/query-builders/functions/json.d.ts +157 -0
  417. package/dist/query-builders/functions/math.d.ts +215 -0
  418. package/dist/query-builders/functions/string.d.ts +233 -0
  419. package/dist/query-builders/functions/window.d.ts +209 -0
  420. package/dist/query-builders/include-builder.d.ts +85 -0
  421. package/dist/query-builders/index-expression-builder.d.ts +153 -0
  422. package/dist/query-builders/index.d.ts +121 -0
  423. package/dist/query-builders/index.js +10 -0
  424. package/dist/query-builders/insert-builder.d.ts +62 -0
  425. package/dist/query-builders/model-helpers.d.ts +28 -0
  426. package/dist/query-builders/order-limit-builder.d.ts +84 -0
  427. package/dist/query-builders/sql-compiler.d.ts +562 -0
  428. package/dist/query-builders/subquery-builder.d.ts +133 -0
  429. package/dist/query-builders/update-builder.d.ts +71 -0
  430. package/dist/query-builders/view-builder.d.ts +189 -0
  431. package/dist/query-builders/where-builder.d.ts +124 -0
  432. package/dist/query-interface.d.ts +407 -0
  433. package/dist/query-interface.js +84 -0
  434. package/dist/query-optimizers/batch-optimizer.d.ts +175 -0
  435. package/dist/query-optimizers/explain-plans.d.ts +187 -0
  436. package/dist/query-optimizers/index.d.ts +27 -0
  437. package/dist/query-optimizers/prepared-statement-cache.d.ts +252 -0
  438. package/dist/query-optimizers/query-hints.d.ts +201 -0
  439. package/dist/query-optimizers/query-optimizer.d.ts +162 -0
  440. package/dist/query-optimizers/slow-query-logger.d.ts +186 -0
  441. package/dist/replica-manager.d.ts +235 -0
  442. package/dist/schema/index.d.ts +7 -0
  443. package/dist/schema/migration-generator.d.ts +74 -0
  444. package/dist/schema/schema-differ.d.ts +114 -0
  445. package/dist/schema/types.d.ts +246 -0
  446. package/dist/schema-objects.d.ts +78 -0
  447. package/dist/schema-objects.js +203 -0
  448. package/dist/sql-constants.d.ts +240 -0
  449. package/dist/sqlite-advanced.d.ts +572 -0
  450. package/dist/streams/index.d.ts +5 -0
  451. package/dist/streams/transforms.d.ts +120 -0
  452. package/dist/transaction.d.ts +221 -0
  453. package/dist/types/index.d.ts +3123 -0
  454. package/dist/user-management.d.ts +141 -0
  455. package/dist/utils/date.d.ts +149 -0
  456. package/dist/utils/index.d.ts +359 -0
  457. package/dist/utils/string.d.ts +88 -0
  458. package/dist/validators/index.d.ts +7 -0
  459. package/dist/validators/validator.d.ts +306 -0
  460. package/package.json +20 -5
package/dist/prorm.js CHANGED
@@ -60,7 +60,13 @@ const hana_1 = require("./dialects/hana");
60
60
  const spanner_1 = require("./dialects/spanner");
61
61
  const connection_pool_1 = require("./connection-pool");
62
62
  const models_1 = require("./models");
63
+ const eager_load_1 = require("./models/eager-load");
63
64
  const decorators_1 = require("./models/decorators");
65
+ const view_1 = require("./decorators/view");
66
+ const view_2 = require("./decorators/view");
67
+ const trigger_1 = require("./decorators/trigger");
68
+ const procedure_1 = require("./decorators/procedure");
69
+ const external_fields_1 = require("./external-fields");
64
70
  const logging_1 = require("./logging");
65
71
  const utils_1 = require("./errors/utils");
66
72
  const validator_1 = require("./validators/validator");
@@ -455,6 +461,67 @@ function resolveModelPrimaryKeyAttr(m) {
455
461
  }
456
462
  return 'id';
457
463
  }
464
+ /**
465
+ * Derive a model's table name from its name and options.
466
+ *
467
+ * This is the single definition of that rule. It previously existed in six
468
+ * places, two of which (`syncCreate` and the table-name resolver) omitted the
469
+ * pluralization entirely and returned the bare model name - so `define('User')`
470
+ * followed by a plain `sync()` looked for a table called `User`, never found the
471
+ * `users` table that `define()` had already created, and created a second empty
472
+ * table beside it on every call. `syncForce`/`syncAlter` used the correct form,
473
+ * which is why the bug was invisible to anyone testing with `{ force: true }`.
474
+ */
475
+ function deriveTableName(modelName, options) {
476
+ if (options?.tableName)
477
+ return options.tableName;
478
+ return options?.freezeTableName ? modelName : (0, utils_2.pluralize)(modelName.toLowerCase());
479
+ }
480
+ /**
481
+ * Resolve the column type a synthesized foreign key should use so that it
482
+ * matches the column it references.
483
+ *
484
+ * A foreign key column must be type-compatible with its referenced key. This
485
+ * used to be hardcoded to INTEGER, which meant any model with a UUID or STRING
486
+ * primary key produced a child column that Postgres rejects outright at CREATE
487
+ * TABLE (`foreign key constraint cannot be implemented`) and SQLite accepts
488
+ * while silently coercing the stored values.
489
+ *
490
+ * Auto-increment types are mapped to their referenceable equivalent - a column
491
+ * pointing at a SERIAL key is an INTEGER, not another SERIAL.
492
+ */
493
+ function resolveForeignKeyColumnType(refModel, refField) {
494
+ const attrs = refModel?.rawAttributes || refModel?.attributes;
495
+ const attr = attrs?.[refField];
496
+ // Unwrap the several shapes an attribute can take: `{ type: X }`, a DataType
497
+ // instance, or a bare factory function.
498
+ let type = attr && typeof attr === 'object' && 'type' in attr ? attr.type : attr;
499
+ if (typeof type === 'function') {
500
+ try {
501
+ const built = type();
502
+ if (built && typeof built === 'object')
503
+ type = built;
504
+ }
505
+ catch {
506
+ type = undefined;
507
+ }
508
+ }
509
+ const key = String(type?.key ?? type?.toString?.() ?? type ?? '').toUpperCase();
510
+ if (!key || key === 'UNDEFINED') {
511
+ return 'INTEGER';
512
+ }
513
+ // A reference to an auto-incrementing key is a plain integer of matching width.
514
+ if (key.startsWith('BIGSERIAL'))
515
+ return 'BIGINT';
516
+ if (key.startsWith('SMALLSERIAL'))
517
+ return 'SMALLINT';
518
+ if (key.startsWith('SERIAL'))
519
+ return 'INTEGER';
520
+ if (attr?.autoIncrement && key.startsWith('INTEGER'))
521
+ return 'INTEGER';
522
+ // Otherwise mirror the referenced column's own type (UUID, STRING(n), BIGINT, ...).
523
+ return (type ?? 'INTEGER');
524
+ }
458
525
  // Tracks (via Node's AsyncLocalStorage, which correctly follows a logical
459
526
  // call chain across `await`s regardless of how concurrent operations
460
527
  // interleave) whether the currently-executing code is running inside an
@@ -556,6 +623,20 @@ class Prorm extends events_1.EventEmitter {
556
623
  this._rootTransactionQueue = Promise.resolve();
557
624
  this.modelDefinitions = new Map();
558
625
  this.associations = new Map();
626
+ /**
627
+ * Decorator-declared associations waiting for their target model to be
628
+ * registered. See `_flushPendingAssociations()`.
629
+ */
630
+ this._pendingAssociations = [];
631
+ /** Model classes registered via `addModel`, consulted for @View/@Trigger DDL. */
632
+ this._decoratedClasses = [];
633
+ /** Classes registered via `registerProcedures`, for @Procedure/@SqlFunction DDL. */
634
+ this._procedureClasses = [];
635
+ /**
636
+ * Object/key-value stores backing @ExternalField properties. Public so the
637
+ * instance accessors built in `_wrapInstance` can resolve them.
638
+ */
639
+ this.externalStores = new external_fields_1.ExternalStoreRegistry();
559
640
  // Tracks the fire-and-forget auto-create-table promises kicked off by
560
641
  // `define()` (see below). `sync()` awaits these before doing its own
561
642
  // drop/create pass so that a `define()` call followed synchronously by
@@ -1401,7 +1482,7 @@ class Prorm extends events_1.EventEmitter {
1401
1482
  }
1402
1483
  throw new Error(`Model '${modelName}' not found`);
1403
1484
  }
1404
- const tableName = modelDef.options.tableName || modelName;
1485
+ const tableName = deriveTableName(modelName, modelDef.options);
1405
1486
  const schema = modelDef.options.schema;
1406
1487
  const schemaDelimiter = modelDef.options.schemaDelimiter || '.';
1407
1488
  return schema ? `${schema}${schemaDelimiter}${tableName}` : tableName;
@@ -1549,7 +1630,7 @@ class Prorm extends events_1.EventEmitter {
1549
1630
  // Store the model definition
1550
1631
  this.modelDefinitions.set(modelName, { attributes, options });
1551
1632
  // Determine table name and schema
1552
- const tableName = options.tableName || (options.freezeTableName ? modelName : (0, utils_2.pluralize)(modelName.toLowerCase()));
1633
+ const tableName = deriveTableName(modelName, options);
1553
1634
  const schema = options.schema;
1554
1635
  const schemaDelimiter = options.schemaDelimiter || '.';
1555
1636
  // Build the full table name with schema (e.g., "schema.tableName" or "database.table")
@@ -1871,6 +1952,14 @@ class Prorm extends events_1.EventEmitter {
1871
1952
  'save',
1872
1953
  'destroy',
1873
1954
  'isNewRecord',
1955
+ // Bookkeeping properties, not columns. These are enumerable own
1956
+ // properties of the instance, so without them here the sweep below
1957
+ // put them in the UPDATE and every
1958
+ // `instance.field = x; await instance.save()` failed with
1959
+ // "no such column: _previousDataValues". Any future underscore-
1960
+ // prefixed internal is excluded by the check in the loop too.
1961
+ '_previousDataValues',
1962
+ '_changed',
1874
1963
  ]);
1875
1964
  if (pk !== undefined) {
1876
1965
  const saveVals = {};
@@ -1878,6 +1967,8 @@ class Prorm extends events_1.EventEmitter {
1878
1967
  for (const k of Object.keys(rowInstance)) {
1879
1968
  if (reserved.has(k))
1880
1969
  continue;
1970
+ if (k.startsWith('_'))
1971
+ continue; // internal bookkeeping, not a column
1881
1972
  if (typeof rowInstance[k] === 'function')
1882
1973
  continue;
1883
1974
  if (k !== 'id') {
@@ -1885,8 +1976,11 @@ class Prorm extends events_1.EventEmitter {
1885
1976
  dataValues[k] = rowInstance[k];
1886
1977
  }
1887
1978
  }
1888
- // Also include current dataValues (minus id, minus virtual fields)
1979
+ // Also include current dataValues (minus id, minus virtual fields,
1980
+ // minus @ExternalField payloads which live in a store, not a column)
1889
1981
  for (const [k, v] of Object.entries(dataValues)) {
1982
+ if (rowInstance.__externalFields?.has(k))
1983
+ continue;
1890
1984
  if (k !== 'id' && !reserved.has(k)) {
1891
1985
  const attr = attributes[k];
1892
1986
  if (attr) {
@@ -2162,7 +2256,10 @@ class Prorm extends events_1.EventEmitter {
2162
2256
  const sourceMatch = assoc.source === modelRef || assoc.source?.name === modelName;
2163
2257
  if (!sourceMatch)
2164
2258
  continue;
2165
- const assocAlias = assoc.options?.as || assoc.target?.name || '';
2259
+ // Prefer the association's resolved alias, which is what the
2260
+ // eager-loader attaches results under, so `user.getPosts()` and
2261
+ // `include: [Post]` agree on the name.
2262
+ const assocAlias = assoc.as || assoc.options?.as || assoc.target?.name || '';
2166
2263
  const singularAlias = assocAlias.endsWith('s') ? assocAlias.slice(0, -1) : assocAlias;
2167
2264
  const pluralAlias = assocAlias.endsWith('s') ? assocAlias : assocAlias + 's';
2168
2265
  const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
@@ -2172,7 +2269,13 @@ class Prorm extends events_1.EventEmitter {
2172
2269
  const { where: optWhere, ...rest } = opts || {};
2173
2270
  return assoc.target.findAll({
2174
2271
  ...rest,
2175
- where: { [fk]: dataValues.id, ...(optWhere || {}) },
2272
+ // An association `scope` applies every time the association is
2273
+ // traversed; a caller-supplied where can narrow it further.
2274
+ where: {
2275
+ ...(assoc.options?.scope || {}),
2276
+ [fk]: dataValues.id,
2277
+ ...(optWhere || {}),
2278
+ },
2176
2279
  });
2177
2280
  };
2178
2281
  rowInstance[`add${capitalize(singularAlias)}`] = async (instance) => {
@@ -2239,16 +2342,56 @@ class Prorm extends events_1.EventEmitter {
2239
2342
  rowInstance[fk] = targetId;
2240
2343
  await modelRef.update({ [fk]: targetId }, { where: { id: dataValues.id } });
2241
2344
  };
2345
+ // create/has/count were missing here while the equivalent mixins in
2346
+ // models/associations.ts defined them, so which helpers existed on an
2347
+ // instance depended on how the model had been defined.
2348
+ rowInstance[`create${capitalize(assocAlias)}`] = async (values, options) => {
2349
+ const created = await assoc.target.create(values, options);
2350
+ const targetId = created?.id ?? created;
2351
+ dataValues[fk] = targetId;
2352
+ rowInstance[fk] = targetId;
2353
+ await modelRef.update({ [fk]: targetId }, { where: { id: dataValues.id } });
2354
+ return created;
2355
+ };
2356
+ rowInstance[`has${capitalize(assocAlias)}`] = async () => {
2357
+ const fkVal = dataValues[fk];
2358
+ if (fkVal === null || fkVal === undefined)
2359
+ return false;
2360
+ return (await assoc.target.count({ where: { id: fkVal } })) > 0;
2361
+ };
2362
+ rowInstance[`count${capitalize(assocAlias)}`] = async () => {
2363
+ const fkVal = dataValues[fk];
2364
+ if (fkVal === null || fkVal === undefined)
2365
+ return 0;
2366
+ return assoc.target.count({ where: { id: fkVal } });
2367
+ };
2242
2368
  }
2243
2369
  else if (assoc.type === 'hasOne') {
2244
2370
  const fk = assoc.foreignKey;
2245
2371
  rowInstance[`get${capitalize(assocAlias)}`] = async (opts) => {
2246
- return assoc.target.findOne({ where: { [fk]: dataValues.id }, ...(opts || {}) });
2372
+ const { where: optWhere, ...rest } = opts || {};
2373
+ return assoc.target.findOne({
2374
+ ...rest,
2375
+ where: {
2376
+ ...(assoc.options?.scope || {}),
2377
+ [fk]: dataValues.id,
2378
+ ...(optWhere || {}),
2379
+ },
2380
+ });
2247
2381
  };
2248
2382
  rowInstance[`set${capitalize(assocAlias)}`] = async (instance) => {
2249
2383
  const targetId = instance?.id ?? instance;
2250
2384
  await assoc.target.update({ [fk]: dataValues.id }, { where: { id: targetId } });
2251
2385
  };
2386
+ rowInstance[`create${capitalize(assocAlias)}`] = async (values, options) => {
2387
+ return assoc.target.create({ ...values, [fk]: dataValues.id }, options);
2388
+ };
2389
+ rowInstance[`has${capitalize(assocAlias)}`] = async () => {
2390
+ return (await assoc.target.count({ where: { [fk]: dataValues.id } })) > 0;
2391
+ };
2392
+ rowInstance[`count${capitalize(assocAlias)}`] = async () => {
2393
+ return assoc.target.count({ where: { [fk]: dataValues.id } });
2394
+ };
2252
2395
  }
2253
2396
  else if (assoc.type === 'belongsToMany') {
2254
2397
  const fk = assoc.options?.foreignKey || assoc.foreignKey;
@@ -2375,12 +2518,24 @@ class Prorm extends events_1.EventEmitter {
2375
2518
  return true;
2376
2519
  }
2377
2520
  }
2521
+ // @ExternalField payloads are not columns - route the assignment to
2522
+ // the field's own accessor (which records it for the next save) and
2523
+ // keep it out of dataValues, which save() sweeps into the UPDATE.
2524
+ if (target.__externalFields?.has(prop)) {
2525
+ target[prop] = value;
2526
+ return true;
2527
+ }
2378
2528
  // Regular field
2379
2529
  dataValues[prop] = value;
2380
2530
  target[prop] = value;
2381
2531
  return true;
2382
2532
  },
2383
2533
  });
2534
+ // Attach @ExternalField accessors. These are defined on the underlying
2535
+ // rowInstance (not the proxy) as non-enumerable properties, so save()'s
2536
+ // sweep over the instance's own keys never tries to persist a payload
2537
+ // into a column that doesn't exist - only the key column is written.
2538
+ (0, external_fields_1.attachExternalFields)(rowInstance, dataValues, modelName, fullTableName, self.externalStores);
2384
2539
  return proxyInstance;
2385
2540
  };
2386
2541
  const model = {
@@ -2713,6 +2868,32 @@ class Prorm extends events_1.EventEmitter {
2713
2868
  [deletedAt]: { $isNull: true },
2714
2869
  };
2715
2870
  }
2871
+ // Apply INNER-JOIN semantics for filtering includes.
2872
+ //
2873
+ // `include: [{ model: Post, required: true }]` and an include carrying
2874
+ // its own `where` are documented to restrict the parent rows. That was
2875
+ // never implemented - `required` was read by no live code path, so these
2876
+ // queries returned every parent regardless. Resolve the matching parent
2877
+ // keys first and constrain the main query with them, so the restriction
2878
+ // is applied before LIMIT/OFFSET rather than after.
2879
+ if (findOptions.include && findOptions.include.length > 0) {
2880
+ for (const rawInclude of findOptions.include) {
2881
+ const filter = await (0, eager_load_1.resolveRequiredIncludeFilter)(self.eagerLoadContext(), model, modelName, rawInclude);
2882
+ if (!filter)
2883
+ continue;
2884
+ if (filter.values.length === 0) {
2885
+ // No parent can satisfy a required include with no matches.
2886
+ return [];
2887
+ }
2888
+ const existing = where?.[filter.parentAttr];
2889
+ where = {
2890
+ ...where,
2891
+ [filter.parentAttr]: existing
2892
+ ? { $and: [existing, { $in: filter.values }] }
2893
+ : { $in: filter.values },
2894
+ };
2895
+ }
2896
+ }
2716
2897
  // Translate attributes.exclude to explicit list (SQLite doesn't support "* EXCEPT(...)")
2717
2898
  let resolvedAttributes = findOptions.attributes;
2718
2899
  if (resolvedAttributes &&
@@ -2791,232 +2972,12 @@ class Prorm extends events_1.EventEmitter {
2791
2972
  const wrappedRows = result.rows.map((row) => {
2792
2973
  return _wrapInstance(row, modelRef, modelHooksRef);
2793
2974
  });
2794
- // Handle includes (associations) by doing separate queries and attaching results.
2795
- // This recurses into nested `include` entries so `include: [{ model: A, include: [{ model: B }] }]`
2796
- // eager-loads grandchild-level data as well (fetched rows for A become the new "parent" rows for B).
2797
- const loadIncludesRecursive = async (parentRows, parentModel, parentModelName, includeList) => {
2798
- for (const rawIncludeOpt of includeList) {
2799
- // Support both the object form (`{ model: Post, as: 'posts' }`) and
2800
- // the bare-model shorthand (`Post`) used throughout the test suite
2801
- // and docs (e.g. `findAll({ include: [Post] })`). A bare model has
2802
- // no `.model` property of its own but does carry `.rawAttributes`/
2803
- // `.tableName`, so detect that shape and wrap it before use -
2804
- // otherwise `includeModel` was always undefined for the shorthand
2805
- // form and the include was silently skipped.
2806
- const includeOpt = rawIncludeOpt && !rawIncludeOpt.model && rawIncludeOpt.rawAttributes
2807
- ? { model: rawIncludeOpt }
2808
- : rawIncludeOpt;
2809
- const includeModel = includeOpt.model;
2810
- if (!includeModel)
2811
- continue;
2812
- // Find the association definition to get the foreign key (and, for
2813
- // belongsToMany, the junction table info).
2814
- let foreignKey;
2815
- let associationType;
2816
- let assocOtherKey;
2817
- let assocThrough;
2818
- let assocThroughModel;
2819
- let assocAs;
2820
- for (const [, assoc] of self.associations.entries()) {
2821
- const sourceMatch = assoc.source === parentModel || assoc.source?.name === parentModelName;
2822
- const targetMatch = assoc.target === includeModel || assoc.target?.name === includeModel.name;
2823
- const aliasMatch = !includeOpt.as || assoc.options?.as === includeOpt.as;
2824
- if (sourceMatch && targetMatch && aliasMatch) {
2825
- foreignKey = assoc.foreignKey;
2826
- associationType = assoc.type;
2827
- assocOtherKey = assoc.otherKey;
2828
- assocThrough = assoc.through;
2829
- assocThroughModel = assoc.throughModel;
2830
- assocAs = assoc.as;
2831
- break;
2832
- }
2833
- // Also check reverse (belongsTo) - check alias match too
2834
- const revSourceMatch = assoc.source === includeModel || assoc.source?.name === includeModel.name;
2835
- const revTargetMatch = assoc.target === parentModel || assoc.target?.name === parentModelName;
2836
- const revAliasMatch = !includeOpt.as || assoc.options?.as === includeOpt.as;
2837
- if (revSourceMatch && revTargetMatch && revAliasMatch) {
2838
- foreignKey = assoc.foreignKey;
2839
- associationType = assoc.type;
2840
- assocOtherKey = assoc.otherKey;
2841
- assocThrough = assoc.through;
2842
- assocThroughModel = assoc.throughModel;
2843
- assocAs = assoc.as;
2844
- break;
2845
- }
2846
- }
2847
- // Prefer the association's own default alias (e.g. `user` for a
2848
- // singular belongsTo/hasOne, `posts` for a plural hasMany) over
2849
- // the raw table name - using the table name here meant an
2850
- // unaliased `include: [User]` populated `row.users` instead of
2851
- // the expected `row.user`, silently breaking any include that
2852
- // didn't pass an explicit `as`.
2853
- const includeAlias = includeOpt.as || assocAs || includeModel.tableName || includeModel.name;
2854
- // Resolve the actual primary-key attribute for each side of the association
2855
- // (instead of assuming 'id'), since custom primary keys may differ per model.
2856
- const sourcePkAttr = resolveModelPrimaryKeyAttr(parentModel);
2857
- const targetPkAttr = resolveModelPrimaryKeyAttr(includeModel);
2858
- // Get primary keys from parent rows
2859
- const mainPks = parentRows
2860
- .map((r) => r[sourcePkAttr])
2861
- .filter((v) => v !== undefined && v !== null);
2862
- if (mainPks.length === 0)
2863
- continue;
2864
- // For hasMany/hasOne: foreignKey is on target table pointing to source's pk
2865
- // For belongsTo: foreignKey is on source table pointing to target's pk
2866
- // For belongsToMany: foreignKey is on the junction table pointing to source's pk
2867
- if (!foreignKey) {
2868
- // Try to infer foreign key from model name
2869
- foreignKey = `${parentModelName.charAt(0).toLowerCase() + parentModelName.slice(1)}Id`;
2870
- }
2871
- let fetchedRows = [];
2872
- try {
2873
- const includeWhere = { ...includeOpt.where };
2874
- if (associationType === 'belongsTo') {
2875
- // foreignKey is on this model, not the target
2876
- // The association goes: this.foreignKey references target's pk
2877
- const fkValues = parentRows
2878
- .map((r) => r[foreignKey])
2879
- .filter((v) => v !== undefined && v !== null);
2880
- if (fkValues.length === 0) {
2881
- for (const row of parentRows) {
2882
- row[includeAlias] = null;
2883
- if (row.dataValues)
2884
- row.dataValues[includeAlias] = null;
2885
- }
2886
- continue;
2887
- }
2888
- const targetRows = await includeModel.findAll({
2889
- where: { ...includeWhere, [targetPkAttr]: { $in: fkValues } },
2890
- ...(includeOpt.attributes ? { attributes: includeOpt.attributes } : {}),
2891
- });
2892
- fetchedRows = targetRows;
2893
- const targetMap = new Map();
2894
- for (const tr of targetRows) {
2895
- targetMap.set(tr[targetPkAttr], tr);
2896
- }
2897
- for (const row of parentRows) {
2898
- const fkVal = row[foreignKey];
2899
- const associated = fkVal !== undefined && fkVal !== null ? targetMap.get(fkVal) || null : null;
2900
- row[includeAlias] = associated;
2901
- if (row.dataValues)
2902
- row.dataValues[includeAlias] = associated;
2903
- }
2904
- }
2905
- else if (associationType === 'belongsToMany') {
2906
- // Many-to-many: the foreign key linking to the source lives on the junction
2907
- // (through) table, not on the target table directly. Mirrors the logic used by
2908
- // the row-instance `get<Alias>()` methods: find the through model, query junction
2909
- // rows matching the parent rows' PKs, then query target rows by the resulting ids.
2910
- let throughModel = assocThroughModel;
2911
- if (!throughModel && assocThrough) {
2912
- for (const m of Object.values(self.models)) {
2913
- if (m.tableName === assocThrough ||
2914
- m.name === assocThrough) {
2915
- throughModel = m;
2916
- break;
2917
- }
2918
- }
2919
- }
2920
- if (!throughModel || typeof throughModel.findAll !== 'function') {
2921
- throw new Error(`Cannot resolve through model for belongsToMany include "${includeAlias}"`);
2922
- }
2923
- const otherKey = assocOtherKey || `${includeModel.name}Id`;
2924
- const junctionRows = await throughModel.findAll({
2925
- where: { [foreignKey]: { $in: mainPks } },
2926
- });
2927
- const targetIdsBySource = new Map();
2928
- const allTargetIds = [];
2929
- for (const jr of junctionRows) {
2930
- const srcVal = jr[foreignKey];
2931
- const tgtVal = jr[otherKey];
2932
- if (tgtVal === undefined || tgtVal === null)
2933
- continue;
2934
- if (!targetIdsBySource.has(srcVal))
2935
- targetIdsBySource.set(srcVal, []);
2936
- targetIdsBySource.get(srcVal).push(tgtVal);
2937
- allTargetIds.push(tgtVal);
2938
- }
2939
- if (allTargetIds.length === 0) {
2940
- for (const row of parentRows) {
2941
- row[includeAlias] = [];
2942
- if (row.dataValues)
2943
- row.dataValues[includeAlias] = [];
2944
- }
2945
- continue;
2946
- }
2947
- const targetRows = await includeModel.findAll({
2948
- where: { ...includeWhere, [targetPkAttr]: { $in: allTargetIds } },
2949
- ...(includeOpt.attributes ? { attributes: includeOpt.attributes } : {}),
2950
- ...(includeOpt.order ? { order: includeOpt.order } : {}),
2951
- });
2952
- fetchedRows = targetRows;
2953
- const targetMap = new Map();
2954
- for (const tr of targetRows) {
2955
- targetMap.set(tr[targetPkAttr], tr);
2956
- }
2957
- for (const row of parentRows) {
2958
- const srcPk = row[sourcePkAttr];
2959
- const targetIds = targetIdsBySource.get(srcPk) || [];
2960
- const associated = targetIds
2961
- .map((tid) => targetMap.get(tid))
2962
- .filter((v) => v !== undefined);
2963
- row[includeAlias] = associated;
2964
- if (row.dataValues)
2965
- row.dataValues[includeAlias] = associated;
2966
- }
2967
- }
2968
- else {
2969
- // hasMany/hasOne: foreignKey is on target table
2970
- if (includeOpt.where)
2971
- Object.assign(includeWhere, includeOpt.where);
2972
- includeWhere[foreignKey] = { $in: mainPks };
2973
- const assocRows = await includeModel.findAll({
2974
- where: includeWhere,
2975
- ...(includeOpt.attributes ? { attributes: includeOpt.attributes } : {}),
2976
- ...(includeOpt.order ? { order: includeOpt.order } : {}),
2977
- });
2978
- fetchedRows = assocRows;
2979
- // Group by foreignKey value
2980
- const groupedRows = new Map();
2981
- for (const ar of assocRows) {
2982
- const fkVal = ar[foreignKey];
2983
- if (!groupedRows.has(fkVal))
2984
- groupedRows.set(fkVal, []);
2985
- groupedRows.get(fkVal).push(ar);
2986
- }
2987
- // Attach to parent rows
2988
- for (const row of parentRows) {
2989
- const pk = row[sourcePkAttr];
2990
- const associated = groupedRows.get(pk) || [];
2991
- // For hasOne, use the first result or null
2992
- const attachValue = associationType === 'hasOne' ? associated[0] || null : associated;
2993
- row[includeAlias] = attachValue;
2994
- if (row.dataValues)
2995
- row.dataValues[includeAlias] = attachValue;
2996
- }
2997
- }
2998
- // Recurse into nested includes, treating the just-fetched rows as the new
2999
- // "parent" rows for the next level down.
3000
- const nestedIncludes = includeOpt.include;
3001
- if (nestedIncludes && nestedIncludes.length > 0 && fetchedRows.length > 0) {
3002
- await loadIncludesRecursive(fetchedRows, includeModel, includeModel.name, nestedIncludes);
3003
- }
3004
- }
3005
- catch {
3006
- // If include query fails, set empty
3007
- for (const row of parentRows) {
3008
- if (row[includeAlias] === undefined) {
3009
- row[includeAlias] =
3010
- associationType === 'hasMany' || associationType === 'belongsToMany' ? [] : null;
3011
- if (row.dataValues)
3012
- row.dataValues[includeAlias] = row[includeAlias];
3013
- }
3014
- }
3015
- }
3016
- }
3017
- };
2975
+ // Eager-load associations. The implementation is shared with the
2976
+ // class-based Model API (see models/eager-load.ts) - it used to be
2977
+ // duplicated here and in models/model.ts, and the two copies had
2978
+ // drifted apart in how they handled belongsTo and belongsToMany.
3018
2979
  if (findOptions.include && findOptions.include.length > 0) {
3019
- await loadIncludesRecursive(wrappedRows, model, modelName, findOptions.include);
2980
+ await (0, eager_load_1.loadIncludes)(self.eagerLoadContext(), wrappedRows, model, modelName, findOptions.include);
3020
2981
  }
3021
2982
  return wrappedRows;
3022
2983
  },
@@ -3025,13 +2986,12 @@ class Prorm extends events_1.EventEmitter {
3025
2986
  */
3026
2987
  async findAndCountAll(findOptions = {}) {
3027
2988
  const rows = await this.findAll(findOptions);
3028
- // Count all matching rows without limit/offset
3029
- const countOptions = {
3030
- where: findOptions.where,
3031
- paranoid: findOptions.paranoid,
3032
- };
3033
- const allRows = await this.findAll(countOptions);
3034
- const count = allRows.length;
2989
+ // Count with an aggregate rather than by re-running the full query
2990
+ // without a limit and measuring the array. `limit`/`offset` are dropped
2991
+ // (the count describes the whole matching set), everything else that
2992
+ // narrows the set - including filtering includes - is preserved.
2993
+ const { limit: _limit, offset: _offset, ...countable } = this._mergeScopes(findOptions);
2994
+ const count = await self._countRows(model, modelName, countable);
3035
2995
  return { rows, count };
3036
2996
  },
3037
2997
  /**
@@ -3056,11 +3016,45 @@ class Prorm extends events_1.EventEmitter {
3056
3016
  if (existing) {
3057
3017
  return [existing, false];
3058
3018
  }
3059
- const createValues = { ...findOptions.defaults, ...findOptions.where };
3060
- const created = await this.create(createValues, {
3061
- transaction: findOptions.transaction,
3062
- });
3063
- return [created, true];
3019
+ // Only equality conditions from `where` describe a row that can be
3020
+ // created. Operator objects (`{ email: { $like: 'a%' } }`) used to be
3021
+ // spread into the INSERT verbatim, which tried to store the operator
3022
+ // object itself as the column value.
3023
+ const equalityFromWhere = {};
3024
+ for (const [key, value] of Object.entries((findOptions.where || {}))) {
3025
+ if (key.startsWith('$'))
3026
+ continue; // $and / $or / $not - not a column
3027
+ const isOperatorObject = value !== null &&
3028
+ typeof value === 'object' &&
3029
+ !(value instanceof Date) &&
3030
+ !Array.isArray(value) &&
3031
+ Object.keys(value).some((k) => k.startsWith('$'));
3032
+ if (!isOperatorObject)
3033
+ equalityFromWhere[key] = value;
3034
+ }
3035
+ const createValues = {
3036
+ ...findOptions.defaults,
3037
+ ...equalityFromWhere,
3038
+ };
3039
+ try {
3040
+ const created = await this.create(createValues, {
3041
+ transaction: findOptions.transaction,
3042
+ });
3043
+ return [created, true];
3044
+ }
3045
+ catch (error) {
3046
+ // The find and the create are two statements, so a concurrent caller
3047
+ // can insert between them. When the database rejects our insert on a
3048
+ // unique/primary key, the row we wanted now exists - return it rather
3049
+ // than surfacing a constraint error from an idempotent-looking call.
3050
+ const message = error instanceof Error ? error.message : String(error);
3051
+ if (!/unique|duplicate|constraint/i.test(message))
3052
+ throw error;
3053
+ const raced = await this.findOne(findOptions);
3054
+ if (raced)
3055
+ return [raced, false];
3056
+ throw error;
3057
+ }
3064
3058
  },
3065
3059
  /**
3066
3060
  * Find a record or build (但不保存) it if not found
@@ -3158,6 +3152,18 @@ class Prorm extends events_1.EventEmitter {
3158
3152
  const validationErrors = [];
3159
3153
  for (const [attrName, attr] of Object.entries(attributes)) {
3160
3154
  const val = hookInstance[attrName];
3155
+ // An auto-increment key is assigned by the database on insert, so it
3156
+ // is legitimately absent here. Requiring it would make every
3157
+ // `@PrimaryKey @AutoIncrement` column reject its own INSERT.
3158
+ if ((val === undefined || val === null) && attr.autoIncrement)
3159
+ continue;
3160
+ // A column with a default (including a DB-side default) is likewise
3161
+ // allowed to be absent - the database supplies the value.
3162
+ if ((val === undefined || val === null) &&
3163
+ attr.defaultValue !== undefined &&
3164
+ attr.allowNull === false) {
3165
+ continue;
3166
+ }
3161
3167
  // Check allowNull regardless of validate option
3162
3168
  if ((val === undefined || val === null) && attr.allowNull === false) {
3163
3169
  validationErrors.push({
@@ -3576,9 +3582,18 @@ class Prorm extends events_1.EventEmitter {
3576
3582
  /**
3577
3583
  * Count records
3578
3584
  */
3585
+ /**
3586
+ * Count matching rows with a real aggregate query.
3587
+ *
3588
+ * This used to delegate to `findAndCountAll`, which fetched every
3589
+ * matching row (including eager-loading its associations) and returned
3590
+ * `rows.length` - so counting a large table transferred the whole table,
3591
+ * and `distinct`, `col` and `group` were silently discarded.
3592
+ */
3579
3593
  async count(countOptions = {}) {
3580
- const result = await this.findAndCountAll(countOptions);
3581
- return result.count;
3594
+ // Apply defaultScope / named scopes exactly as findAll does, so
3595
+ // `User.scope('admin').count()` counts the scoped set.
3596
+ return self._countRows(model, modelName, this._mergeScopes(countOptions));
3582
3597
  },
3583
3598
  /**
3584
3599
  * Truncate table
@@ -4208,31 +4223,28 @@ class Prorm extends events_1.EventEmitter {
4208
4223
  };
4209
4224
  // Call define to register the model
4210
4225
  const registeredModel = this.define(modelName, attributes, options);
4211
- // Apply associations declared via @HasOne/@HasMany/@BelongsTo/@BelongsToMany decorators
4226
+ // Queue associations declared via @HasOne/@HasMany/@BelongsTo/@BelongsToMany.
4227
+ //
4228
+ // These are NOT bound immediately. `@BelongsTo(() => User)` uses a thunk so
4229
+ // the reference survives a circular import, but binding used to happen right
4230
+ // here - so if `Post` was added before `User`, the association bound against
4231
+ // a class that had never been through `define()`: no `tableName`, no
4232
+ // `rawAttributes`, and `createTableFromModel` then silently skipped the
4233
+ // REFERENCES clause. Whether foreign keys were created depended on the order
4234
+ // the model files happened to be imported in. Queue instead, and flush every
4235
+ // association whose target is registered after each `addModel`.
4212
4236
  const assocMeta = (0, decorators_1.getAssociationMetadata)(modelName);
4213
4237
  if (assocMeta) {
4214
4238
  for (const assoc of assocMeta) {
4215
- const targetModel = assoc.target();
4216
- const assocOptions = {
4217
- as: assoc.as,
4218
- foreignKey: assoc.foreignKey,
4219
- onDelete: assoc.onDelete,
4220
- onUpdate: assoc.onUpdate,
4221
- hooks: assoc.hooks,
4222
- };
4223
- if (assoc.type === 'hasOne' || assoc.type === 'hasMany') {
4224
- assocOptions.sourceKey = assoc.sourceKey;
4225
- }
4226
- if (assoc.type === 'belongsTo') {
4227
- assocOptions.targetKey = assoc.targetKey;
4228
- }
4229
- if (assoc.type === 'belongsToMany') {
4230
- assocOptions.through = assoc.through;
4231
- assocOptions.otherKey = assoc.otherKey;
4232
- }
4233
- registeredModel[assoc.type](targetModel, assocOptions);
4239
+ this._pendingAssociations.push({ sourceName: modelName, source: registeredModel, assoc });
4234
4240
  }
4235
4241
  }
4242
+ this._flushPendingAssociations();
4243
+ // Remember the class so sync() can apply its @View / @MaterializedView /
4244
+ // @Trigger annotations.
4245
+ if (!this._decoratedClasses.includes(modelClass)) {
4246
+ this._decoratedClasses.push(modelClass);
4247
+ }
4236
4248
  // Replay any scopes queued on the class via addScope() before it was
4237
4249
  // registered (associations are replayed elsewhere; scopes were not).
4238
4250
  const pendingScopes = modelClass._pendingScopes;
@@ -4247,7 +4259,7 @@ class Prorm extends events_1.EventEmitter {
4247
4259
  Object.setPrototypeOf(modelClass, registeredModelObj);
4248
4260
  // Also ensure the original class has the right rawAttributes, tableName, and options
4249
4261
  modelClass.rawAttributes = attributes;
4250
- modelClass.tableName = options.tableName || (options.freezeTableName ? modelName : (0, utils_2.pluralize)(modelName.toLowerCase()));
4262
+ modelClass.tableName = deriveTableName(modelName, options);
4251
4263
  modelClass.options = options;
4252
4264
  // Set the prorm reference
4253
4265
  modelClass.prorm = this;
@@ -4327,6 +4339,9 @@ class Prorm extends events_1.EventEmitter {
4327
4339
  comment: attr?.comment,
4328
4340
  };
4329
4341
  }
4342
+ // Composite foreign keys can't be expressed as an inline column REFERENCES,
4343
+ // so they accumulate here and are merged into the table-level constraints below.
4344
+ const synthesizedFkConstraints = [];
4330
4345
  // Wire up foreign-key constraints implied by hasOne/hasMany/belongsTo
4331
4346
  // associations declared on this or other models. Associations only add
4332
4347
  // runtime getter/setter/create helpers (see models/associations.ts) - they
@@ -4344,45 +4359,99 @@ class Prorm extends events_1.EventEmitter {
4344
4359
  for (const assoc of this.associations.values()) {
4345
4360
  const assocAny = assoc;
4346
4361
  const fk = assocAny.foreignKey;
4347
- if (typeof fk !== 'string')
4348
- continue; // skip composite/array foreign keys
4362
+ if (!fk)
4363
+ continue;
4349
4364
  const sourceName = assocAny.source?.name;
4350
4365
  const targetName = assocAny.target?.name;
4366
+ // Which side of the association owns the foreign key column decides
4367
+ // both the table it references and which option names the referenced key.
4368
+ let refModel;
4369
+ let refKeyOption;
4351
4370
  if (assocAny.type === 'belongsTo' && sourceName === modelName) {
4352
4371
  // FK column lives on this (source) table, referencing the target's table
4353
- const refTable = assocAny.target?.tableName;
4354
- if (refTable && !columns[fk]?.references) {
4355
- columns[fk] = {
4356
- ...(columns[fk] || { type: 'INTEGER', allowNull: true }),
4357
- references: {
4358
- table: refTable,
4359
- field: assocAny.options?.targetKey || 'id',
4360
- onDelete: assocAny.options?.onDelete,
4361
- onUpdate: assocAny.options?.onUpdate,
4362
- },
4363
- };
4364
- }
4372
+ refModel = assocAny.target;
4373
+ refKeyOption = assocAny.options?.targetKey;
4365
4374
  }
4366
4375
  else if ((assocAny.type === 'hasMany' || assocAny.type === 'hasOne') &&
4367
4376
  targetName === modelName) {
4368
4377
  // FK column lives on this (target) table, referencing the source's table
4369
- const refTable = assocAny.source?.tableName;
4370
- if (refTable && !columns[fk]?.references) {
4371
- columns[fk] = {
4372
- ...(columns[fk] || { type: 'INTEGER', allowNull: true }),
4378
+ refModel = assocAny.source;
4379
+ refKeyOption = assocAny.options?.sourceKey;
4380
+ }
4381
+ else {
4382
+ continue;
4383
+ }
4384
+ const refTable = refModel?.tableName;
4385
+ if (!refTable)
4386
+ continue;
4387
+ // Composite foreign keys (`foreignKey: ['orgId', 'userId']`) are a
4388
+ // supported option shape and were previously dropped outright here, so
4389
+ // they produced neither a column nor a constraint. Normalize both forms
4390
+ // to arrays and pair each local column with its referenced key.
4391
+ const fkCols = Array.isArray(fk) ? fk : [fk];
4392
+ const refKeys = Array.isArray(refKeyOption)
4393
+ ? refKeyOption
4394
+ : refKeyOption
4395
+ ? [refKeyOption]
4396
+ : fkCols.length === 1
4397
+ ? [resolveModelPrimaryKeyAttr(refModel)]
4398
+ : [];
4399
+ if (refKeys.length !== fkCols.length) {
4400
+ throw new errors_1.AssociationError(`${assocAny.type} association between ${sourceName} and ${targetName} declares ` +
4401
+ `${fkCols.length} foreign key column(s) (${fkCols.join(', ')}) but ` +
4402
+ `${refKeys.length} referenced key(s)` +
4403
+ (refKeys.length ? ` (${refKeys.join(', ')})` : '') +
4404
+ `. Pass a matching ${assocAny.type === 'belongsTo' ? 'targetKey' : 'sourceKey'} array.`, { association: assocAny.as });
4405
+ }
4406
+ // Synthesize any missing column with a type that actually matches the
4407
+ // key it references, rather than assuming INTEGER.
4408
+ fkCols.forEach((col, i) => {
4409
+ if (!columns[col]) {
4410
+ columns[col] = {
4411
+ type: resolveForeignKeyColumnType(refModel, refKeys[i]),
4412
+ allowNull: true,
4413
+ };
4414
+ }
4415
+ });
4416
+ if (fkCols.length === 1) {
4417
+ // Single-column keys stay as an inline column-level REFERENCES.
4418
+ if (!columns[fkCols[0]].references) {
4419
+ columns[fkCols[0]] = {
4420
+ ...columns[fkCols[0]],
4373
4421
  references: {
4374
4422
  table: refTable,
4375
- field: assocAny.options?.sourceKey || 'id',
4423
+ field: refKeys[0],
4376
4424
  onDelete: assocAny.options?.onDelete,
4377
4425
  onUpdate: assocAny.options?.onUpdate,
4378
4426
  },
4379
4427
  };
4380
4428
  }
4381
4429
  }
4430
+ else {
4431
+ // A composite key can only be expressed as a table-level constraint.
4432
+ const constraintName = `fk_${tableName}_${fkCols.join('_')}`;
4433
+ synthesizedFkConstraints.push({
4434
+ name: constraintName,
4435
+ type: 'FOREIGN KEY',
4436
+ fields: fkCols,
4437
+ references: {
4438
+ table: refTable,
4439
+ field: refKeys,
4440
+ onDelete: assocAny.options?.onDelete,
4441
+ onUpdate: assocAny.options?.onUpdate,
4442
+ },
4443
+ });
4444
+ }
4382
4445
  }
4383
4446
  }
4384
- // Add auto-id column if no primary key is defined in attributes
4385
- if (!Object.values(columns).some((col) => col.primaryKey) && !columns['id']) {
4447
+ // Add auto-id column if no primary key is defined in attributes.
4448
+ // A composite primary key can only be declared as a table-level constraint,
4449
+ // so that counts as "has a primary key" too - otherwise an `id` column was
4450
+ // injected alongside it and the table came out with two primary keys.
4451
+ const hasTablePrimaryKey = (options.constraints || []).some((c) => c?.type === 'PRIMARY KEY');
4452
+ if (!Object.values(columns).some((col) => col.primaryKey) &&
4453
+ !columns['id'] &&
4454
+ !hasTablePrimaryKey) {
4386
4455
  const withId = {
4387
4456
  id: {
4388
4457
  type: 'INTEGER',
@@ -4444,6 +4513,10 @@ class Prorm extends events_1.EventEmitter {
4444
4513
  : undefined,
4445
4514
  check: c.check,
4446
4515
  }));
4516
+ const allTableConstraints = [
4517
+ ...(tableConstraints || []),
4518
+ ...synthesizedFkConstraints,
4519
+ ];
4447
4520
  await this.dialect.createTable(tableName, columns, {
4448
4521
  ifNotExists: true,
4449
4522
  engine: options.engine,
@@ -4454,7 +4527,7 @@ class Prorm extends events_1.EventEmitter {
4454
4527
  initialAutoIncrement: options.initialAutoIncrement,
4455
4528
  uniqueKeys: options.uniqueKeys,
4456
4529
  indexes: tableIndexes,
4457
- constraints: tableConstraints,
4530
+ constraints: allTableConstraints,
4458
4531
  });
4459
4532
  }
4460
4533
  /**
@@ -5986,6 +6059,15 @@ $$ LANGUAGE plpgsql;`;
5986
6059
  this.pendingTableCreates = [];
5987
6060
  await Promise.all(pending);
5988
6061
  }
6062
+ // Bind any decorator associations whose target became available after the
6063
+ // source model was added, then surface the ones that never resolved rather
6064
+ // than creating tables with silently missing foreign keys.
6065
+ this._flushPendingAssociations();
6066
+ const unresolved = this._unresolvedAssociationTargets();
6067
+ if (unresolved.length > 0) {
6068
+ throw new errors_1.AssociationError(`Cannot sync: ${unresolved.length} association(s) reference a model that was never ` +
6069
+ `registered (${unresolved.join('; ')}). Pass every model to addModel()/define().`);
6070
+ }
5989
6071
  const { force = false, alter = false, match, logging = false, hooks = true, indexes = true, constraints = true, } = options;
5990
6072
  // Default options
5991
6073
  const syncOptions = {
@@ -6033,6 +6115,9 @@ $$ LANGUAGE plpgsql;`;
6033
6115
  // Default mode: create missing tables only
6034
6116
  await this.syncCreate();
6035
6117
  }
6118
+ // Apply annotation-declared DDL (@View / @MaterializedView / @Trigger /
6119
+ // @Procedure) now that the tables they depend on exist.
6120
+ await this.syncSchemaObjects();
6036
6121
  // Run afterSync hook if enabled
6037
6122
  if (hooks) {
6038
6123
  // Run global afterSync hooks
@@ -6055,6 +6140,342 @@ $$ LANGUAGE plpgsql;`;
6055
6140
  }
6056
6141
  return this;
6057
6142
  }
6143
+ /**
6144
+ * Register a class carrying `@Procedure` / `@SqlFunction` annotations so that
6145
+ * `sync()` creates them.
6146
+ *
6147
+ * Procedures are declared on plain classes rather than models, so there is no
6148
+ * other point at which the ORM would learn about them.
6149
+ */
6150
+ /**
6151
+ * Register an object or key-value store for use by `@ExternalField`.
6152
+ *
6153
+ * Accepts any of the shipped stores (S3, MinIO, R2, GCS, Azure Blob, Redis,
6154
+ * ...) directly - the differing method names are normalized internally - or
6155
+ * any object implementing `ExternalStoreAdapter`.
6156
+ *
6157
+ * ```typescript
6158
+ * const s3 = new S3Store({ region: 'us-east-1' });
6159
+ * await s3.connect();
6160
+ * prorm.registerStore('assets', s3, { defaultBucket: 'avatars' });
6161
+ * ```
6162
+ */
6163
+ registerStore(name, store, options = {}) {
6164
+ this.externalStores.register(name, store, options);
6165
+ return this;
6166
+ }
6167
+ registerProcedures(procedureClass) {
6168
+ if (!this._procedureClasses.includes(procedureClass)) {
6169
+ this._procedureClasses.push(procedureClass);
6170
+ }
6171
+ return this;
6172
+ }
6173
+ /**
6174
+ * Apply the DDL declared by annotations: `@View`, `@MaterializedView`,
6175
+ * `@Trigger` on model classes, and `@Procedure` / `@SqlFunction` on classes
6176
+ * passed to `registerProcedures()`.
6177
+ *
6178
+ * These registries previously generated correct SQL that nothing executed -
6179
+ * `ViewRegistry`, `getTriggers` and `ProcedureRegistry` were referenced only
6180
+ * from inside `src/decorators/`, so annotating a class produced a string the
6181
+ * user still had to fetch and hand to `prorm.query()` themselves.
6182
+ */
6183
+ async syncSchemaObjects() {
6184
+ if (!this.dialect)
6185
+ return;
6186
+ const dialectName = this.dialect.name;
6187
+ for (const modelClass of this._decoratedClasses) {
6188
+ // Views / materialized views
6189
+ try {
6190
+ if ((0, view_1.getViewOptions)(modelClass)) {
6191
+ await this.query(view_1.ViewRegistry.getSQL(modelClass, dialectName));
6192
+ this.logger.info(`Created view for ${modelClass.name}`);
6193
+ }
6194
+ }
6195
+ catch (error) {
6196
+ this.logger.error(`Failed to create view for ${modelClass.name}`, { error });
6197
+ throw error;
6198
+ }
6199
+ try {
6200
+ if ((0, view_2.getMaterializedViewOptions)(modelClass)) {
6201
+ await this.query(view_2.MaterializedViewRegistry.getSQL(modelClass, dialectName));
6202
+ this.logger.info(`Created materialized view for ${modelClass.name}`);
6203
+ }
6204
+ }
6205
+ catch (error) {
6206
+ this.logger.error(`Failed to create materialized view for ${modelClass.name}`, { error });
6207
+ throw error;
6208
+ }
6209
+ // Triggers
6210
+ const triggers = (0, trigger_1.getTriggers)(modelClass);
6211
+ if (triggers.length > 0) {
6212
+ const tableName = modelClass.tableName || deriveTableName(modelClass.name, undefined);
6213
+ for (const trigger of triggers) {
6214
+ try {
6215
+ const sql = (0, trigger_1.buildCreateTriggerSQL)(tableName, trigger, dialectName);
6216
+ // The postgres builder returns the function and the trigger joined
6217
+ // by ';\n\n'; most drivers reject multi-statement strings.
6218
+ for (const stmt of sql.split(/;\s*\n\s*\n/)) {
6219
+ const trimmed = stmt.trim().replace(/;$/, '');
6220
+ if (trimmed)
6221
+ await this.query(trimmed);
6222
+ }
6223
+ this.logger.info(`Created trigger ${trigger.name} on ${tableName}`);
6224
+ }
6225
+ catch (error) {
6226
+ this.logger.error(`Failed to create trigger ${trigger.name}`, { error });
6227
+ throw error;
6228
+ }
6229
+ }
6230
+ }
6231
+ }
6232
+ // Stored procedures and SQL functions
6233
+ for (const procClass of this._procedureClasses) {
6234
+ for (const meta of procedure_1.ProcedureRegistry.getAll(procClass)) {
6235
+ try {
6236
+ const sql = procedure_1.ProcedureRegistry.getSQL(procClass, meta.methodKey);
6237
+ await this.query(sql);
6238
+ this.logger.info(`Created ${meta.kind ?? 'procedure'} ${meta.options?.name ?? meta.methodKey}`);
6239
+ }
6240
+ catch (error) {
6241
+ this.logger.error(`Failed to create procedure ${meta.methodKey}`, { error });
6242
+ throw error;
6243
+ }
6244
+ }
6245
+ }
6246
+ }
6247
+ /**
6248
+ * Bind every queued decorator association whose target model is registered.
6249
+ *
6250
+ * Called after each `addModel()`, and again by `sync()` so that a model graph
6251
+ * registered in any order ends up fully wired before tables are created.
6252
+ * Associations whose target is still unregistered stay queued.
6253
+ */
6254
+ _flushPendingAssociations() {
6255
+ if (this._pendingAssociations.length === 0)
6256
+ return;
6257
+ const stillPending = [];
6258
+ for (const entry of this._pendingAssociations) {
6259
+ const { source, assoc } = entry;
6260
+ let targetModel;
6261
+ try {
6262
+ targetModel = assoc.target();
6263
+ }
6264
+ catch {
6265
+ // The thunk can still throw while a circular import is mid-flight.
6266
+ stillPending.push(entry);
6267
+ continue;
6268
+ }
6269
+ const targetName = targetModel?.name;
6270
+ const registeredTarget = targetName ? this._models.get(targetName) : undefined;
6271
+ if (!registeredTarget) {
6272
+ stillPending.push(entry);
6273
+ continue;
6274
+ }
6275
+ const assocOptions = {
6276
+ as: assoc.as,
6277
+ foreignKey: assoc.foreignKey,
6278
+ onDelete: assoc.onDelete,
6279
+ onUpdate: assoc.onUpdate,
6280
+ hooks: assoc.hooks,
6281
+ };
6282
+ if (assoc.type === 'hasOne' || assoc.type === 'hasMany') {
6283
+ assocOptions.sourceKey = assoc.sourceKey;
6284
+ }
6285
+ if (assoc.type === 'belongsTo') {
6286
+ assocOptions.targetKey = assoc.targetKey;
6287
+ }
6288
+ if (assoc.type === 'belongsToMany') {
6289
+ assocOptions.through = assoc.through;
6290
+ assocOptions.otherKey = assoc.otherKey;
6291
+ }
6292
+ source[assoc.type](registeredTarget, assocOptions);
6293
+ }
6294
+ this._pendingAssociations = stillPending;
6295
+ }
6296
+ /**
6297
+ * Names of models referenced by a decorator association that were never
6298
+ * registered, so callers can report them rather than silently skipping.
6299
+ */
6300
+ _unresolvedAssociationTargets() {
6301
+ const names = new Set();
6302
+ for (const { sourceName, assoc } of this._pendingAssociations) {
6303
+ let targetName = 'unknown';
6304
+ try {
6305
+ targetName = assoc.target()?.name ?? 'unknown';
6306
+ }
6307
+ catch {
6308
+ /* keep 'unknown' */
6309
+ }
6310
+ names.add(`${sourceName}.${assoc.as ?? assoc.type} -> ${targetName}`);
6311
+ }
6312
+ return Array.from(names);
6313
+ }
6314
+ /**
6315
+ * Issue a real `SELECT COUNT(...)` for a model.
6316
+ *
6317
+ * Honors `where`, paranoid filtering, filtering (`required`) includes,
6318
+ * `distinct`/`col` and `group`:
6319
+ * - `col` picks the counted expression (`COUNT("col")`)
6320
+ * - `distinct: true` counts distinct values of `col`, or of the primary key
6321
+ * - `group` returns the number of groups
6322
+ */
6323
+ async _countRows(model, modelName, options = {}) {
6324
+ if (!this.dialect) {
6325
+ throw new Error('Database not connected');
6326
+ }
6327
+ const dialect = this.dialect;
6328
+ const opts = options;
6329
+ const modelOptions = model.options;
6330
+ const isParanoid = modelOptions?.paranoid === true;
6331
+ const deletedAt = modelOptions?.deletedAt || 'deletedAt';
6332
+ let where = options.where;
6333
+ if (isParanoid && options.paranoid !== false && model.rawAttributes?.[deletedAt]) {
6334
+ where = { ...where, [deletedAt]: { $isNull: true } };
6335
+ }
6336
+ // Filtering includes restrict the counted set exactly as they restrict findAll.
6337
+ if (options.include && options.include.length > 0) {
6338
+ for (const rawInclude of options.include) {
6339
+ const filter = await (0, eager_load_1.resolveRequiredIncludeFilter)(this.eagerLoadContext(), model, modelName, rawInclude);
6340
+ if (!filter)
6341
+ continue;
6342
+ if (filter.values.length === 0)
6343
+ return 0;
6344
+ const existing = where?.[filter.parentAttr];
6345
+ where = {
6346
+ ...where,
6347
+ [filter.parentAttr]: existing
6348
+ ? { $and: [existing, { $in: filter.values }] }
6349
+ : { $in: filter.values },
6350
+ };
6351
+ }
6352
+ }
6353
+ const pkAttr = resolveModelPrimaryKeyAttr(model);
6354
+ const countCol = opts.col;
6355
+ const distinct = opts.distinct === true;
6356
+ let countExpr;
6357
+ if (distinct) {
6358
+ countExpr = `COUNT(DISTINCT ${dialect.escapeId(countCol || pkAttr)})`;
6359
+ }
6360
+ else if (countCol) {
6361
+ countExpr = `COUNT(${dialect.escapeId(countCol)})`;
6362
+ }
6363
+ else {
6364
+ countExpr = 'COUNT(*)';
6365
+ }
6366
+ const tableName = model.tableName;
6367
+ const qualified = model.schema
6368
+ ? `${dialect.escapeId(model.schema)}.${dialect.escapeId(tableName)}`
6369
+ : dialect.escapeId(tableName);
6370
+ const groupFields = opts.group
6371
+ ? (Array.isArray(opts.group) ? opts.group : [opts.group]).map((g) => typeof g === 'string' ? g : String(g))
6372
+ : undefined;
6373
+ let sql = `SELECT ${countExpr} AS ${dialect.escapeId('count')} FROM ${qualified}`;
6374
+ let values = [];
6375
+ if (where && Object.keys(where).length > 0) {
6376
+ const built = dialect.buildWhereClause(where);
6377
+ sql += ` WHERE ${built.sql}`;
6378
+ values = built.values;
6379
+ }
6380
+ if (groupFields?.length) {
6381
+ sql += ` GROUP BY ${groupFields.map((g) => dialect.escapeId(g)).join(', ')}`;
6382
+ }
6383
+ const finalSql = dialect.replaceReplacements(sql, values);
6384
+ const result = await dialect.query(finalSql);
6385
+ const rows = result.rows || [];
6386
+ // With GROUP BY the aggregate returns one row per group; the count is the
6387
+ // number of groups, matching how callers use `count({ group })`.
6388
+ if (groupFields?.length)
6389
+ return rows.length;
6390
+ if (!rows.length)
6391
+ return 0;
6392
+ const raw = rows[0].count ?? rows[0].COUNT ?? Object.values(rows[0])[0];
6393
+ return Number(raw) || 0;
6394
+ }
6395
+ /**
6396
+ * The view of this instance that the shared eager-loader needs.
6397
+ */
6398
+ eagerLoadContext() {
6399
+ return {
6400
+ // Materialize to an array: the loader scans this once per include and
6401
+ // again for every nested level, and a Map iterator would be exhausted
6402
+ // after the first scan (leaving later includes with no associations to
6403
+ // match against).
6404
+ associations: Array.from(this.associations.values()),
6405
+ models: this._models,
6406
+ };
6407
+ }
6408
+ /**
6409
+ * Order models so that every table is created after the tables it references.
6410
+ *
6411
+ * Table creation used to follow `modelDefinitions` insertion order, i.e. the
6412
+ * order the user happened to call `define()` in, while `createTableFromModel`
6413
+ * simultaneously emitted REFERENCES clauses. On any dialect that validates
6414
+ * foreign keys at creation time, defining `Post` before `User` failed. The
6415
+ * ordering is derived from the association graph so declaration order stops
6416
+ * mattering.
6417
+ *
6418
+ * Cycles (two models referencing each other) can't be resolved by ordering
6419
+ * alone; those models are emitted last in declaration order, which is the same
6420
+ * behaviour as before for that case.
6421
+ */
6422
+ orderModelsByDependency() {
6423
+ const entries = Array.from(this.modelDefinitions.entries());
6424
+ const byName = new Map(entries.map(([name]) => [name, true]));
6425
+ // dependsOn: model -> set of model names whose tables must exist first.
6426
+ const dependsOn = new Map();
6427
+ for (const [name] of entries)
6428
+ dependsOn.set(name, new Set());
6429
+ for (const assoc of this.associations.values()) {
6430
+ const a = assoc;
6431
+ const sourceName = a.source?.name;
6432
+ const targetName = a.target?.name;
6433
+ if (!sourceName || !targetName || sourceName === targetName)
6434
+ continue;
6435
+ if (!byName.has(sourceName) || !byName.has(targetName))
6436
+ continue;
6437
+ if (a.type === 'belongsTo') {
6438
+ // FK lives on the source, pointing at the target: target first.
6439
+ dependsOn.get(sourceName).add(targetName);
6440
+ }
6441
+ else if (a.type === 'hasMany' || a.type === 'hasOne') {
6442
+ // FK lives on the target, pointing at the source: source first.
6443
+ dependsOn.get(targetName).add(sourceName);
6444
+ }
6445
+ }
6446
+ const ordered = [];
6447
+ const emitted = new Set();
6448
+ const defs = new Map(entries);
6449
+ // Repeatedly emit every model whose dependencies are already satisfied,
6450
+ // preserving declaration order within each pass for stable output.
6451
+ let progress = true;
6452
+ while (progress) {
6453
+ progress = false;
6454
+ for (const [name] of entries) {
6455
+ if (emitted.has(name))
6456
+ continue;
6457
+ const deps = dependsOn.get(name);
6458
+ let ready = true;
6459
+ for (const dep of deps) {
6460
+ if (!emitted.has(dep)) {
6461
+ ready = false;
6462
+ break;
6463
+ }
6464
+ }
6465
+ if (ready) {
6466
+ ordered.push([name, defs.get(name)]);
6467
+ emitted.add(name);
6468
+ progress = true;
6469
+ }
6470
+ }
6471
+ }
6472
+ // Anything left is part of a dependency cycle - emit in declaration order.
6473
+ for (const [name, def] of entries) {
6474
+ if (!emitted.has(name))
6475
+ ordered.push([name, def]);
6476
+ }
6477
+ return ordered;
6478
+ }
6058
6479
  /**
6059
6480
  * Default sync mode - creates missing tables only (safe)
6060
6481
  */
@@ -6062,16 +6483,14 @@ $$ LANGUAGE plpgsql;`;
6062
6483
  if (!this.dialect) {
6063
6484
  throw new Error('Database not connected');
6064
6485
  }
6065
- // Get all registered models and their definitions
6066
- for (const [modelName, modelDef] of this.modelDefinitions) {
6067
- const tableName = modelDef.options.tableName || modelName;
6486
+ // Read the existing table list once rather than once per model.
6487
+ const tables = new Set(await this.dialect.showTables());
6488
+ for (const [modelName, modelDef] of this.orderModelsByDependency()) {
6489
+ const tableName = deriveTableName(modelName, modelDef.options);
6068
6490
  try {
6069
- // Check if table exists
6070
- const tables = await this.dialect.showTables();
6071
- const tableExists = tables.includes(tableName);
6072
- if (!tableExists) {
6073
- // Create the table
6491
+ if (!tables.has(tableName)) {
6074
6492
  await this.createTableFromModel(tableName, modelDef.attributes, modelDef.options, modelName);
6493
+ tables.add(tableName);
6075
6494
  this.logger.info(`Created table: ${tableName}`);
6076
6495
  }
6077
6496
  else {
@@ -6103,9 +6522,9 @@ $$ LANGUAGE plpgsql;`;
6103
6522
  this.logger.warn(`Failed to drop table ${tableName}`, { error });
6104
6523
  }
6105
6524
  }
6106
- // Now create all tables fresh
6107
- for (const [modelName, modelDef] of this.modelDefinitions) {
6108
- const tableName = modelDef.options.tableName || (modelDef.options.freezeTableName ? modelName : (0, utils_2.pluralize)(modelName.toLowerCase()));
6525
+ // Now create all tables fresh, parents before children.
6526
+ for (const [modelName, modelDef] of this.orderModelsByDependency()) {
6527
+ const tableName = deriveTableName(modelName, modelDef.options);
6109
6528
  try {
6110
6529
  await this.createTableFromModel(tableName, modelDef.attributes, modelDef.options, modelName);
6111
6530
  this.logger.info(`Created table: ${tableName}`);
@@ -6126,9 +6545,9 @@ $$ LANGUAGE plpgsql;`;
6126
6545
  }
6127
6546
  // Get all tables in the database
6128
6547
  const existingTables = await this.dialect.showTables();
6129
- // Process each model
6130
- for (const [modelName, modelDef] of this.modelDefinitions) {
6131
- const tableName = modelDef.options.tableName || (modelDef.options.freezeTableName ? modelName : (0, utils_2.pluralize)(modelName.toLowerCase()));
6548
+ // Process each model, parents before children.
6549
+ for (const [modelName, modelDef] of this.orderModelsByDependency()) {
6550
+ const tableName = deriveTableName(modelName, modelDef.options);
6132
6551
  const modelAttributes = modelDef.attributes;
6133
6552
  const modelOptions = modelDef.options;
6134
6553
  try {