@warlock.js/cascade 4.0.48 → 4.0.59

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 (401) hide show
  1. package/cjs/context/database-data-source-context.d.ts +29 -0
  2. package/cjs/context/database-data-source-context.d.ts.map +1 -0
  3. package/cjs/context/database-data-source-context.js +28 -0
  4. package/cjs/context/database-data-source-context.js.map +1 -0
  5. package/cjs/context/database-transaction-context.d.ts +31 -0
  6. package/cjs/context/database-transaction-context.d.ts.map +1 -0
  7. package/cjs/context/database-transaction-context.js +34 -0
  8. package/cjs/context/database-transaction-context.js.map +1 -0
  9. package/cjs/contracts/database-driver.contract.d.ts +143 -0
  10. package/cjs/contracts/database-driver.contract.d.ts.map +1 -0
  11. package/cjs/contracts/database-id-generator.contract.d.ts +109 -0
  12. package/cjs/contracts/database-id-generator.contract.d.ts.map +1 -0
  13. package/cjs/contracts/database-remover.contract.d.ts +104 -0
  14. package/cjs/contracts/database-remover.contract.d.ts.map +1 -0
  15. package/cjs/contracts/database-restorer.contract.d.ts +143 -0
  16. package/cjs/contracts/database-restorer.contract.d.ts.map +1 -0
  17. package/cjs/contracts/database-writer.contract.d.ts +119 -0
  18. package/cjs/contracts/database-writer.contract.d.ts.map +1 -0
  19. package/cjs/contracts/driver-blueprint.contract.d.ts +45 -0
  20. package/cjs/contracts/driver-blueprint.contract.d.ts.map +1 -0
  21. package/cjs/contracts/index.d.ts +10 -0
  22. package/cjs/contracts/index.d.ts.map +1 -0
  23. package/cjs/contracts/migration-driver.contract.d.ts +365 -0
  24. package/cjs/contracts/migration-driver.contract.d.ts.map +1 -0
  25. package/cjs/contracts/query-builder.contract.d.ts +1128 -0
  26. package/cjs/contracts/query-builder.contract.d.ts.map +1 -0
  27. package/cjs/contracts/sync-adapter.contract.d.ts +58 -0
  28. package/cjs/contracts/sync-adapter.contract.d.ts.map +1 -0
  29. package/cjs/data-source/data-source-registry.d.ts +104 -0
  30. package/cjs/data-source/data-source-registry.d.ts.map +1 -0
  31. package/cjs/data-source/data-source-registry.js +138 -0
  32. package/cjs/data-source/data-source-registry.js.map +1 -0
  33. package/cjs/data-source/data-source.d.ts +106 -0
  34. package/cjs/data-source/data-source.d.ts.map +1 -0
  35. package/cjs/data-source/data-source.js +77 -0
  36. package/cjs/data-source/data-source.js.map +1 -0
  37. package/cjs/database-dirty-tracker.d.ts +253 -0
  38. package/cjs/database-dirty-tracker.d.ts.map +1 -0
  39. package/cjs/database-dirty-tracker.js +389 -0
  40. package/cjs/database-dirty-tracker.js.map +1 -0
  41. package/cjs/drivers/mongo/mongo-id-generator.d.ts +116 -0
  42. package/cjs/drivers/mongo/mongo-id-generator.d.ts.map +1 -0
  43. package/cjs/drivers/mongo/mongo-id-generator.js +149 -0
  44. package/cjs/drivers/mongo/mongo-id-generator.js.map +1 -0
  45. package/cjs/drivers/mongo/mongo-migration-driver.d.ts +228 -0
  46. package/cjs/drivers/mongo/mongo-migration-driver.d.ts.map +1 -0
  47. package/cjs/drivers/mongo/mongo-migration-driver.js +524 -0
  48. package/cjs/drivers/mongo/mongo-migration-driver.js.map +1 -0
  49. package/cjs/drivers/mongo/mongo-query-builder.d.ts +922 -0
  50. package/cjs/drivers/mongo/mongo-query-builder.d.ts.map +1 -0
  51. package/cjs/drivers/mongo/mongo-query-builder.js +1740 -0
  52. package/cjs/drivers/mongo/mongo-query-builder.js.map +1 -0
  53. package/cjs/drivers/mongo/mongo-query-operations.d.ts +226 -0
  54. package/cjs/drivers/mongo/mongo-query-operations.d.ts.map +1 -0
  55. package/cjs/drivers/mongo/mongo-query-operations.js +270 -0
  56. package/cjs/drivers/mongo/mongo-query-operations.js.map +1 -0
  57. package/cjs/drivers/mongo/mongo-query-parser.d.ts +262 -0
  58. package/cjs/drivers/mongo/mongo-query-parser.d.ts.map +1 -0
  59. package/cjs/drivers/mongo/mongo-query-parser.js +1351 -0
  60. package/cjs/drivers/mongo/mongo-query-parser.js.map +1 -0
  61. package/cjs/drivers/mongo/mongo-sync-adapter.d.ts +79 -0
  62. package/cjs/drivers/mongo/mongo-sync-adapter.d.ts.map +1 -0
  63. package/cjs/drivers/mongo/mongo-sync-adapter.js +146 -0
  64. package/cjs/drivers/mongo/mongo-sync-adapter.js.map +1 -0
  65. package/cjs/drivers/mongo/mongodb-blueprint.d.ts +30 -0
  66. package/cjs/drivers/mongo/mongodb-blueprint.d.ts.map +1 -0
  67. package/cjs/drivers/mongo/mongodb-blueprint.js +51 -0
  68. package/cjs/drivers/mongo/mongodb-blueprint.js.map +1 -0
  69. package/cjs/drivers/mongo/mongodb-driver.d.ts +188 -0
  70. package/cjs/drivers/mongo/mongodb-driver.d.ts.map +1 -0
  71. package/cjs/drivers/mongo/mongodb-driver.js +411 -0
  72. package/cjs/drivers/mongo/mongodb-driver.js.map +1 -0
  73. package/cjs/drivers/mongo/types.d.ts +43 -0
  74. package/cjs/drivers/mongo/types.d.ts.map +1 -0
  75. package/cjs/errors/missing-data-source.error.d.ts +22 -0
  76. package/cjs/errors/missing-data-source.error.d.ts.map +1 -0
  77. package/cjs/errors/missing-data-source.error.js +29 -0
  78. package/cjs/errors/missing-data-source.error.js.map +1 -0
  79. package/cjs/events/model-events.d.ts +231 -0
  80. package/cjs/events/model-events.d.ts.map +1 -0
  81. package/cjs/events/model-events.js +259 -0
  82. package/cjs/events/model-events.js.map +1 -0
  83. package/cjs/expressions/aggregate-expressions.d.ts +215 -0
  84. package/cjs/expressions/aggregate-expressions.d.ts.map +1 -0
  85. package/cjs/expressions/aggregate-expressions.js +221 -0
  86. package/cjs/expressions/aggregate-expressions.js.map +1 -0
  87. package/cjs/expressions/index.d.ts +2 -0
  88. package/cjs/expressions/index.d.ts.map +1 -0
  89. package/cjs/index.d.ts +41 -0
  90. package/cjs/index.d.ts.map +1 -0
  91. package/cjs/index.js +1 -267
  92. package/cjs/index.js.map +1 -1
  93. package/cjs/migration/column-builder.d.ts +167 -0
  94. package/cjs/migration/column-builder.d.ts.map +1 -0
  95. package/cjs/migration/column-builder.js +217 -0
  96. package/cjs/migration/column-builder.js.map +1 -0
  97. package/cjs/migration/foreign-key-builder.d.ts +110 -0
  98. package/cjs/migration/foreign-key-builder.d.ts.map +1 -0
  99. package/cjs/migration/foreign-key-builder.js +129 -0
  100. package/cjs/migration/foreign-key-builder.js.map +1 -0
  101. package/cjs/migration/index.d.ts +6 -0
  102. package/cjs/migration/index.d.ts.map +1 -0
  103. package/cjs/migration/migration-runner.d.ts +231 -0
  104. package/cjs/migration/migration-runner.d.ts.map +1 -0
  105. package/cjs/migration/migration-runner.js +443 -0
  106. package/cjs/migration/migration-runner.js.map +1 -0
  107. package/cjs/migration/migration.js +1346 -0
  108. package/cjs/migration/migration.js.map +1 -0
  109. package/cjs/migration/types.d.ts +132 -0
  110. package/cjs/migration/types.d.ts.map +1 -0
  111. package/cjs/model/model.d.ts +1267 -0
  112. package/cjs/model/model.d.ts.map +1 -0
  113. package/cjs/model/model.js +1463 -0
  114. package/cjs/model/model.js.map +1 -0
  115. package/cjs/model/register-model.d.ts +80 -0
  116. package/cjs/model/register-model.d.ts.map +1 -0
  117. package/cjs/model/register-model.js +91 -0
  118. package/cjs/model/register-model.js.map +1 -0
  119. package/cjs/remover/database-remover.d.ts +100 -0
  120. package/cjs/remover/database-remover.d.ts.map +1 -0
  121. package/cjs/remover/database-remover.js +209 -0
  122. package/cjs/remover/database-remover.js.map +1 -0
  123. package/cjs/restorer/database-restorer.d.ts +131 -0
  124. package/cjs/restorer/database-restorer.d.ts.map +1 -0
  125. package/cjs/restorer/database-restorer.js +425 -0
  126. package/cjs/restorer/database-restorer.js.map +1 -0
  127. package/cjs/sync/index.d.ts +12 -0
  128. package/cjs/sync/index.d.ts.map +1 -0
  129. package/cjs/sync/model-events.d.ts +62 -0
  130. package/cjs/sync/model-events.d.ts.map +1 -0
  131. package/cjs/sync/model-events.js +49 -0
  132. package/cjs/sync/model-events.js.map +1 -0
  133. package/cjs/sync/model-sync-operation.d.ts +163 -0
  134. package/cjs/sync/model-sync-operation.d.ts.map +1 -0
  135. package/cjs/sync/model-sync-operation.js +292 -0
  136. package/cjs/sync/model-sync-operation.js.map +1 -0
  137. package/cjs/sync/model-sync.d.ts +130 -0
  138. package/cjs/sync/model-sync.d.ts.map +1 -0
  139. package/cjs/sync/model-sync.js +178 -0
  140. package/cjs/sync/model-sync.js.map +1 -0
  141. package/cjs/sync/sync-context.d.ts +70 -0
  142. package/cjs/sync/sync-context.d.ts.map +1 -0
  143. package/cjs/sync/sync-context.js +101 -0
  144. package/cjs/sync/sync-context.js.map +1 -0
  145. package/cjs/sync/sync-manager.d.ts +213 -0
  146. package/cjs/sync/sync-manager.d.ts.map +1 -0
  147. package/cjs/sync/sync-manager.js +689 -0
  148. package/cjs/sync/sync-manager.js.map +1 -0
  149. package/cjs/sync/types.d.ts +289 -0
  150. package/cjs/sync/types.d.ts.map +1 -0
  151. package/cjs/types.d.ts +45 -0
  152. package/cjs/types.d.ts.map +1 -0
  153. package/cjs/utils/connect-to-database.d.ts +246 -0
  154. package/cjs/utils/connect-to-database.d.ts.map +1 -0
  155. package/cjs/utils/connect-to-database.js +92 -0
  156. package/cjs/utils/connect-to-database.js.map +1 -0
  157. package/cjs/utils/database-writer.utils.d.ts +15 -0
  158. package/cjs/utils/database-writer.utils.d.ts.map +1 -0
  159. package/cjs/utils/database-writer.utils.js +14 -0
  160. package/cjs/utils/database-writer.utils.js.map +1 -0
  161. package/cjs/utils/define-model.js +100 -0
  162. package/cjs/utils/define-model.js.map +1 -0
  163. package/cjs/utils/once-connected.d.ts +146 -0
  164. package/cjs/utils/once-connected.d.ts.map +1 -0
  165. package/cjs/utils/once-connected.js +252 -0
  166. package/cjs/utils/once-connected.js.map +1 -0
  167. package/cjs/validation/database-seal-plugins.d.ts +2 -0
  168. package/cjs/validation/database-seal-plugins.d.ts.map +1 -0
  169. package/cjs/validation/database-seal-plugins.js +4 -0
  170. package/cjs/validation/database-seal-plugins.js.map +1 -0
  171. package/cjs/validation/database-writer-validation-error.d.ts +97 -0
  172. package/cjs/validation/database-writer-validation-error.d.ts.map +1 -0
  173. package/cjs/validation/database-writer-validation-error.js +160 -0
  174. package/cjs/validation/database-writer-validation-error.js.map +1 -0
  175. package/cjs/validation/index.d.ts +3 -0
  176. package/cjs/validation/index.d.ts.map +1 -0
  177. package/cjs/validation/mutators/embed-mutator.d.ts +9 -0
  178. package/cjs/validation/mutators/embed-mutator.d.ts.map +1 -0
  179. package/cjs/validation/mutators/embed-mutator.js +33 -0
  180. package/cjs/validation/mutators/embed-mutator.js.map +1 -0
  181. package/cjs/validation/plugins/embed-validator-plugin.d.ts +24 -0
  182. package/cjs/validation/plugins/embed-validator-plugin.d.ts.map +1 -0
  183. package/cjs/validation/plugins/embed-validator-plugin.js +18 -0
  184. package/cjs/validation/plugins/embed-validator-plugin.js.map +1 -0
  185. package/cjs/validation/rules/database-model-rule.d.ts +7 -0
  186. package/cjs/validation/rules/database-model-rule.d.ts.map +1 -0
  187. package/cjs/validation/rules/database-model-rule.js +27 -0
  188. package/cjs/validation/rules/database-model-rule.js.map +1 -0
  189. package/cjs/validation/transformers/embed-model-transformer.d.ts +3 -0
  190. package/cjs/validation/transformers/embed-model-transformer.d.ts.map +1 -0
  191. package/cjs/validation/transformers/embed-model-transformer.js +18 -0
  192. package/cjs/validation/transformers/embed-model-transformer.js.map +1 -0
  193. package/cjs/validation/validators/embed-validator.d.ts +21 -0
  194. package/cjs/validation/validators/embed-validator.d.ts.map +1 -0
  195. package/cjs/validation/validators/embed-validator.js +42 -0
  196. package/cjs/validation/validators/embed-validator.js.map +1 -0
  197. package/cjs/writer/database-writer.d.ts +181 -0
  198. package/cjs/writer/database-writer.d.ts.map +1 -0
  199. package/cjs/writer/database-writer.js +402 -0
  200. package/cjs/writer/database-writer.js.map +1 -0
  201. package/esm/context/database-data-source-context.d.ts +29 -0
  202. package/esm/context/database-data-source-context.d.ts.map +1 -0
  203. package/esm/context/database-data-source-context.js +28 -0
  204. package/esm/context/database-data-source-context.js.map +1 -0
  205. package/esm/context/database-transaction-context.d.ts +31 -0
  206. package/esm/context/database-transaction-context.d.ts.map +1 -0
  207. package/esm/context/database-transaction-context.js +34 -0
  208. package/esm/context/database-transaction-context.js.map +1 -0
  209. package/esm/contracts/database-driver.contract.d.ts +143 -0
  210. package/esm/contracts/database-driver.contract.d.ts.map +1 -0
  211. package/esm/contracts/database-id-generator.contract.d.ts +109 -0
  212. package/esm/contracts/database-id-generator.contract.d.ts.map +1 -0
  213. package/esm/contracts/database-remover.contract.d.ts +104 -0
  214. package/esm/contracts/database-remover.contract.d.ts.map +1 -0
  215. package/esm/contracts/database-restorer.contract.d.ts +143 -0
  216. package/esm/contracts/database-restorer.contract.d.ts.map +1 -0
  217. package/esm/contracts/database-writer.contract.d.ts +119 -0
  218. package/esm/contracts/database-writer.contract.d.ts.map +1 -0
  219. package/esm/contracts/driver-blueprint.contract.d.ts +45 -0
  220. package/esm/contracts/driver-blueprint.contract.d.ts.map +1 -0
  221. package/esm/contracts/index.d.ts +10 -0
  222. package/esm/contracts/index.d.ts.map +1 -0
  223. package/esm/contracts/migration-driver.contract.d.ts +365 -0
  224. package/esm/contracts/migration-driver.contract.d.ts.map +1 -0
  225. package/esm/contracts/query-builder.contract.d.ts +1128 -0
  226. package/esm/contracts/query-builder.contract.d.ts.map +1 -0
  227. package/esm/contracts/sync-adapter.contract.d.ts +58 -0
  228. package/esm/contracts/sync-adapter.contract.d.ts.map +1 -0
  229. package/esm/data-source/data-source-registry.d.ts +104 -0
  230. package/esm/data-source/data-source-registry.d.ts.map +1 -0
  231. package/esm/data-source/data-source-registry.js +138 -0
  232. package/esm/data-source/data-source-registry.js.map +1 -0
  233. package/esm/data-source/data-source.d.ts +106 -0
  234. package/esm/data-source/data-source.d.ts.map +1 -0
  235. package/esm/data-source/data-source.js +77 -0
  236. package/esm/data-source/data-source.js.map +1 -0
  237. package/esm/database-dirty-tracker.d.ts +253 -0
  238. package/esm/database-dirty-tracker.d.ts.map +1 -0
  239. package/esm/database-dirty-tracker.js +389 -0
  240. package/esm/database-dirty-tracker.js.map +1 -0
  241. package/esm/drivers/mongo/mongo-id-generator.d.ts +116 -0
  242. package/esm/drivers/mongo/mongo-id-generator.d.ts.map +1 -0
  243. package/esm/drivers/mongo/mongo-id-generator.js +149 -0
  244. package/esm/drivers/mongo/mongo-id-generator.js.map +1 -0
  245. package/esm/drivers/mongo/mongo-migration-driver.d.ts +228 -0
  246. package/esm/drivers/mongo/mongo-migration-driver.d.ts.map +1 -0
  247. package/esm/drivers/mongo/mongo-migration-driver.js +524 -0
  248. package/esm/drivers/mongo/mongo-migration-driver.js.map +1 -0
  249. package/esm/drivers/mongo/mongo-query-builder.d.ts +922 -0
  250. package/esm/drivers/mongo/mongo-query-builder.d.ts.map +1 -0
  251. package/esm/drivers/mongo/mongo-query-builder.js +1740 -0
  252. package/esm/drivers/mongo/mongo-query-builder.js.map +1 -0
  253. package/esm/drivers/mongo/mongo-query-operations.d.ts +226 -0
  254. package/esm/drivers/mongo/mongo-query-operations.d.ts.map +1 -0
  255. package/esm/drivers/mongo/mongo-query-operations.js +270 -0
  256. package/esm/drivers/mongo/mongo-query-operations.js.map +1 -0
  257. package/esm/drivers/mongo/mongo-query-parser.d.ts +262 -0
  258. package/esm/drivers/mongo/mongo-query-parser.d.ts.map +1 -0
  259. package/esm/drivers/mongo/mongo-query-parser.js +1351 -0
  260. package/esm/drivers/mongo/mongo-query-parser.js.map +1 -0
  261. package/esm/drivers/mongo/mongo-sync-adapter.d.ts +79 -0
  262. package/esm/drivers/mongo/mongo-sync-adapter.d.ts.map +1 -0
  263. package/esm/drivers/mongo/mongo-sync-adapter.js +146 -0
  264. package/esm/drivers/mongo/mongo-sync-adapter.js.map +1 -0
  265. package/esm/drivers/mongo/mongodb-blueprint.d.ts +30 -0
  266. package/esm/drivers/mongo/mongodb-blueprint.d.ts.map +1 -0
  267. package/esm/drivers/mongo/mongodb-blueprint.js +51 -0
  268. package/esm/drivers/mongo/mongodb-blueprint.js.map +1 -0
  269. package/esm/drivers/mongo/mongodb-driver.d.ts +188 -0
  270. package/esm/drivers/mongo/mongodb-driver.d.ts.map +1 -0
  271. package/esm/drivers/mongo/mongodb-driver.js +411 -0
  272. package/esm/drivers/mongo/mongodb-driver.js.map +1 -0
  273. package/esm/drivers/mongo/types.d.ts +43 -0
  274. package/esm/drivers/mongo/types.d.ts.map +1 -0
  275. package/esm/errors/missing-data-source.error.d.ts +22 -0
  276. package/esm/errors/missing-data-source.error.d.ts.map +1 -0
  277. package/esm/errors/missing-data-source.error.js +29 -0
  278. package/esm/errors/missing-data-source.error.js.map +1 -0
  279. package/esm/events/model-events.d.ts +231 -0
  280. package/esm/events/model-events.d.ts.map +1 -0
  281. package/esm/events/model-events.js +259 -0
  282. package/esm/events/model-events.js.map +1 -0
  283. package/esm/expressions/aggregate-expressions.d.ts +215 -0
  284. package/esm/expressions/aggregate-expressions.d.ts.map +1 -0
  285. package/esm/expressions/aggregate-expressions.js +221 -0
  286. package/esm/expressions/aggregate-expressions.js.map +1 -0
  287. package/esm/expressions/index.d.ts +2 -0
  288. package/esm/expressions/index.d.ts.map +1 -0
  289. package/esm/index.d.ts +41 -0
  290. package/esm/index.d.ts.map +1 -0
  291. package/esm/index.js +1 -40
  292. package/esm/index.js.map +1 -1
  293. package/esm/migration/column-builder.d.ts +167 -0
  294. package/esm/migration/column-builder.d.ts.map +1 -0
  295. package/esm/migration/column-builder.js +217 -0
  296. package/esm/migration/column-builder.js.map +1 -0
  297. package/esm/migration/foreign-key-builder.d.ts +110 -0
  298. package/esm/migration/foreign-key-builder.d.ts.map +1 -0
  299. package/esm/migration/foreign-key-builder.js +129 -0
  300. package/esm/migration/foreign-key-builder.js.map +1 -0
  301. package/esm/migration/index.d.ts +6 -0
  302. package/esm/migration/index.d.ts.map +1 -0
  303. package/esm/migration/migration-runner.d.ts +231 -0
  304. package/esm/migration/migration-runner.d.ts.map +1 -0
  305. package/esm/migration/migration-runner.js +443 -0
  306. package/esm/migration/migration-runner.js.map +1 -0
  307. package/esm/migration/migration.js +1346 -0
  308. package/esm/migration/migration.js.map +1 -0
  309. package/esm/migration/types.d.ts +132 -0
  310. package/esm/migration/types.d.ts.map +1 -0
  311. package/esm/model/model.d.ts +1267 -0
  312. package/esm/model/model.d.ts.map +1 -0
  313. package/esm/model/model.js +1463 -0
  314. package/esm/model/model.js.map +1 -0
  315. package/esm/model/register-model.d.ts +80 -0
  316. package/esm/model/register-model.d.ts.map +1 -0
  317. package/esm/model/register-model.js +91 -0
  318. package/esm/model/register-model.js.map +1 -0
  319. package/esm/remover/database-remover.d.ts +100 -0
  320. package/esm/remover/database-remover.d.ts.map +1 -0
  321. package/esm/remover/database-remover.js +209 -0
  322. package/esm/remover/database-remover.js.map +1 -0
  323. package/esm/restorer/database-restorer.d.ts +131 -0
  324. package/esm/restorer/database-restorer.d.ts.map +1 -0
  325. package/esm/restorer/database-restorer.js +425 -0
  326. package/esm/restorer/database-restorer.js.map +1 -0
  327. package/esm/sync/index.d.ts +12 -0
  328. package/esm/sync/index.d.ts.map +1 -0
  329. package/esm/sync/model-events.d.ts +62 -0
  330. package/esm/sync/model-events.d.ts.map +1 -0
  331. package/esm/sync/model-events.js +49 -0
  332. package/esm/sync/model-events.js.map +1 -0
  333. package/esm/sync/model-sync-operation.d.ts +163 -0
  334. package/esm/sync/model-sync-operation.d.ts.map +1 -0
  335. package/esm/sync/model-sync-operation.js +292 -0
  336. package/esm/sync/model-sync-operation.js.map +1 -0
  337. package/esm/sync/model-sync.d.ts +130 -0
  338. package/esm/sync/model-sync.d.ts.map +1 -0
  339. package/esm/sync/model-sync.js +178 -0
  340. package/esm/sync/model-sync.js.map +1 -0
  341. package/esm/sync/sync-context.d.ts +70 -0
  342. package/esm/sync/sync-context.d.ts.map +1 -0
  343. package/esm/sync/sync-context.js +101 -0
  344. package/esm/sync/sync-context.js.map +1 -0
  345. package/esm/sync/sync-manager.d.ts +213 -0
  346. package/esm/sync/sync-manager.d.ts.map +1 -0
  347. package/esm/sync/sync-manager.js +689 -0
  348. package/esm/sync/sync-manager.js.map +1 -0
  349. package/esm/sync/types.d.ts +289 -0
  350. package/esm/sync/types.d.ts.map +1 -0
  351. package/esm/types.d.ts +45 -0
  352. package/esm/types.d.ts.map +1 -0
  353. package/esm/utils/connect-to-database.d.ts +246 -0
  354. package/esm/utils/connect-to-database.d.ts.map +1 -0
  355. package/esm/utils/connect-to-database.js +92 -0
  356. package/esm/utils/connect-to-database.js.map +1 -0
  357. package/esm/utils/database-writer.utils.d.ts +15 -0
  358. package/esm/utils/database-writer.utils.d.ts.map +1 -0
  359. package/esm/utils/database-writer.utils.js +14 -0
  360. package/esm/utils/database-writer.utils.js.map +1 -0
  361. package/esm/utils/define-model.js +100 -0
  362. package/esm/utils/define-model.js.map +1 -0
  363. package/esm/utils/once-connected.d.ts +146 -0
  364. package/esm/utils/once-connected.d.ts.map +1 -0
  365. package/esm/utils/once-connected.js +252 -0
  366. package/esm/utils/once-connected.js.map +1 -0
  367. package/esm/validation/database-seal-plugins.d.ts +2 -0
  368. package/esm/validation/database-seal-plugins.d.ts.map +1 -0
  369. package/esm/validation/database-seal-plugins.js +4 -0
  370. package/esm/validation/database-seal-plugins.js.map +1 -0
  371. package/esm/validation/database-writer-validation-error.d.ts +97 -0
  372. package/esm/validation/database-writer-validation-error.d.ts.map +1 -0
  373. package/esm/validation/database-writer-validation-error.js +160 -0
  374. package/esm/validation/database-writer-validation-error.js.map +1 -0
  375. package/esm/validation/index.d.ts +3 -0
  376. package/esm/validation/index.d.ts.map +1 -0
  377. package/esm/validation/mutators/embed-mutator.d.ts +9 -0
  378. package/esm/validation/mutators/embed-mutator.d.ts.map +1 -0
  379. package/esm/validation/mutators/embed-mutator.js +33 -0
  380. package/esm/validation/mutators/embed-mutator.js.map +1 -0
  381. package/esm/validation/plugins/embed-validator-plugin.d.ts +24 -0
  382. package/esm/validation/plugins/embed-validator-plugin.d.ts.map +1 -0
  383. package/esm/validation/plugins/embed-validator-plugin.js +18 -0
  384. package/esm/validation/plugins/embed-validator-plugin.js.map +1 -0
  385. package/esm/validation/rules/database-model-rule.d.ts +7 -0
  386. package/esm/validation/rules/database-model-rule.d.ts.map +1 -0
  387. package/esm/validation/rules/database-model-rule.js +27 -0
  388. package/esm/validation/rules/database-model-rule.js.map +1 -0
  389. package/esm/validation/transformers/embed-model-transformer.d.ts +3 -0
  390. package/esm/validation/transformers/embed-model-transformer.d.ts.map +1 -0
  391. package/esm/validation/transformers/embed-model-transformer.js +18 -0
  392. package/esm/validation/transformers/embed-model-transformer.js.map +1 -0
  393. package/esm/validation/validators/embed-validator.d.ts +21 -0
  394. package/esm/validation/validators/embed-validator.d.ts.map +1 -0
  395. package/esm/validation/validators/embed-validator.js +42 -0
  396. package/esm/validation/validators/embed-validator.js.map +1 -0
  397. package/esm/writer/database-writer.d.ts +181 -0
  398. package/esm/writer/database-writer.d.ts.map +1 -0
  399. package/esm/writer/database-writer.js +402 -0
  400. package/esm/writer/database-writer.js.map +1 -0
  401. package/package.json +61 -52
@@ -0,0 +1,1463 @@
1
+ import {get,only,set,unset,merge}from'@mongez/reinforcements';import {dataSourceRegistry}from'../data-source/data-source-registry.js';import {DatabaseDirtyTracker}from'../database-dirty-tracker.js';import {globalModelEvents,ModelEvents}from'../events/model-events.js';import {DatabaseRemover}from'../remover/database-remover.js';import {DatabaseRestorer}from'../restorer/database-restorer.js';import {modelSync}from'../sync/model-sync.js';import {DatabaseWriter}from'../writer/database-writer.js';import {getModelFromRegistry,getAllModelsFromRegistry,removeModelFromRegistery}from'./register-model.js';/**
2
+ * Sentinel value used to distinguish between undefined and missing fields.
3
+ */
4
+ const MISSING_VALUE = Symbol("missing");
5
+ /**
6
+ * WeakMap registry that associates each model constructor with its own event emitter.
7
+ */
8
+ const modelEventsRegistry = new WeakMap();
9
+ /**
10
+ * Base class that powers all Cascade models.
11
+ *
12
+ * Provides:
13
+ * - Type-safe value accessors with dot-notation support (get, set, has, unset, merge)
14
+ * - Automatic dirty tracking for efficient partial updates
15
+ * - Lifecycle event hooks (saving, created, deleting, etc.)
16
+ * - Integration with the data-source registry for multi-database support
17
+ * - Support for both per-model and global event listeners
18
+ *
19
+ * @template TSchema - The shape of the model's underlying data
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * interface UserSchema {
24
+ * id: number;
25
+ * name: string;
26
+ * email: string;
27
+ * }
28
+ *
29
+ * class User extends Model<UserSchema> {
30
+ * public static table = "users";
31
+ * }
32
+ *
33
+ * const user = new User({ name: "Alice" });
34
+ * user.set("email", "alice@example.com");
35
+ * console.log(user.hasChanges()); // true
36
+ * ```
37
+ */
38
+ class Model {
39
+ /**
40
+ * The database table or collection name associated with this model.
41
+ *
42
+ * Must be defined by each concrete model subclass.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * class User extends Model {
47
+ * public static table = "users";
48
+ * }
49
+ * ```
50
+ */
51
+ static table;
52
+ /**
53
+ * Resource for this model.
54
+ * It is a class that holds a toJSON function
55
+ * Called when the model is being converted to JSON (by calling toJSON or JSON.stringify(model))
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * class User extends Model {
60
+ * public static resource = UserResource;
61
+ * }
62
+ * ```
63
+ */
64
+ static resource;
65
+ /**
66
+ * Resource columns
67
+ * Define what columns should be sent to the resource (if any) when converting to JSON
68
+ */
69
+ static resourceColumns;
70
+ /**
71
+ * JSON keys for this model.
72
+ * This could be used if resource is not passed
73
+ * It will select only these keys from the model
74
+ * @example
75
+ * ```typescript
76
+ * class User extends Model {
77
+ * public static toJsonColumns = ["id", "name"];
78
+ * }
79
+ * ```
80
+ */
81
+ static toJsonColumns;
82
+ /**
83
+ * Data source reference for this model.
84
+ *
85
+ * Can be:
86
+ * - A string name registered in the data-source registry
87
+ * - A DataSource instance
88
+ * - Undefined (falls back to the default data source)
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * class User extends Model {
93
+ * public static dataSource = "primary";
94
+ * }
95
+ * ```
96
+ */
97
+ static dataSource;
98
+ /**
99
+ * Query builder class
100
+ */
101
+ static builder;
102
+ /**
103
+ * Primary key field name used to identify records.
104
+ *
105
+ * @default "id"
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * class User extends Model {
110
+ * public static primaryKey = "_id"; // MongoDB
111
+ * }
112
+ *
113
+ * class Product extends Model {
114
+ * public static primaryKey = "id"; // SQL
115
+ * }
116
+ * ```
117
+ */
118
+ static primaryKey = "id";
119
+ /**
120
+ * Embeded fields when document is Being embeded
121
+ */
122
+ static embed;
123
+ /**
124
+ * Validation and casting schema using @warlock.js/seal.
125
+ *
126
+ * Defines validation rules and data transformations for the model.
127
+ * Used automatically during save operations.
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * import { v } from "@warlock.js/seal";
132
+ *
133
+ * class User extends Model {
134
+ * public static schema = v.object({
135
+ * name: v.string().required().trim(),
136
+ * age: v.number().min(0).max(120),
137
+ * email: v.string().email().required().toLowerCase(),
138
+ * createdAt: v.date().default(() => new Date()),
139
+ * });
140
+ * }
141
+ * ```
142
+ */
143
+ static schema;
144
+ /**
145
+ * Strict mode behavior for unknown fields.
146
+ *
147
+ * - `"strip"`: Remove unknown fields silently (default, recommended for APIs)
148
+ * - `"fail"`: Throw validation error on unknown fields (strict validation)
149
+ * - `"allow"`: Allow unknown fields to pass through (permissive)
150
+ *
151
+ * @default "strip"
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * import { Model, type StrictMode } from "@warlock.js/cascade";
156
+ *
157
+ * class User extends Model {
158
+ * public static strictMode: StrictMode = "fail"; // Throw on unknown fields
159
+ * }
160
+ *
161
+ * const user = new User({ name: "Alice", unknownField: "value" });
162
+ * await user.save(); // DatabaseWriterValidationError: unknown field
163
+ * ```
164
+ */
165
+ static strictMode = "strip";
166
+ /**
167
+ * Auto-generate incremental `id` field on insert (NoSQL only).
168
+ *
169
+ * When enabled, the ID generator creates a sequential integer ID
170
+ * separate from the database's native ID (_id for MongoDB).
171
+ *
172
+ * **Note:** SQL databases use native AUTO_INCREMENT and don't need this.
173
+ *
174
+ * @default true
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * class User extends Model {
179
+ * public static autoGenerateId = true;
180
+ * }
181
+ *
182
+ * const user = new User({ name: "Alice" });
183
+ * await user.save();
184
+ * console.log(user.get("_id")); // ObjectId("...") - MongoDB
185
+ * console.log(user.get("id")); // 1 - Generated
186
+ * ```
187
+ */
188
+ static autoGenerateId = true;
189
+ /**
190
+ * Initial ID value for the first record.
191
+ *
192
+ * If not set, defaults to 1 or uses `randomInitialId`.
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * class User extends Model {
197
+ * public static initialId = 1000; // Start from 1000
198
+ * }
199
+ * ```
200
+ */
201
+ static initialId;
202
+ /**
203
+ * Randomly generate the initial ID.
204
+ *
205
+ * Can be:
206
+ * - `true`: Generate random ID between 10000-499999
207
+ * - Function: Custom random ID generator
208
+ * - `false`: Use `initialId` or default to 1
209
+ *
210
+ * @default false
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * class User extends Model {
215
+ * public static randomInitialId = true; // Random 10000-499999
216
+ * }
217
+ *
218
+ * class Product extends Model {
219
+ * public static randomInitialId = () => Math.floor(Math.random() * 1000000);
220
+ * }
221
+ * ```
222
+ */
223
+ static randomInitialId;
224
+ /**
225
+ * Amount to increment ID by for each new record.
226
+ *
227
+ * If not set, defaults to 1 or uses `randomIncrement`.
228
+ *
229
+ * @default 1
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * class User extends Model {
234
+ * public static incrementIdBy = 5; // Increment by 5
235
+ * }
236
+ * ```
237
+ */
238
+ static incrementIdBy = 1;
239
+ /**
240
+ * Randomly generate the increment amount.
241
+ *
242
+ * Can be:
243
+ * - `true`: Generate random increment between 1-10
244
+ * - Function: Custom random increment generator
245
+ * - `false`: Use `incrementIdBy` or default to 1
246
+ *
247
+ * @default false
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * class User extends Model {
252
+ * public static randomIncrement = true; // Random 1-10
253
+ * }
254
+ *
255
+ * class Product extends Model {
256
+ * public static randomIncrement = () => Math.floor(Math.random() * 100);
257
+ * }
258
+ * ```
259
+ */
260
+ static randomIncrement;
261
+ /**
262
+ * Created at column name.
263
+ */
264
+ static createdAtColumn = "createdAt";
265
+ /**
266
+ * Updated at column name.
267
+ */
268
+ static updatedAtColumn = "updatedAt";
269
+ /**
270
+ * Delete strategy for this model.
271
+ *
272
+ * Controls how models are deleted:
273
+ * - `"trash"` - Moves to trash collection, then deletes
274
+ * - `"permanent"` - Direct deletion (hard delete)
275
+ * - `"soft"` - Sets deletedAt timestamp (soft delete)
276
+ *
277
+ * Can be overridden by destroy() options.
278
+ * Falls back to data source default if not set.
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * class User extends Model {
283
+ * public static deleteStrategy: DeleteStrategy = "soft";
284
+ * }
285
+ * ```
286
+ */
287
+ static deleteStrategy;
288
+ /**
289
+ * Column name for soft delete timestamp.
290
+ *
291
+ * Used when delete strategy is "soft".
292
+ *
293
+ * @default "deletedAt"
294
+ *
295
+ * @example
296
+ * ```typescript
297
+ * class User extends Model {
298
+ * public static deletedAtColumn = "archivedAt";
299
+ * }
300
+ * ```
301
+ */
302
+ static deletedAtColumn = "deletedAt";
303
+ /**
304
+ * Trash table/collection name override.
305
+ *
306
+ * If not set, defaults to `{table}Trash` or data source default.
307
+ * Used when delete strategy is "trash".
308
+ *
309
+ * @example
310
+ * ```typescript
311
+ * class User extends Model {
312
+ * public static trashTable = "userRecycleBin";
313
+ * }
314
+ * ```
315
+ */
316
+ static trashTable;
317
+ /**
318
+ * Global scopes that are automatically applied to all queries.
319
+ * These scopes are inherited by child models.
320
+ */
321
+ static globalScopes = new Map();
322
+ /**
323
+ * Local scopes that can be manually applied to queries.
324
+ * These are reusable query snippets that developers opt into.
325
+ */
326
+ static localScopes = new Map();
327
+ /**
328
+ * Flag indicating whether this model instance represents a new (unsaved) record.
329
+ *
330
+ * - `true`: The model has not been persisted to the database yet
331
+ * - `false`: The model represents an existing database record
332
+ *
333
+ * This flag is used by the writer to determine whether to perform an insert or update.
334
+ */
335
+ isNew = true;
336
+ /**
337
+ * The raw mutable data backing this model instance.
338
+ *
339
+ * All field accessors (get, set, merge, etc.) operate on this object.
340
+ */
341
+ data;
342
+ /**
343
+ * Dirty tracker that monitors changes to the model's data.
344
+ *
345
+ * Tracks:
346
+ * - Which fields have been modified (dirty columns)
347
+ * - Which fields have been removed
348
+ * - Original vs. current values for each dirty field
349
+ *
350
+ * Used by the writer to generate efficient partial update payloads.
351
+ */
352
+ dirtyTracker;
353
+ /**
354
+ * Constructs a new model instance with optional initial data.
355
+ *
356
+ * Initializes the dirty tracker with a snapshot of the provided data.
357
+ *
358
+ * @param initialData - Partial data to populate the model
359
+ *
360
+ * @example
361
+ * ```typescript
362
+ * const user = new User({ name: "Alice", email: "alice@example.com" });
363
+ * ```
364
+ */
365
+ constructor(initialData = {}) {
366
+ this.data = initialData;
367
+ this.dirtyTracker = new DatabaseDirtyTracker(this.data);
368
+ }
369
+ /**
370
+ * Get a model class by its name from the global registry.
371
+ *
372
+ * Models must be decorated with @RegisterModel() to be available in the registry.
373
+ *
374
+ * @param name - The model class name
375
+ * @returns The model class or undefined if not found
376
+ *
377
+ * @example
378
+ * ```typescript
379
+ * const UserModel = Model.getModel("User");
380
+ * if (UserModel) {
381
+ * const user = await UserModel.find(1);
382
+ * }
383
+ * ```
384
+ */
385
+ static getModel(name) {
386
+ return getModelFromRegistry(name);
387
+ }
388
+ /**
389
+ * Get all registered models from the global registry.
390
+ *
391
+ * Only models decorated with @RegisterModel() will appear here.
392
+ *
393
+ * @returns A Map of all registered model classes by name
394
+ *
395
+ * @example
396
+ * ```typescript
397
+ * const allModels = Model.getAllModels();
398
+ * for (const [name, ModelClass] of allModels) {
399
+ * console.log(`Found model: ${name} with table: ${ModelClass.table}`);
400
+ * }
401
+ * ```
402
+ */
403
+ static getAllModels() {
404
+ return getAllModelsFromRegistry();
405
+ }
406
+ // ============================================================================
407
+ // STATIC SYNC METHODS
408
+ // ============================================================================
409
+ /**
410
+ * Create a sync operation for a single embedded document.
411
+ *
412
+ * When this model is updated, the target model's field
413
+ * will be updated with the embedded data.
414
+ *
415
+ * @param TargetModel - Target model class that receives data
416
+ * @param targetField - Field path in target model
417
+ * @returns Sync operation for chaining configuration
418
+ *
419
+ * @example
420
+ * ```typescript
421
+ * // When Category updates, update Product.category
422
+ * Category.sync(Product, "category");
423
+ * ```
424
+ */
425
+ static sync(TargetModel, targetField) {
426
+ return modelSync.sync(this, TargetModel, targetField);
427
+ }
428
+ /**
429
+ * Create a sync operation for an array of embedded documents.
430
+ *
431
+ * When this model is updated, the corresponding element
432
+ * in the target model's array field will be updated.
433
+ *
434
+ * @param TargetModel - Target model class that receives data
435
+ * @param targetField - Array field path in target model
436
+ * @returns Sync operation for chaining configuration
437
+ *
438
+ * @example
439
+ * ```typescript
440
+ * // When Tag updates, update Post.tags[i] where tags[i].id matches
441
+ * Tag.syncMany(Post, "tags").identifyBy("id");
442
+ * ```
443
+ */
444
+ static syncMany(TargetModel, targetField) {
445
+ return modelSync.syncMany(this, TargetModel, targetField);
446
+ }
447
+ /**
448
+ * Get model id
449
+ */
450
+ get id() {
451
+ return this.get("id");
452
+ }
453
+ get(field, defaultValue) {
454
+ return get(this.data, field, defaultValue);
455
+ }
456
+ only(fields) {
457
+ return only(this.data, fields);
458
+ }
459
+ /**
460
+ * Get a string value
461
+ */
462
+ string(key, defaultValue) {
463
+ return this.get(key, defaultValue);
464
+ }
465
+ /**
466
+ * Get a number value
467
+ */
468
+ number(key, defaultValue) {
469
+ return this.get(key, defaultValue);
470
+ }
471
+ /**
472
+ * Get a boolean value
473
+ */
474
+ boolean(key, defaultValue) {
475
+ return this.get(key, defaultValue);
476
+ }
477
+ set(field, value) {
478
+ const path = String(field);
479
+ set(this.data, path, value);
480
+ const partial = {};
481
+ set(partial, path, value);
482
+ this.dirtyTracker.mergeChanges(partial);
483
+ return this;
484
+ }
485
+ has(field) {
486
+ return get(this.data, field, MISSING_VALUE) !== MISSING_VALUE;
487
+ }
488
+ increment(field, amount) {
489
+ const value = this.get(field, 0);
490
+ const incrementedValue = value + amount;
491
+ return this.set(field, incrementedValue);
492
+ }
493
+ decrement(field, amount) {
494
+ const value = this.get(field, 0);
495
+ const decrementedValue = value - amount;
496
+ return this.set(field, decrementedValue);
497
+ }
498
+ unset(...fields) {
499
+ this.data = unset(this.data, fields);
500
+ this.dirtyTracker.unset(fields);
501
+ return this;
502
+ }
503
+ merge(values) {
504
+ this.data = merge(this.data, values);
505
+ this.dirtyTracker.mergeChanges(values);
506
+ return this;
507
+ }
508
+ /**
509
+ * Checks whether the model's data has changed since instantiation or last reset.
510
+ *
511
+ * @returns `true` if any fields have been modified or removed, `false` otherwise
512
+ *
513
+ * @example
514
+ * ```typescript
515
+ * const user = new User({ name: "Alice" });
516
+ * user.hasChanges(); // false
517
+ * user.set("name", "Bob");
518
+ * user.hasChanges(); // true
519
+ * ```
520
+ */
521
+ hasChanges() {
522
+ return this.dirtyTracker.hasChanges();
523
+ }
524
+ /**
525
+ * Check if the given column has been modified.
526
+ *
527
+ * @param column - The column name to check
528
+ * @returns `true` if the column has been modified, `false` otherwise
529
+ *
530
+ * @example
531
+ * ```typescript
532
+ * user.set("name", "Bob");
533
+ * user.isDirty("name"); // true
534
+ * ```
535
+ */
536
+ isDirty(column) {
537
+ return this.dirtyTracker.isDirty(column);
538
+ }
539
+ /**
540
+ * Retrieves all dirty columns with their old and new values.
541
+ *
542
+ * @returns A record mapping each dirty column to its previous and current value
543
+ *
544
+ * @example
545
+ * ```typescript
546
+ * user.set("name", "Bob");
547
+ * user.getDirtyColumnsWithValues();
548
+ * // { name: { oldValue: "Alice", newValue: "Bob" } }
549
+ * ```
550
+ */
551
+ getDirtyColumnsWithValues() {
552
+ return this.dirtyTracker.getDirtyColumnsWithValues();
553
+ }
554
+ /**
555
+ * Lists all columns that have been removed from the model's data.
556
+ *
557
+ * @returns An array of field names that were present initially but have been unset
558
+ *
559
+ * @example
560
+ * ```typescript
561
+ * user.unset("tempField");
562
+ * user.getRemovedColumns(); // ["tempField"]
563
+ * ```
564
+ */
565
+ getRemovedColumns() {
566
+ return this.dirtyTracker.getRemovedColumns();
567
+ }
568
+ /**
569
+ * Lists all columns that have been modified since instantiation or last reset.
570
+ *
571
+ * @returns An array of field names that have changed
572
+ *
573
+ * @example
574
+ * ```typescript
575
+ * user.set("name", "Bob");
576
+ * user.getDirtyColumns(); // ["name"]
577
+ * ```
578
+ */
579
+ getDirtyColumns() {
580
+ return this.dirtyTracker.getDirtyColumns();
581
+ }
582
+ /**
583
+ * Emits a lifecycle event to both per-model and global listeners.
584
+ *
585
+ * This method is public so that external services (like the writer) can trigger
586
+ * lifecycle events when appropriate.
587
+ *
588
+ * @param event - The event name (e.g., "saving", "created", "deleting")
589
+ * @param context - Optional context data to pass to listeners
590
+ *
591
+ * @example
592
+ * ```typescript
593
+ * await user.emitEvent("saving");
594
+ * await user.emitEvent("validated", { errors: [] });
595
+ * ```
596
+ */
597
+ async emitEvent(event, context) {
598
+ const ctor = this.constructor;
599
+ await ctor.events().emit(event, this, context);
600
+ await globalModelEvents.emit(event, this, context);
601
+ }
602
+ /**
603
+ * Resolves the data source associated with this model.
604
+ *
605
+ * Resolution order:
606
+ * 1. If `dataSource` is a string, looks it up in the data-source registry
607
+ * 2. If `dataSource` is a DataSource instance, returns it directly
608
+ * 3. Otherwise, returns the default data source from the registry
609
+ *
610
+ * @returns The resolved DataSource instance
611
+ * @throws Error if no data source is found
612
+ *
613
+ * @example
614
+ * ```typescript
615
+ * class User extends Model {
616
+ * public static dataSource = "primary";
617
+ * }
618
+ *
619
+ * const ds = User.getDataSource();
620
+ * ```
621
+ */
622
+ static getDataSource() {
623
+ const ref = this.dataSource;
624
+ let dataSource;
625
+ if (typeof ref === "string") {
626
+ dataSource = dataSourceRegistry.get(ref);
627
+ }
628
+ else if (ref) {
629
+ dataSource = ref;
630
+ }
631
+ else {
632
+ dataSource = dataSourceRegistry.get();
633
+ }
634
+ // Apply model defaults from data source (only once per model class)
635
+ if (!this.hasOwnProperty("_defaultsApplied") && dataSource.modelDefaults) {
636
+ this.applyModelDefaults(dataSource.modelDefaults);
637
+ this._defaultsApplied = true;
638
+ }
639
+ return dataSource;
640
+ }
641
+ /**
642
+ * Apply model defaults from data source configuration.
643
+ *
644
+ * This is called automatically by getDataSource() the first time
645
+ * a model accesses its data source. Defaults are only applied if
646
+ * the model doesn't already have its own value set.
647
+ *
648
+ * @param defaults - Model default configuration from data source
649
+ * @private
650
+ */
651
+ static applyModelDefaults(defaults) {
652
+ // Only apply defaults if model doesn't have its own value
653
+ // Note: autoGenerateId is not applied here as it's a driver-level setting
654
+ if (defaults.initialId !== undefined && this.initialId === undefined) {
655
+ this.initialId = defaults.initialId;
656
+ }
657
+ if (defaults.randomInitialId !== undefined && this.randomInitialId === undefined) {
658
+ this.randomInitialId = defaults.randomInitialId;
659
+ }
660
+ if (defaults.incrementIdBy !== undefined && this.incrementIdBy === undefined) {
661
+ this.incrementIdBy = defaults.incrementIdBy;
662
+ }
663
+ if (defaults.randomIncrement !== undefined && this.randomIncrement === undefined) {
664
+ this.randomIncrement = defaults.randomIncrement;
665
+ }
666
+ if (defaults.deleteStrategy !== undefined && this.deleteStrategy === undefined) {
667
+ this.deleteStrategy = defaults.deleteStrategy;
668
+ }
669
+ if (defaults.strictMode !== undefined && this.strictMode === undefined) {
670
+ this.strictMode = defaults.strictMode;
671
+ }
672
+ }
673
+ /**
674
+ * Add a global scope that is automatically applied to all queries.
675
+ *
676
+ * Global scopes are inherited by child models and applied before query execution.
677
+ * Use for security filters, multi-tenancy, soft deletes, etc.
678
+ *
679
+ * @param name - Unique name for the scope
680
+ * @param callback - Function that modifies the query
681
+ * @param options - Scope options (timing: 'before' | 'after')
682
+ *
683
+ * @example
684
+ * ```typescript
685
+ * // Multi-tenancy scope
686
+ * Model.addGlobalScope('tenant', (query) => {
687
+ * query.where('tenantId', getCurrentTenant());
688
+ * }, { timing: 'before' });
689
+ *
690
+ * // Soft delete scope
691
+ * User.addGlobalScope('notDeleted', (query) => {
692
+ * query.whereNull('deletedAt');
693
+ * });
694
+ * ```
695
+ */
696
+ static addGlobalScope(name, callback, options = {}) {
697
+ this.globalScopes.set(name, {
698
+ callback,
699
+ timing: options.timing || "before",
700
+ });
701
+ }
702
+ /**
703
+ * Remove a global scope by name.
704
+ *
705
+ * @param name - Name of the scope to remove
706
+ *
707
+ * @example
708
+ * ```typescript
709
+ * Model.removeGlobalScope('tenant');
710
+ * ```
711
+ */
712
+ static removeGlobalScope(name) {
713
+ this.globalScopes.delete(name);
714
+ }
715
+ /**
716
+ * Add a local scope that can be manually applied to queries.
717
+ *
718
+ * Local scopes are reusable query snippets that developers opt into.
719
+ * They are not automatically applied.
720
+ *
721
+ * @param name - Unique name for the scope
722
+ * @param callback - Function that modifies the query
723
+ *
724
+ * @example
725
+ * ```typescript
726
+ * // Define reusable scopes
727
+ * User.addScope('active', (query) => {
728
+ * query.where('isActive', true);
729
+ * });
730
+ *
731
+ * User.addScope('admins', (query) => {
732
+ * query.where('role', 'admin');
733
+ * });
734
+ *
735
+ * // Use explicitly
736
+ * await User.query().scope('active').get();
737
+ * await User.query().scope('admins').get();
738
+ * ```
739
+ */
740
+ static addScope(name, callback) {
741
+ this.localScopes.set(name, callback);
742
+ }
743
+ /**
744
+ * Remove a local scope by name.
745
+ *
746
+ * @param name - Name of the scope to remove
747
+ *
748
+ * @example
749
+ * ```typescript
750
+ * User.removeScope('active');
751
+ * ```
752
+ */
753
+ static removeScope(name) {
754
+ this.localScopes.delete(name);
755
+ }
756
+ /**
757
+ * Create a new query builder for this model
758
+ */
759
+ static query() {
760
+ // Call newQueryBuilder as a static method (may be overridden in child classes)
761
+ const queryBuilder = this.newQueryBuilder();
762
+ const ModelClass = this;
763
+ // Collect global scopes from base Model and child model
764
+ const allGlobalScopes = new Map([
765
+ ...Model.globalScopes,
766
+ ...this.globalScopes,
767
+ ]);
768
+ // Pass scopes to query builder
769
+ queryBuilder.pendingGlobalScopes = allGlobalScopes;
770
+ queryBuilder.availableLocalScopes = this.localScopes;
771
+ queryBuilder.disabledGlobalScopes = new Set();
772
+ // Emit fetching event
773
+ this.events().emitFetching(queryBuilder, { table: this.table, modelClass: this });
774
+ queryBuilder.hydrate((data) => {
775
+ const model = new ModelClass(data);
776
+ model.isNew = false;
777
+ return model;
778
+ });
779
+ // Wire up onFetched callback to emit model-level event
780
+ queryBuilder.onFetched(async (models, context) => {
781
+ await this.events().emit("fetched", models, context);
782
+ });
783
+ return queryBuilder;
784
+ }
785
+ /**
786
+ * Create new query builder.
787
+ *
788
+ * If the model has a static `builder` property set to a query builder class,
789
+ * it will be instantiated instead of the default driver query builder.
790
+ *
791
+ * @example
792
+ * ```typescript
793
+ * class UserQueryBuilder<T = User> extends MongoQueryBuilder<T> {
794
+ * active() { return this.where("isActive", true); }
795
+ * }
796
+ *
797
+ * class User extends Model {
798
+ * static builder = UserQueryBuilder; // That's it! ✨
799
+ * }
800
+ *
801
+ * // Now User.query() returns UserQueryBuilder<User> with autocomplete!
802
+ * ```
803
+ */
804
+ static newQueryBuilder() {
805
+ const dataSource = this.getDataSource();
806
+ // Check if model has a custom builder class
807
+ if (this.builder) {
808
+ const BuilderClass = this.builder;
809
+ return new BuilderClass(this.table, dataSource);
810
+ }
811
+ // Use default driver query builder
812
+ const queryBuilder = dataSource.driver.queryBuilder(this.table);
813
+ return queryBuilder;
814
+ }
815
+ /**
816
+ * Get First matched record for the given filter
817
+ */
818
+ static async first(filter) {
819
+ const query = this.query();
820
+ if (filter) {
821
+ query.where(filter);
822
+ }
823
+ return query.first();
824
+ }
825
+ /**
826
+ * Get last matched record for the given filter
827
+ */
828
+ static async last(filter) {
829
+ const query = this.query();
830
+ if (filter) {
831
+ query.where(filter);
832
+ }
833
+ return query.last();
834
+ }
835
+ static where(...args) {
836
+ return this.query().where(...args);
837
+ }
838
+ /**
839
+ * Count the number of records in the table
840
+ * @param filter - The filter to apply to the query
841
+ */
842
+ static count(filter) {
843
+ const query = this.query();
844
+ if (filter) {
845
+ query.where(filter);
846
+ }
847
+ return query.count();
848
+ }
849
+ /**
850
+ * Find record by id
851
+ */
852
+ static async find(id) {
853
+ const query = this.query();
854
+ return query.where(this.primaryKey, id).first();
855
+ }
856
+ /**
857
+ * Get all records from the table
858
+ *
859
+ * @param filter - The filter to apply to the query
860
+ * @returns All records from the table
861
+ */
862
+ static async all(filter) {
863
+ const query = this.query();
864
+ if (filter) {
865
+ query.where(filter);
866
+ }
867
+ return query.get();
868
+ }
869
+ /**
870
+ * Get latest records from the table
871
+ *
872
+ * @param filter - The filter to apply to the query
873
+ */
874
+ static async latest(filter) {
875
+ const query = this.query();
876
+ if (filter) {
877
+ query.where(filter);
878
+ }
879
+ return (await query.latest());
880
+ }
881
+ /**
882
+ * Increment the given field by the given amount
883
+ *
884
+ * @example ```typescript
885
+ * // Increase age by 1 for user id 1
886
+ * User.increment({id: 1}, "age", 1);
887
+ * // Increase age by 1 and views by 2 for user id 1
888
+ * User.increment({id: 1}, {age: 1, views: 2});
889
+ * ```
890
+ */
891
+ static increase(filter, field, amount) {
892
+ const query = this.query().where(filter);
893
+ return query.increment(field, amount);
894
+ }
895
+ /**
896
+ * Decrement the given field by the given amount
897
+ * @example ```typescript
898
+ * // Decrease age by 1 for user id 1
899
+ * User.decrement({id: 1}, "age", 1);
900
+ * // Decrease age by 1 and views by 2 for user id 1
901
+ * User.decrement({id: 1}, {age: 1, views: 2});
902
+ * ```
903
+ */
904
+ static decrease(filter, field, amount) {
905
+ const query = this.query().where(filter);
906
+ return query.decrement(field, amount);
907
+ }
908
+ /**
909
+ * Perform atomic operation
910
+ */
911
+ static async atomic(filter, operations) {
912
+ const dataSource = this.getDataSource();
913
+ const result = await dataSource.driver.atomic(this.table, filter, operations);
914
+ return result.modifiedCount;
915
+ }
916
+ /**
917
+ * Destroy (delete) the current model instance from the database.
918
+ *
919
+ * Emits lifecycle events:
920
+ * - `deleting` - Before deletion
921
+ * - `deleted` - After successful deletion
922
+ *
923
+ * @param options - Destroy options (strategy override, skipEvents)
924
+ * @throws {Error} If the model is new (not saved) or if deletion fails
925
+ *
926
+ * @example
927
+ * ```typescript
928
+ * const user = await User.find(1);
929
+ * await user.destroy(); // Uses default strategy
930
+ * await user.destroy({ strategy: "permanent" }); // Override strategy
931
+ * await user.destroy({ skipEvents: true }); // Silent delete
932
+ * ```
933
+ */
934
+ async destroy(options) {
935
+ const remover = new DatabaseRemover(this);
936
+ return await remover.destroy(options);
937
+ }
938
+ /**
939
+ * Get the class constructor from an instance.
940
+ *
941
+ * This helper method allows instance methods to access static properties
942
+ * and methods of the model class in a type-safe way.
943
+ *
944
+ * @returns The model class constructor
945
+ *
946
+ * @example
947
+ * ```typescript
948
+ * const constructor = this.self();
949
+ * const table = constructor.table;
950
+ * await constructor.deleteOne({ id: 1 });
951
+ * ```
952
+ */
953
+ self() {
954
+ return this.constructor;
955
+ }
956
+ /**
957
+ * Creates an immutable clone of the model with its current state.
958
+ *
959
+ * The cloned model:
960
+ * - Contains a deep copy of all current data
961
+ * - Has frozen (immutable) data that cannot be modified
962
+ * - Preserves the `isNew` flag from the original
963
+ * - Has no dirty changes (clean state)
964
+ * - Cannot be saved or modified
965
+ *
966
+ * This is useful for:
967
+ * - Creating snapshots of model state
968
+ * - Passing read-only model data to other parts of the application
969
+ * - Preventing accidental mutations
970
+ * - Maintaining historical records
971
+ *
972
+ * @returns A new immutable model instance with the current state
973
+ *
974
+ * @example
975
+ * ```typescript
976
+ * const user = new User({ name: "Alice", email: "alice@example.com" });
977
+ * await user.save();
978
+ *
979
+ * // Create an immutable snapshot
980
+ * const snapshot = user.clone();
981
+ *
982
+ * // This will throw an error because the clone is immutable
983
+ * snapshot.set("name", "Bob"); // TypeError: Cannot assign to read only property
984
+ *
985
+ * // Original can still be modified
986
+ * user.set("name", "Bob");
987
+ * await user.save();
988
+ * ```
989
+ */
990
+ clone() {
991
+ // Deep copy the current data using JSON serialization
992
+ // This ensures nested objects are also copied
993
+ const clonedData = JSON.parse(JSON.stringify(this.data));
994
+ // Create a new instance of the same model class
995
+ const ModelClass = this.self();
996
+ const clonedModel = new ModelClass(clonedData);
997
+ // Preserve the isNew state
998
+ clonedModel.isNew = this.isNew;
999
+ // Freeze the data to make it immutable
1000
+ // This recursively freezes all nested objects
1001
+ this.deepFreeze(clonedModel.data);
1002
+ // Reset the dirty tracker to have no changes
1003
+ // The clone represents a clean snapshot
1004
+ clonedModel.dirtyTracker.reset();
1005
+ return clonedModel;
1006
+ }
1007
+ /**
1008
+ * Recursively freezes an object and all its nested properties.
1009
+ *
1010
+ * @param obj - The object to freeze
1011
+ * @returns The frozen object
1012
+ * @private
1013
+ */
1014
+ deepFreeze(obj) {
1015
+ // Freeze the object itself
1016
+ Object.freeze(obj);
1017
+ // Recursively freeze all properties
1018
+ Object.getOwnPropertyNames(obj).forEach((prop) => {
1019
+ const value = obj[prop];
1020
+ // Only freeze objects and arrays, skip primitives and null
1021
+ if (value !== null &&
1022
+ (typeof value === "object" || typeof value === "function") &&
1023
+ !Object.isFrozen(value)) {
1024
+ this.deepFreeze(value);
1025
+ }
1026
+ });
1027
+ return obj;
1028
+ }
1029
+ /**
1030
+ * Get table name
1031
+ */
1032
+ getTableName() {
1033
+ return this.self().table;
1034
+ }
1035
+ /**
1036
+ * Get primary key name
1037
+ */
1038
+ getPrimaryKey() {
1039
+ return this.self().primaryKey;
1040
+ }
1041
+ /**
1042
+ * Get model schema
1043
+ */
1044
+ getSchema() {
1045
+ return this.self().schema;
1046
+ }
1047
+ /**
1048
+ * Check if schema has the given key
1049
+ */
1050
+ schemaHas(key) {
1051
+ return this.self().schema?.schema[key] !== undefined;
1052
+ }
1053
+ /**
1054
+ * Get strict mode
1055
+ */
1056
+ getStrictMode() {
1057
+ return this.self().strictMode;
1058
+ }
1059
+ /**
1060
+ * Get data source (Connection)
1061
+ */
1062
+ getConnection() {
1063
+ return this.self().getDataSource();
1064
+ }
1065
+ /**
1066
+ * Delete all matching documents from the table.
1067
+ */
1068
+ static async delete(filter) {
1069
+ return await this.getDataSource().driver.deleteMany(this.table, filter);
1070
+ }
1071
+ /**
1072
+ * Delete a single matching document from the table.
1073
+ */
1074
+ static async deleteOne(filter) {
1075
+ return await this.getDataSource().driver.delete(this.table, filter);
1076
+ }
1077
+ /**
1078
+ * Restore a single deleted record by its ID.
1079
+ *
1080
+ * Automatically detects whether the record was deleted via "trash" or "soft" strategy.
1081
+ * Handles ID conflicts based on options.
1082
+ *
1083
+ * @param id - The primary key value of the record to restore
1084
+ * @param options - Restorer options (onIdConflict, skipEvents)
1085
+ * @returns The restored model instance
1086
+ *
1087
+ * @throws {Error} If record not found in trash or soft-deleted records
1088
+ * @throws {Error} If ID conflict and onIdConflict is "fail"
1089
+ *
1090
+ * @example
1091
+ * ```typescript
1092
+ * // Restore with default options (assign new ID if conflict)
1093
+ * const user = await User.restore(123);
1094
+ *
1095
+ * // Restore and fail if ID conflict
1096
+ * const user = await User.restore(123, { onIdConflict: "fail" });
1097
+ *
1098
+ * // Silent restore (skip events)
1099
+ * const user = await User.restore(123, { skipEvents: true });
1100
+ * ```
1101
+ */
1102
+ static async restore(id, options) {
1103
+ const restorer = new DatabaseRestorer(this);
1104
+ const result = await restorer.restore(id, options);
1105
+ if (!result.restoredRecord) {
1106
+ throw new Error(`Failed to restore ${this.name} with ${this.primaryKey} ${id}: no record returned.`);
1107
+ }
1108
+ return result.restoredRecord;
1109
+ }
1110
+ /**
1111
+ * Restore all deleted records for the model's table.
1112
+ *
1113
+ * Restores all records from the trash table (if using trash strategy)
1114
+ * or all soft-deleted records (if using soft strategy).
1115
+ *
1116
+ * @param options - Restorer options (onIdConflict, skipEvents)
1117
+ * @returns Array of restored model instances
1118
+ *
1119
+ * @example
1120
+ * ```typescript
1121
+ * // Restore all with default options
1122
+ * const users = await User.restoreAll();
1123
+ *
1124
+ * // Restore all and fail on any ID conflict
1125
+ * const users = await User.restoreAll({ onIdConflict: "fail" });
1126
+ * ```
1127
+ */
1128
+ static async restoreAll(options) {
1129
+ const restorer = new DatabaseRestorer(this);
1130
+ const result = await restorer.restoreAll(options);
1131
+ if (result.restoredCount === 0) {
1132
+ return [];
1133
+ }
1134
+ return result.restoredRecords;
1135
+ }
1136
+ /**
1137
+ * Create a new record in database and return the model instance.
1138
+ *
1139
+ * The data type is automatically inferred from the model's schema type.
1140
+ *
1141
+ * @param data - Partial data matching the model's schema type
1142
+ * @returns The created model instance
1143
+ *
1144
+ * @example
1145
+ * ```typescript
1146
+ * // TypeScript automatically infers UserSchema from User model
1147
+ * const user = await User.create({
1148
+ * name: "Alice",
1149
+ * email: "alice@example.com",
1150
+ * age: 30
1151
+ * });
1152
+ * // Type: User (with UserSchema inferred)
1153
+ * ```
1154
+ */
1155
+ static async create(data) {
1156
+ const model = new this(data);
1157
+ await model.save();
1158
+ return model;
1159
+ }
1160
+ /**
1161
+ * Create many documents and return an array of created models
1162
+ */
1163
+ static async createMany(data) {
1164
+ return await Promise.all(data.map((data) => this.create(data)));
1165
+ }
1166
+ /**
1167
+ * Find a record or create it if not found.
1168
+ *
1169
+ * Does NOT update existing records - returns them as-is.
1170
+ * Useful when you want to ensure a record exists without modifying it.
1171
+ *
1172
+ * @param filter - Conditions to find by
1173
+ * @param data - Data to create if not found (merged with filter)
1174
+ * @returns Promise resolving to found or created model
1175
+ *
1176
+ * @example
1177
+ * ```typescript
1178
+ * // Ensure default admin exists (don't modify if exists)
1179
+ * const admin = await User.findOrCreate(
1180
+ * { email: "admin@example.com" },
1181
+ * { email: "admin@example.com", name: "Admin", role: "admin" }
1182
+ * );
1183
+ * // If admin exists, returns existing (password unchanged)
1184
+ * // If not found, creates new admin
1185
+ * ```
1186
+ */
1187
+ static async findOrCreate(filter, data) {
1188
+ // Try to find existing record
1189
+ const existing = await this.first(filter);
1190
+ if (existing) {
1191
+ return existing; // Return as-is, no update
1192
+ }
1193
+ // Create new record with merged data
1194
+ return await this.create({ ...filter, ...data });
1195
+ }
1196
+ /**
1197
+ * Update a record or create it if not found (upsert).
1198
+ *
1199
+ * DOES update existing records with new data.
1200
+ * Useful when you want to ensure a record exists with the latest data.
1201
+ *
1202
+ * Includes full Model features:
1203
+ * - ID generation (if creating)
1204
+ * - createdAt timestamp (if creating)
1205
+ * - updatedAt timestamp (always)
1206
+ * - Validation & casting
1207
+ * - Lifecycle events
1208
+ * - Sync operations
1209
+ *
1210
+ * @param filter - Conditions to find by
1211
+ * @param data - Data to update or create (merged with filter)
1212
+ * @returns Promise resolving to updated or created model
1213
+ *
1214
+ * @example
1215
+ * ```typescript
1216
+ * // Sync user from external API (update if exists, create if not)
1217
+ * const user = await User.updateOrCreate(
1218
+ * { externalId: "ext-123" },
1219
+ * {
1220
+ * externalId: "ext-123",
1221
+ * name: "John Updated",
1222
+ * email: "john.new@example.com",
1223
+ * lastSyncedAt: new Date()
1224
+ * }
1225
+ * );
1226
+ * // User always has latest data from API
1227
+ * ```
1228
+ */
1229
+ static async updateOrCreate(filter, data) {
1230
+ // Try to find existing record
1231
+ const existing = await this.first(filter);
1232
+ if (existing) {
1233
+ // Update existing record
1234
+ await existing.save({ merge: data });
1235
+ return existing;
1236
+ }
1237
+ // Create new record with merged data
1238
+ return await this.create({ ...filter, ...data });
1239
+ }
1240
+ /**
1241
+ * Returns embedded data for sync operations.
1242
+ * Excludes internal MongoDB fields and ensures proper date serialization.
1243
+ *
1244
+ * @returns Embedded data object suitable for syncing
1245
+ *
1246
+ * @example
1247
+ * ```typescript
1248
+ * const user = await User.find(1);
1249
+ * const embedData = user.embedData;
1250
+ * // Returns: { id: 1, name: "Alice", email: "alice@example.com", ... }
1251
+ * // Excludes: _id
1252
+ * ```
1253
+ */
1254
+ get embedData() {
1255
+ return this.self().embed ? this.only(this.self().embed) : this.data;
1256
+ }
1257
+ /**
1258
+ * Accesses the event emitter dedicated to this model constructor.
1259
+ *
1260
+ * Each model subclass gets its own isolated event emitter, allowing you to
1261
+ * register lifecycle hooks that only apply to that specific model.
1262
+ *
1263
+ * @returns The ModelEvents instance for this model constructor
1264
+ *
1265
+ * @example
1266
+ * ```typescript
1267
+ * User.events().onSaving((user) => {
1268
+ * console.log("User is being saved:", user);
1269
+ * });
1270
+ * ```
1271
+ */
1272
+ static events() {
1273
+ let events = modelEventsRegistry.get(this);
1274
+ if (!events) {
1275
+ events = new ModelEvents();
1276
+ modelEventsRegistry.set(this, events);
1277
+ }
1278
+ return events;
1279
+ }
1280
+ /**
1281
+ * Cleanup model events
1282
+ */
1283
+ static $cleanup() {
1284
+ modelEventsRegistry.delete(this);
1285
+ removeModelFromRegistery(this.name);
1286
+ }
1287
+ /**
1288
+ * Registers an event listener for this model constructor.
1289
+ *
1290
+ * Convenience shorthand for `Model.events().on(...)`.
1291
+ *
1292
+ * @param event - The event name (e.g., "saving", "created")
1293
+ * @param listener - The callback to invoke when the event fires
1294
+ * @returns An unsubscribe function
1295
+ *
1296
+ * @example
1297
+ * ```typescript
1298
+ * const unsubscribe = User.on("saving", (user) => {
1299
+ * console.log("Saving user:", user);
1300
+ * });
1301
+ * ```
1302
+ */
1303
+ static on(event, listener) {
1304
+ return this.events().on(event, listener);
1305
+ }
1306
+ /**
1307
+ * Registers a one-time event listener for this model constructor.
1308
+ *
1309
+ * The listener will automatically unsubscribe after its first invocation.
1310
+ * Convenience shorthand for `Model.events().once(...)`.
1311
+ *
1312
+ * @param event - The event name (e.g., "saving", "created")
1313
+ * @param listener - The callback to invoke when the event fires
1314
+ * @returns An unsubscribe function
1315
+ *
1316
+ * @example
1317
+ * ```typescript
1318
+ * User.once("created", (user) => {
1319
+ * console.log("First user created:", user);
1320
+ * });
1321
+ * ```
1322
+ */
1323
+ static once(event, listener) {
1324
+ return this.events().once(event, listener);
1325
+ }
1326
+ /**
1327
+ * Removes an event listener from this model constructor.
1328
+ *
1329
+ * Convenience shorthand for `Model.events().off(...)`.
1330
+ *
1331
+ * @param event - The event name
1332
+ * @param listener - The callback to remove
1333
+ *
1334
+ * @example
1335
+ * ```typescript
1336
+ * const listener = (user) => console.log(user);
1337
+ * User.on("saving", listener);
1338
+ * User.off("saving", listener);
1339
+ * ```
1340
+ */
1341
+ static off(event, listener) {
1342
+ this.events().off(event, listener);
1343
+ }
1344
+ /**
1345
+ * Accesses the global event emitter shared by all model instances.
1346
+ *
1347
+ * Use this for cross-cutting concerns like auditing, logging, or injecting
1348
+ * common fields (e.g., `createdBy`, `updatedBy`) across all models.
1349
+ *
1350
+ * @returns The global ModelEvents instance
1351
+ *
1352
+ * @example
1353
+ * ```typescript
1354
+ * Model.globalEvents().onSaving((model) => {
1355
+ * model.set("updatedAt", new Date());
1356
+ * });
1357
+ * ```
1358
+ */
1359
+ static globalEvents() {
1360
+ return globalModelEvents;
1361
+ }
1362
+ /**
1363
+ * Replace the model's data entirely.
1364
+ *
1365
+ * Used internally by the writer after validation to update the model
1366
+ * with validated/casted data.
1367
+ *
1368
+ * **Warning:** This replaces all data and updates the dirty tracker.
1369
+ * Use with caution in application code.
1370
+ *
1371
+ * @param data - New data to replace current data
1372
+ *
1373
+ * @example
1374
+ * ```typescript
1375
+ * // Internal usage by writer
1376
+ * model.replaceData(validatedData);
1377
+ * ```
1378
+ */
1379
+ replaceData(data) {
1380
+ this.data = data;
1381
+ this.dirtyTracker.replaceCurrentData(data);
1382
+ }
1383
+ /**
1384
+ * Save the model to the database.
1385
+ *
1386
+ * Performs insert if `isNew === true`, otherwise performs update.
1387
+ * Automatically validates, casts, generates IDs, and emits lifecycle events.
1388
+ *
1389
+ * **Features:**
1390
+ * - Validation via @warlock.js/seal schema
1391
+ * - Data casting (string → number, etc.)
1392
+ * - ID generation (NoSQL only)
1393
+ * - Partial updates (only changed fields)
1394
+ * - Lifecycle events (validating, saving, created/updated, saved)
1395
+ *
1396
+ * @param data - Optional data to merge before saving
1397
+ * @param options - Save options
1398
+ * @returns The model instance for method chaining
1399
+ *
1400
+ * @throws {ValidationError} If validation fails
1401
+ * @throws {Error} If database operation fails
1402
+ *
1403
+ * @example
1404
+ * ```typescript
1405
+ * // Simple save
1406
+ * const user = new User({ name: "Alice" });
1407
+ * await user.save();
1408
+ *
1409
+ * // Merge data before saving
1410
+ * await user.save({ age: 31, email: "alice@example.com" });
1411
+ *
1412
+ * // Silent save (no events)
1413
+ * await user.save(null, { skipEvents: true });
1414
+ *
1415
+ * // Skip validation
1416
+ * await user.save(null, { skipValidation: true });
1417
+ *
1418
+ * // Method chaining
1419
+ * await user.set("name", "Bob").save();
1420
+ * ```
1421
+ */
1422
+ async save(options) {
1423
+ if (options?.merge) {
1424
+ this.merge(options.merge);
1425
+ }
1426
+ const writer = new DatabaseWriter(this);
1427
+ await writer.save(options);
1428
+ return this;
1429
+ }
1430
+ /**
1431
+ * Serialize the model data for storage in database
1432
+ */
1433
+ serialize() {
1434
+ return this.self().getDataSource().driver.serialize(this.data);
1435
+ }
1436
+ /**
1437
+ * Deseriaze the given data
1438
+ */
1439
+ static deserialize(data) {
1440
+ const deserializedData = this.getDataSource().driver.deserialize(data);
1441
+ const model = new this(deserializedData);
1442
+ model.isNew = false;
1443
+ return model;
1444
+ }
1445
+ /**
1446
+ * Convert the model into JSON
1447
+ */
1448
+ toJSON() {
1449
+ const resource = this.self().resource;
1450
+ if (!resource) {
1451
+ const toJsonColumns = this.self().toJsonColumns;
1452
+ if (toJsonColumns && toJsonColumns.length > 0) {
1453
+ return this.only(toJsonColumns);
1454
+ }
1455
+ return this.data;
1456
+ }
1457
+ const resourceColumns = this.self().resourceColumns;
1458
+ const data = resourceColumns !== undefined && resourceColumns.length > 0
1459
+ ? this.only(resourceColumns)
1460
+ : this.data;
1461
+ return new resource(data).toJSON();
1462
+ }
1463
+ }export{Model};//# sourceMappingURL=model.js.map