@warlock.js/cascade 4.8.2 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (318) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/cjs/index.cjs +160 -124
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/context/database-data-source-context.d.mts +1 -1
  5. package/esm/context/database-data-source-context.d.mts.map +1 -1
  6. package/esm/context/database-data-source-context.mjs +1 -1
  7. package/esm/context/database-data-source-context.mjs.map +1 -1
  8. package/esm/context/database-transaction-context.d.mts +1 -1
  9. package/esm/context/database-transaction-context.d.mts.map +1 -1
  10. package/esm/context/database-transaction-context.mjs +1 -1
  11. package/esm/context/database-transaction-context.mjs.map +1 -1
  12. package/esm/contracts/database-driver.contract.d.mts +1 -1
  13. package/esm/contracts/database-driver.contract.d.mts.map +1 -1
  14. package/esm/contracts/database-id-generator.contract.d.mts +1 -1
  15. package/esm/contracts/database-id-generator.contract.d.mts.map +1 -1
  16. package/esm/contracts/database-remover.contract.d.mts +1 -1
  17. package/esm/contracts/database-remover.contract.d.mts.map +1 -1
  18. package/esm/contracts/database-restorer.contract.d.mts +1 -1
  19. package/esm/contracts/database-restorer.contract.d.mts.map +1 -1
  20. package/esm/contracts/database-writer.contract.d.mts +1 -1
  21. package/esm/contracts/database-writer.contract.d.mts.map +1 -1
  22. package/esm/contracts/driver-blueprint.contract.d.mts +1 -1
  23. package/esm/contracts/driver-blueprint.contract.d.mts.map +1 -1
  24. package/esm/contracts/migration-driver.contract.d.mts +1 -1
  25. package/esm/contracts/migration-driver.contract.d.mts.map +1 -1
  26. package/esm/contracts/query-builder.contract.d.mts +1 -1
  27. package/esm/contracts/query-builder.contract.d.mts.map +1 -1
  28. package/esm/contracts/sync-adapter.contract.d.mts +1 -1
  29. package/esm/contracts/sync-adapter.contract.d.mts.map +1 -1
  30. package/esm/data-source/data-source-registry.d.mts +1 -1
  31. package/esm/data-source/data-source-registry.d.mts.map +1 -1
  32. package/esm/data-source/data-source-registry.mjs +1 -1
  33. package/esm/data-source/data-source-registry.mjs.map +1 -1
  34. package/esm/data-source/data-source.d.mts +1 -1
  35. package/esm/data-source/data-source.d.mts.map +1 -1
  36. package/esm/data-source/data-source.mjs +1 -1
  37. package/esm/data-source/data-source.mjs.map +1 -1
  38. package/esm/database-dirty-tracker.d.mts +1 -1
  39. package/esm/database-dirty-tracker.d.mts.map +1 -1
  40. package/esm/database-dirty-tracker.mjs +1 -1
  41. package/esm/database-dirty-tracker.mjs.map +1 -1
  42. package/esm/drivers/mongodb/mongodb-blueprint.mjs +1 -1
  43. package/esm/drivers/mongodb/mongodb-blueprint.mjs.map +1 -1
  44. package/esm/drivers/mongodb/mongodb-driver.d.mts +1 -1
  45. package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
  46. package/esm/drivers/mongodb/mongodb-driver.mjs +1 -1
  47. package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
  48. package/esm/drivers/mongodb/mongodb-id-generator.d.mts +1 -1
  49. package/esm/drivers/mongodb/mongodb-id-generator.d.mts.map +1 -1
  50. package/esm/drivers/mongodb/mongodb-id-generator.mjs +1 -1
  51. package/esm/drivers/mongodb/mongodb-id-generator.mjs.map +1 -1
  52. package/esm/drivers/mongodb/mongodb-migration-driver.d.mts +1 -1
  53. package/esm/drivers/mongodb/mongodb-migration-driver.d.mts.map +1 -1
  54. package/esm/drivers/mongodb/mongodb-migration-driver.mjs +1 -1
  55. package/esm/drivers/mongodb/mongodb-migration-driver.mjs.map +1 -1
  56. package/esm/drivers/mongodb/mongodb-query-builder.d.mts +1 -1
  57. package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
  58. package/esm/drivers/mongodb/mongodb-query-builder.mjs +1 -1
  59. package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
  60. package/esm/drivers/mongodb/mongodb-query-operations.d.mts +1 -1
  61. package/esm/drivers/mongodb/mongodb-query-operations.d.mts.map +1 -1
  62. package/esm/drivers/mongodb/mongodb-query-operations.mjs +1 -1
  63. package/esm/drivers/mongodb/mongodb-query-operations.mjs.map +1 -1
  64. package/esm/drivers/mongodb/mongodb-query-parser.d.mts +1 -1
  65. package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
  66. package/esm/drivers/mongodb/mongodb-query-parser.mjs +1 -1
  67. package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
  68. package/esm/drivers/mongodb/mongodb-sync-adapter.d.mts +1 -1
  69. package/esm/drivers/mongodb/mongodb-sync-adapter.d.mts.map +1 -1
  70. package/esm/drivers/mongodb/mongodb-sync-adapter.mjs +1 -1
  71. package/esm/drivers/mongodb/mongodb-sync-adapter.mjs.map +1 -1
  72. package/esm/drivers/mongodb/types.d.mts +1 -1
  73. package/esm/drivers/mongodb/types.d.mts.map +1 -1
  74. package/esm/drivers/postgres/postgres-blueprint.d.mts +1 -1
  75. package/esm/drivers/postgres/postgres-blueprint.d.mts.map +1 -1
  76. package/esm/drivers/postgres/postgres-blueprint.mjs +1 -1
  77. package/esm/drivers/postgres/postgres-blueprint.mjs.map +1 -1
  78. package/esm/drivers/postgres/postgres-dialect.d.mts +1 -1
  79. package/esm/drivers/postgres/postgres-dialect.d.mts.map +1 -1
  80. package/esm/drivers/postgres/postgres-dialect.mjs +1 -1
  81. package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
  82. package/esm/drivers/postgres/postgres-driver.d.mts +1 -1
  83. package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
  84. package/esm/drivers/postgres/postgres-driver.mjs +1 -1
  85. package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
  86. package/esm/drivers/postgres/postgres-migration-driver.d.mts +1 -1
  87. package/esm/drivers/postgres/postgres-migration-driver.d.mts.map +1 -1
  88. package/esm/drivers/postgres/postgres-migration-driver.mjs +1 -1
  89. package/esm/drivers/postgres/postgres-migration-driver.mjs.map +1 -1
  90. package/esm/drivers/postgres/postgres-query-builder.d.mts +1 -1
  91. package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
  92. package/esm/drivers/postgres/postgres-query-builder.mjs +1 -1
  93. package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
  94. package/esm/drivers/postgres/postgres-query-parser.d.mts +1 -1
  95. package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
  96. package/esm/drivers/postgres/postgres-query-parser.mjs +1 -1
  97. package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
  98. package/esm/drivers/postgres/postgres-sql-serializer.mjs +1 -1
  99. package/esm/drivers/postgres/postgres-sql-serializer.mjs.map +1 -1
  100. package/esm/drivers/postgres/postgres-sync-adapter.d.mts +1 -1
  101. package/esm/drivers/postgres/postgres-sync-adapter.d.mts.map +1 -1
  102. package/esm/drivers/postgres/postgres-sync-adapter.mjs +1 -1
  103. package/esm/drivers/postgres/postgres-sync-adapter.mjs.map +1 -1
  104. package/esm/drivers/postgres/types.d.mts +1 -1
  105. package/esm/drivers/postgres/types.d.mts.map +1 -1
  106. package/esm/drivers/sql/sql-dialect.contract.d.mts +1 -1
  107. package/esm/drivers/sql/sql-dialect.contract.d.mts.map +1 -1
  108. package/esm/drivers/sql/sql-types.d.mts +1 -1
  109. package/esm/drivers/sql/sql-types.d.mts.map +1 -1
  110. package/esm/errors/missing-data-source.error.d.mts +1 -1
  111. package/esm/errors/missing-data-source.error.d.mts.map +1 -1
  112. package/esm/errors/missing-data-source.error.mjs +1 -1
  113. package/esm/errors/missing-data-source.error.mjs.map +1 -1
  114. package/esm/errors/transaction-rollback.error.d.mts +1 -1
  115. package/esm/errors/transaction-rollback.error.d.mts.map +1 -1
  116. package/esm/errors/transaction-rollback.error.mjs +1 -1
  117. package/esm/errors/transaction-rollback.error.mjs.map +1 -1
  118. package/esm/events/model-events.d.mts +1 -1
  119. package/esm/events/model-events.d.mts.map +1 -1
  120. package/esm/events/model-events.mjs +1 -1
  121. package/esm/events/model-events.mjs.map +1 -1
  122. package/esm/expressions/aggregate-expressions.d.mts +1 -1
  123. package/esm/expressions/aggregate-expressions.d.mts.map +1 -1
  124. package/esm/expressions/aggregate-expressions.mjs +1 -1
  125. package/esm/expressions/aggregate-expressions.mjs.map +1 -1
  126. package/esm/expressions/column-expressions.d.mts +1 -1
  127. package/esm/expressions/column-expressions.d.mts.map +1 -1
  128. package/esm/expressions/column-expressions.mjs +1 -1
  129. package/esm/expressions/column-expressions.mjs.map +1 -1
  130. package/esm/migration/column-builder.d.mts +1 -1
  131. package/esm/migration/column-builder.d.mts.map +1 -1
  132. package/esm/migration/column-builder.mjs +1 -1
  133. package/esm/migration/column-builder.mjs.map +1 -1
  134. package/esm/migration/column-helpers.d.mts +1 -1
  135. package/esm/migration/column-helpers.d.mts.map +1 -1
  136. package/esm/migration/column-helpers.mjs +1 -1
  137. package/esm/migration/column-helpers.mjs.map +1 -1
  138. package/esm/migration/foreign-key-builder.d.mts +1 -1
  139. package/esm/migration/foreign-key-builder.d.mts.map +1 -1
  140. package/esm/migration/foreign-key-builder.mjs +1 -1
  141. package/esm/migration/foreign-key-builder.mjs.map +1 -1
  142. package/esm/migration/migration-runner.d.mts +1 -1
  143. package/esm/migration/migration-runner.d.mts.map +1 -1
  144. package/esm/migration/migration-runner.mjs +4 -40
  145. package/esm/migration/migration-runner.mjs.map +1 -1
  146. package/esm/migration/migration.d.mts +1 -1
  147. package/esm/migration/migration.d.mts.map +1 -1
  148. package/esm/migration/migration.mjs +1 -1
  149. package/esm/migration/migration.mjs.map +1 -1
  150. package/esm/migration/parse-created-at.mjs +67 -0
  151. package/esm/migration/parse-created-at.mjs.map +1 -0
  152. package/esm/migration/sql-grammar.mjs +16 -6
  153. package/esm/migration/sql-grammar.mjs.map +1 -1
  154. package/esm/migration/sql-serializer.d.mts +1 -1
  155. package/esm/migration/sql-serializer.d.mts.map +1 -1
  156. package/esm/migration/sql-serializer.mjs +1 -1
  157. package/esm/migration/sql-serializer.mjs.map +1 -1
  158. package/esm/migration/types.d.mts +1 -1
  159. package/esm/migration/types.d.mts.map +1 -1
  160. package/esm/model/methods/accessor-methods.mjs +1 -1
  161. package/esm/model/methods/accessor-methods.mjs.map +1 -1
  162. package/esm/model/methods/delete-methods.mjs +1 -1
  163. package/esm/model/methods/delete-methods.mjs.map +1 -1
  164. package/esm/model/methods/dirty-methods.mjs +1 -1
  165. package/esm/model/methods/dirty-methods.mjs.map +1 -1
  166. package/esm/model/methods/hydration-methods.mjs +1 -1
  167. package/esm/model/methods/hydration-methods.mjs.map +1 -1
  168. package/esm/model/methods/instance-event-methods.mjs +1 -1
  169. package/esm/model/methods/instance-event-methods.mjs.map +1 -1
  170. package/esm/model/methods/meta-methods.mjs +1 -1
  171. package/esm/model/methods/meta-methods.mjs.map +1 -1
  172. package/esm/model/methods/pivot-methods.mjs +1 -1
  173. package/esm/model/methods/pivot-methods.mjs.map +1 -1
  174. package/esm/model/methods/query-methods.mjs +1 -1
  175. package/esm/model/methods/query-methods.mjs.map +1 -1
  176. package/esm/model/methods/restore-methods.mjs +1 -1
  177. package/esm/model/methods/restore-methods.mjs.map +1 -1
  178. package/esm/model/methods/scope-methods.mjs +1 -1
  179. package/esm/model/methods/scope-methods.mjs.map +1 -1
  180. package/esm/model/methods/serialization-methods.mjs +1 -1
  181. package/esm/model/methods/serialization-methods.mjs.map +1 -1
  182. package/esm/model/methods/static-event-methods.mjs +1 -1
  183. package/esm/model/methods/static-event-methods.mjs.map +1 -1
  184. package/esm/model/methods/write-methods.d.mts +1 -1
  185. package/esm/model/methods/write-methods.d.mts.map +1 -1
  186. package/esm/model/methods/write-methods.mjs +1 -1
  187. package/esm/model/methods/write-methods.mjs.map +1 -1
  188. package/esm/model/model.d.mts +1 -1
  189. package/esm/model/model.d.mts.map +1 -1
  190. package/esm/model/model.mjs +1 -1
  191. package/esm/model/model.mjs.map +1 -1
  192. package/esm/model/model.types.d.mts +1 -1
  193. package/esm/model/model.types.d.mts.map +1 -1
  194. package/esm/model/register-model.d.mts +1 -1
  195. package/esm/model/register-model.d.mts.map +1 -1
  196. package/esm/model/register-model.mjs +1 -1
  197. package/esm/model/register-model.mjs.map +1 -1
  198. package/esm/model/relation-decorators.d.mts +1 -1
  199. package/esm/model/relation-decorators.d.mts.map +1 -1
  200. package/esm/model/relation-decorators.mjs +1 -1
  201. package/esm/model/relation-decorators.mjs.map +1 -1
  202. package/esm/operations/database.d.mts +1 -1
  203. package/esm/operations/database.d.mts.map +1 -1
  204. package/esm/operations/database.mjs +1 -1
  205. package/esm/operations/database.mjs.map +1 -1
  206. package/esm/operations/migrations.d.mts +1 -1
  207. package/esm/operations/migrations.d.mts.map +1 -1
  208. package/esm/operations/migrations.mjs +1 -1
  209. package/esm/operations/migrations.mjs.map +1 -1
  210. package/esm/query-builder/query-builder.d.mts +1 -1
  211. package/esm/query-builder/query-builder.d.mts.map +1 -1
  212. package/esm/query-builder/query-builder.mjs +1 -1
  213. package/esm/query-builder/query-builder.mjs.map +1 -1
  214. package/esm/relations/key-conventions.mjs +1 -1
  215. package/esm/relations/key-conventions.mjs.map +1 -1
  216. package/esm/relations/pivot-operations.d.mts +1 -1
  217. package/esm/relations/pivot-operations.d.mts.map +1 -1
  218. package/esm/relations/pivot-operations.mjs +1 -1
  219. package/esm/relations/pivot-operations.mjs.map +1 -1
  220. package/esm/relations/relation-hydrator.d.mts +1 -1
  221. package/esm/relations/relation-hydrator.d.mts.map +1 -1
  222. package/esm/relations/relation-hydrator.mjs +1 -1
  223. package/esm/relations/relation-hydrator.mjs.map +1 -1
  224. package/esm/relations/relation-loader.d.mts +1 -1
  225. package/esm/relations/relation-loader.d.mts.map +1 -1
  226. package/esm/relations/relation-loader.mjs +1 -1
  227. package/esm/relations/relation-loader.mjs.map +1 -1
  228. package/esm/relations/types.d.mts +1 -1
  229. package/esm/relations/types.d.mts.map +1 -1
  230. package/esm/remover/database-remover.d.mts +1 -1
  231. package/esm/remover/database-remover.d.mts.map +1 -1
  232. package/esm/remover/database-remover.mjs +1 -1
  233. package/esm/remover/database-remover.mjs.map +1 -1
  234. package/esm/restorer/database-restorer.d.mts +1 -1
  235. package/esm/restorer/database-restorer.d.mts.map +1 -1
  236. package/esm/restorer/database-restorer.mjs +1 -1
  237. package/esm/restorer/database-restorer.mjs.map +1 -1
  238. package/esm/sql-database-dirty-tracker.d.mts +1 -1
  239. package/esm/sql-database-dirty-tracker.d.mts.map +1 -1
  240. package/esm/sql-database-dirty-tracker.mjs +1 -1
  241. package/esm/sql-database-dirty-tracker.mjs.map +1 -1
  242. package/esm/sync/model-events.mjs +1 -1
  243. package/esm/sync/model-events.mjs.map +1 -1
  244. package/esm/sync/model-sync-operation.d.mts +1 -1
  245. package/esm/sync/model-sync-operation.d.mts.map +1 -1
  246. package/esm/sync/model-sync-operation.mjs +1 -1
  247. package/esm/sync/model-sync-operation.mjs.map +1 -1
  248. package/esm/sync/model-sync.d.mts +1 -1
  249. package/esm/sync/model-sync.d.mts.map +1 -1
  250. package/esm/sync/model-sync.mjs +1 -1
  251. package/esm/sync/model-sync.mjs.map +1 -1
  252. package/esm/sync/sync-context.d.mts +1 -1
  253. package/esm/sync/sync-context.d.mts.map +1 -1
  254. package/esm/sync/sync-context.mjs +1 -1
  255. package/esm/sync/sync-context.mjs.map +1 -1
  256. package/esm/sync/sync-manager.d.mts +1 -1
  257. package/esm/sync/sync-manager.d.mts.map +1 -1
  258. package/esm/sync/sync-manager.mjs +1 -1
  259. package/esm/sync/sync-manager.mjs.map +1 -1
  260. package/esm/sync/types.d.mts +1 -1
  261. package/esm/sync/types.d.mts.map +1 -1
  262. package/esm/types.d.mts +1 -1
  263. package/esm/types.d.mts.map +1 -1
  264. package/esm/utils/connect-to-database.d.mts +1 -1
  265. package/esm/utils/connect-to-database.d.mts.map +1 -1
  266. package/esm/utils/connect-to-database.mjs +1 -1
  267. package/esm/utils/connect-to-database.mjs.map +1 -1
  268. package/esm/utils/database-writer.utils.d.mts +1 -1
  269. package/esm/utils/database-writer.utils.d.mts.map +1 -1
  270. package/esm/utils/database-writer.utils.mjs +1 -1
  271. package/esm/utils/database-writer.utils.mjs.map +1 -1
  272. package/esm/utils/define-model.d.mts +1 -1
  273. package/esm/utils/define-model.d.mts.map +1 -1
  274. package/esm/utils/define-model.mjs +1 -1
  275. package/esm/utils/define-model.mjs.map +1 -1
  276. package/esm/utils/is-valid-date-value.mjs +1 -1
  277. package/esm/utils/is-valid-date-value.mjs.map +1 -1
  278. package/esm/utils/once-connected.d.mts +1 -1
  279. package/esm/utils/once-connected.d.mts.map +1 -1
  280. package/esm/utils/once-connected.mjs +1 -1
  281. package/esm/utils/once-connected.mjs.map +1 -1
  282. package/esm/validation/database-seal-plugins.mjs +1 -1
  283. package/esm/validation/database-seal-plugins.mjs.map +1 -1
  284. package/esm/validation/database-writer-validation-error.d.mts +1 -1
  285. package/esm/validation/database-writer-validation-error.d.mts.map +1 -1
  286. package/esm/validation/database-writer-validation-error.mjs +1 -1
  287. package/esm/validation/database-writer-validation-error.mjs.map +1 -1
  288. package/esm/validation/mutators/embed-mutator.mjs +1 -1
  289. package/esm/validation/mutators/embed-mutator.mjs.map +1 -1
  290. package/esm/validation/plugins/database-rules-plugin.d.mts +1 -1
  291. package/esm/validation/plugins/database-rules-plugin.d.mts.map +1 -1
  292. package/esm/validation/plugins/database-rules-plugin.mjs +1 -1
  293. package/esm/validation/plugins/database-rules-plugin.mjs.map +1 -1
  294. package/esm/validation/plugins/embed-validator-plugin.d.mts +1 -1
  295. package/esm/validation/plugins/embed-validator-plugin.d.mts.map +1 -1
  296. package/esm/validation/plugins/embed-validator-plugin.mjs +1 -1
  297. package/esm/validation/plugins/embed-validator-plugin.mjs.map +1 -1
  298. package/esm/validation/rules/database-model-rule.mjs +1 -1
  299. package/esm/validation/rules/database-model-rule.mjs.map +1 -1
  300. package/esm/validation/rules/exists-rule.mjs +1 -1
  301. package/esm/validation/rules/exists-rule.mjs.map +1 -1
  302. package/esm/validation/rules/unique-rule.mjs +1 -1
  303. package/esm/validation/rules/unique-rule.mjs.map +1 -1
  304. package/esm/validation/transformers/embed-model-transformer.mjs +1 -1
  305. package/esm/validation/transformers/embed-model-transformer.mjs.map +1 -1
  306. package/esm/validation/types.d.mts +1 -1
  307. package/esm/validation/types.d.mts.map +1 -1
  308. package/esm/validation/validators/embed-validator.d.mts +1 -1
  309. package/esm/validation/validators/embed-validator.d.mts.map +1 -1
  310. package/esm/validation/validators/embed-validator.mjs +1 -1
  311. package/esm/validation/validators/embed-validator.mjs.map +1 -1
  312. package/esm/writer/database-writer.d.mts +1 -1
  313. package/esm/writer/database-writer.d.mts.map +1 -1
  314. package/esm/writer/database-writer.mjs +1 -1
  315. package/esm/writer/database-writer.mjs.map +1 -1
  316. package/llms-full.txt +14 -0
  317. package/package.json +4 -4
  318. package/skills/write-migration/SKILL.md +14 -0
@@ -1 +1 @@
1
- {"version":3,"file":"database-restorer.mjs","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/restorer/database-restorer.ts"],"sourcesContent":["import type { DriverContract, UpdateOperations } from \"../contracts/database-driver.contract\";\nimport type {\n RestorerContract,\n RestorerOptions,\n RestorerResult,\n} from \"../contracts/database-restorer.contract\";\nimport type { DataSource } from \"../data-source/data-source\";\nimport type { Model } from \"../model/model\";\nimport type { DeleteStrategy } from \"../types\";\n\n/**\n * Database restorer service that orchestrates model restoration.\n *\n * Handles the complete restoration pipeline:\n * 1. Strategy detection (trash vs soft delete)\n * 2. Record retrieval from trash table or soft-deleted records\n * 3. ID conflict resolution\n * 4. Event emission (restoring, restored)\n * 5. Driver execution (insert back to original table, remove from trash/clear deletedAt)\n *\n * @example\n * ```typescript\n * const restorer = new DatabaseRestorer(User);\n * const result = await restorer.restore(123);\n *\n * console.log(result.success); // true\n * console.log(result.strategy); // \"trash\" | \"soft\"\n * ```\n */\nexport class DatabaseRestorer implements RestorerContract {\n /** Model constructor reference */\n private readonly ctor: typeof Model;\n\n /** Data source containing driver */\n private readonly dataSource: DataSource;\n\n /** Database driver for executing queries */\n private readonly driver: DriverContract;\n\n /** Table/collection name */\n private readonly table: string;\n\n /** Primary key field name */\n private readonly primaryKey: string;\n\n /**\n * Create a new restorer instance for a model class.\n *\n * @param modelClass - The model class (static context)\n *\n * @example\n * ```typescript\n * const restorer = new DatabaseRestorer(User);\n * await restorer.restore(123);\n * ```\n */\n public constructor(modelClass: typeof Model) {\n this.ctor = modelClass;\n this.dataSource = modelClass.getDataSource();\n this.driver = this.dataSource.driver;\n this.table = modelClass.table;\n this.primaryKey = modelClass.primaryKey;\n }\n\n /**\n * Restore a single deleted record by its ID.\n *\n * @param id - The primary key value of the record to restore\n * @param options - Restorer options\n * @returns Result containing success status, strategy used, and restored record\n * @throws {Error} If record not found in trash or soft-deleted records\n * @throws {Error} If ID conflict and onIdConflict is \"fail\"\n */\n public async restore(\n id: string | number,\n options: RestorerOptions = {},\n ): Promise<RestorerResult> {\n const onIdConflict = options.onIdConflict ?? \"assignNew\";\n const skipEvents = options.skipEvents ?? false;\n\n // 1. Resolve strategy (options → model static → data source default)\n const strategy = this.resolveStrategy(options.strategy);\n\n // 2. Validate strategy (cannot restore permanent deletes)\n if (strategy === \"permanent\") {\n throw new Error(\n `Cannot restore ${this.ctor.name} with ${this.primaryKey} ${id}: permanently deleted records cannot be restored.`,\n );\n }\n\n // 3. Fetch record based on strategy\n const recordData = await this.fetchRecordByStrategy(id, strategy);\n\n if (!recordData) {\n throw new Error(\n `Cannot restore ${this.ctor.name} with ${this.primaryKey} ${id}: record not found in ${strategy === \"trash\" ? \"trash table\" : \"soft-deleted records\"}.`,\n );\n }\n\n // 4. Prepare record data (remove metadata fields)\n const restoredData = { ...recordData };\n delete restoredData.deletedAt;\n delete restoredData.originalTable;\n\n // 5. Check for ID conflict and handle\n const finalData = await this.handleIdConflict(restoredData, id, onIdConflict);\n\n // 6. Create temporary model instance for event emission\n // Note: Model is abstract, but at runtime this.ctor is a concrete subclass\n const model = new (this.ctor as any)(finalData) as Model;\n\n // 7. Emit restoring event (unless skipEvents)\n if (!skipEvents) {\n await model.emitEvent(\"restoring\");\n }\n\n // 8. Restore based on strategy\n if (strategy === \"trash\") {\n // Insert record back into original table (was moved to trash)\n await this.driver.insert(this.table, finalData);\n model.isNew = false;\n\n // Remove from trash table\n await this.driver.delete(this.resolveTrashTable(), {\n [this.primaryKey]: id,\n });\n } else if (strategy === \"soft\") {\n // Record still exists, just unset deletedAt (don't insert - would create duplicate!)\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (deletedAtColumn) {\n const filter = { [this.primaryKey]: id };\n const updateOperations: UpdateOperations = {\n $unset: { [deletedAtColumn]: 1 },\n };\n\n await this.driver.update(this.table, filter, updateOperations);\n model.isNew = false;\n }\n }\n\n // 10. Emit restored event (unless skipEvents)\n if (!skipEvents) {\n await model.emitEvent(\"restored\");\n }\n\n return {\n success: true,\n restoredCount: 1,\n strategy,\n restoredRecord: model,\n };\n }\n\n /**\n * Restore all deleted records for the model's table.\n *\n * @param options - Restorer options\n * @returns Result containing success status, strategy used, and aggregate counts\n */\n public async restoreAll(options: RestorerOptions = {}): Promise<RestorerResult> {\n const onIdConflict = options.onIdConflict ?? \"assignNew\";\n const skipEvents = options.skipEvents ?? false;\n\n // 1. Resolve strategy (options → model static → data source default)\n const strategy = this.resolveStrategy(options.strategy);\n\n // 2. Validate strategy (cannot restore permanent deletes)\n if (strategy === \"permanent\") {\n throw new Error(\n `Cannot restore all ${this.ctor.name} records: permanently deleted records cannot be restored.`,\n );\n }\n\n // 3. Fetch all records based on strategy\n const recordsToRestore = await this.fetchAllRecordsByStrategy(strategy);\n\n if (recordsToRestore.length === 0) {\n return {\n success: true,\n restoredCount: 0,\n strategy,\n };\n }\n\n // 4. Restore each record\n let restoredCount = 0;\n const conflicts: Array<{ id: string | number; reason: string }> = [];\n\n const restoredRecords: Model[] = [];\n\n for (const recordData of recordsToRestore) {\n const id = recordData[this.primaryKey] as string | number;\n\n try {\n // Prepare record data (remove metadata fields)\n const restoredData = { ...recordData };\n delete restoredData.deletedAt;\n delete restoredData.originalTable;\n\n // Check for ID conflict\n const idExists = await this.checkIdExists(id);\n if (idExists) {\n if (onIdConflict === \"fail\") {\n throw new Error(\n `Cannot restore ${this.ctor.name} with ${this.primaryKey} ${id}: ID already exists in target table.`,\n );\n }\n\n // Assign new ID\n const finalData = await this.assignNewId(restoredData);\n conflicts.push({\n id,\n reason: `ID ${id} already exists, assigned new ID ${finalData[this.primaryKey]}`,\n });\n\n // Create temporary model for events\n const model = new (this.ctor as any)(finalData) as Model;\n\n // Emit restoring event\n if (!skipEvents) {\n await model.emitEvent(\"restoring\");\n }\n\n // Restore based on strategy\n if (strategy === \"trash\") {\n // Insert with new ID (was moved to trash)\n await this.driver.insert(this.table, finalData);\n model.isNew = false;\n } else if (strategy === \"soft\") {\n // Record still exists, just unset deletedAt\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (deletedAtColumn) {\n const filter = { [this.primaryKey]: id };\n const updateOperations: UpdateOperations = {\n $unset: { [deletedAtColumn]: 1 },\n };\n await this.driver.update(this.table, filter, updateOperations);\n model.isNew = false;\n }\n }\n\n restoredRecords.push(model);\n\n // Emit restored event\n if (!skipEvents) {\n await model.emitEvent(\"restored\");\n }\n } else {\n // No conflict, restore with original ID\n const model = new (this.ctor as any)(restoredData) as Model;\n\n // Emit restoring event\n if (!skipEvents) {\n await model.emitEvent(\"restoring\");\n }\n\n // Restore based on strategy\n if (strategy === \"trash\") {\n // Insert with original ID (was moved to trash)\n await this.driver.insert(this.table, restoredData);\n model.isNew = false;\n } else if (strategy === \"soft\") {\n // Record still exists, just unset deletedAt\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (deletedAtColumn) {\n const filter = { [this.primaryKey]: id };\n const updateOperations: UpdateOperations = {\n $unset: { [deletedAtColumn]: 1 },\n };\n await this.driver.update(this.table, filter, updateOperations);\n model.isNew = false;\n }\n }\n\n restoredRecords.push(model);\n\n // Emit restored event\n if (!skipEvents) {\n await model.emitEvent(\"restored\");\n }\n }\n\n // Remove from trash (only for trash strategy)\n if (strategy === \"trash\") {\n const trashTable = this.resolveTrashTable();\n const trashFilter = { [this.primaryKey]: id };\n await this.driver.delete(trashTable, trashFilter);\n }\n\n restoredCount++;\n } catch (error) {\n if (onIdConflict === \"fail\") {\n throw error;\n }\n // Continue with next record if assignNew mode\n conflicts.push({\n id,\n reason: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return {\n success: true,\n restoredCount,\n restoredRecords,\n strategy,\n conflicts: conflicts.length > 0 ? conflicts : undefined,\n };\n }\n\n /**\n * Resolve the delete strategy.\n *\n * Priority: options → model static → data source default → \"permanent\"\n *\n * @param strategyOption - Optional strategy override from options\n * @returns The resolved delete strategy\n * @private\n */\n private resolveStrategy(strategyOption?: \"trash\" | \"soft\"): DeleteStrategy {\n return (\n strategyOption ??\n this.ctor.deleteStrategy ??\n this.dataSource.defaultDeleteStrategy ??\n \"permanent\"\n );\n }\n\n /**\n * Fetch a record by ID based on the delete strategy.\n *\n * @param id - The primary key value\n * @param strategy - The delete strategy to use\n * @returns The record data, or null if not found\n * @private\n */\n private async fetchRecordByStrategy(id: string | number, strategy: \"trash\" | \"soft\") {\n if (strategy === \"trash\") {\n const trashTable = this.resolveTrashTable();\n try {\n const trashQuery = await this.driver\n .queryBuilder(trashTable)\n .where(this.primaryKey, id)\n .first<Record<string, unknown>>();\n\n return trashQuery;\n } catch {\n return null;\n }\n } else if (strategy === \"soft\") {\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (!deletedAtColumn) {\n return null;\n }\n\n try {\n const softDeletedQuery = await this.driver\n .queryBuilder(this.table)\n .where(this.primaryKey, id)\n .whereNotNull(deletedAtColumn)\n .first<Record<string, unknown>>();\n\n return softDeletedQuery;\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n /**\n * Fetch all records based on the delete strategy.\n *\n * @param strategy - The delete strategy to use\n * @returns Array of record data\n * @private\n */\n private async fetchAllRecordsByStrategy(\n strategy: \"trash\" | \"soft\",\n ): Promise<Record<string, unknown>[]> {\n if (strategy === \"trash\") {\n const trashTable = this.resolveTrashTable();\n try {\n const trashQuery = this.driver\n .queryBuilder(trashTable)\n .where(\"originalTable\", this.table)\n .get();\n\n return (await trashQuery) as Record<string, unknown>[];\n } catch {\n return [];\n }\n } else if (strategy === \"soft\") {\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (!deletedAtColumn) {\n return [];\n }\n\n try {\n const softDeletedQuery = this.driver\n .queryBuilder(this.table)\n .whereNotNull(deletedAtColumn)\n .get();\n\n return (await softDeletedQuery) as Record<string, unknown>[];\n } catch {\n return [];\n }\n }\n\n return [];\n }\n\n /**\n * Handle ID conflict by checking if ID exists and assigning new one if needed.\n *\n * @param recordData - The record data to restore\n * @param originalId - The original ID value\n * @param onIdConflict - Conflict resolution strategy\n * @returns Record data with potentially new ID\n * @private\n */\n private async handleIdConflict(\n recordData: Record<string, unknown>,\n originalId: string | number,\n onIdConflict: \"fail\" | \"assignNew\",\n ): Promise<Record<string, unknown>> {\n const idExists = await this.checkIdExists(originalId);\n\n if (idExists) {\n if (onIdConflict === \"fail\") {\n throw new Error(\n `Cannot restore ${this.ctor.name} with ${this.primaryKey} ${originalId}: ID already exists in target table.`,\n );\n }\n\n // Assign new ID\n return await this.assignNewId(recordData);\n }\n\n return recordData;\n }\n\n /**\n * Check if an ID already exists in the target table.\n *\n * @param id - The ID to check\n * @returns True if ID exists, false otherwise\n * @private\n */\n private async checkIdExists(id: string | number): Promise<boolean> {\n try {\n const query = this.driver.queryBuilder(this.table).where(this.primaryKey, id).exists();\n\n return await query;\n } catch {\n return false;\n }\n }\n\n /**\n * Assign a new ID to the record data.\n *\n * For MongoDB: Generates new ObjectId for `_id`, keeps `id` if it exists\n * For SQL: Removes `id` to let database auto-increment\n *\n * @param recordData - The record data\n * @returns Record data with new ID assigned\n * @private\n */\n private async assignNewId(recordData: Record<string, unknown>): Promise<Record<string, unknown>> {\n const isMongoDb = this.driver.name === \"mongodb\";\n const newData = { ...recordData };\n\n if (isMongoDb) {\n // MongoDB: Generate new ObjectId for _id\n if (this.primaryKey === \"_id\") {\n // Remove _id to let MongoDB generate new one\n delete newData._id;\n } else if (this.primaryKey === \"id\") {\n // Remove id to let ID generator create new one\n delete newData.id;\n }\n } else {\n // SQL: Remove primary key to let database auto-increment\n delete newData[this.primaryKey];\n }\n\n return newData;\n }\n\n /**\n * Resolve the trash table/collection name.\n *\n * Priority:\n * 1. Model.trashTable (if set)\n * 2. Data source defaultTrashTable (e.g., \"RecycleBin\" for MongoDB)\n * 3. Default pattern: `{table}Trash`\n *\n * @returns The trash table/collection name\n * @private\n */\n private resolveTrashTable(): string {\n if (this.ctor.trashTable) {\n return this.ctor.trashTable;\n }\n\n if (this.dataSource.defaultTrashTable) {\n return this.dataSource.defaultTrashTable;\n }\n\n return `${this.table}Trash`;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"database-restorer.mjs","names":[],"sources":["../../../../../../../cascade/src/restorer/database-restorer.ts"],"sourcesContent":["import type { DriverContract, UpdateOperations } from \"../contracts/database-driver.contract\";\nimport type {\n RestorerContract,\n RestorerOptions,\n RestorerResult,\n} from \"../contracts/database-restorer.contract\";\nimport type { DataSource } from \"../data-source/data-source\";\nimport type { Model } from \"../model/model\";\nimport type { DeleteStrategy } from \"../types\";\n\n/**\n * Database restorer service that orchestrates model restoration.\n *\n * Handles the complete restoration pipeline:\n * 1. Strategy detection (trash vs soft delete)\n * 2. Record retrieval from trash table or soft-deleted records\n * 3. ID conflict resolution\n * 4. Event emission (restoring, restored)\n * 5. Driver execution (insert back to original table, remove from trash/clear deletedAt)\n *\n * @example\n * ```typescript\n * const restorer = new DatabaseRestorer(User);\n * const result = await restorer.restore(123);\n *\n * console.log(result.success); // true\n * console.log(result.strategy); // \"trash\" | \"soft\"\n * ```\n */\nexport class DatabaseRestorer implements RestorerContract {\n /** Model constructor reference */\n private readonly ctor: typeof Model;\n\n /** Data source containing driver */\n private readonly dataSource: DataSource;\n\n /** Database driver for executing queries */\n private readonly driver: DriverContract;\n\n /** Table/collection name */\n private readonly table: string;\n\n /** Primary key field name */\n private readonly primaryKey: string;\n\n /**\n * Create a new restorer instance for a model class.\n *\n * @param modelClass - The model class (static context)\n *\n * @example\n * ```typescript\n * const restorer = new DatabaseRestorer(User);\n * await restorer.restore(123);\n * ```\n */\n public constructor(modelClass: typeof Model) {\n this.ctor = modelClass;\n this.dataSource = modelClass.getDataSource();\n this.driver = this.dataSource.driver;\n this.table = modelClass.table;\n this.primaryKey = modelClass.primaryKey;\n }\n\n /**\n * Restore a single deleted record by its ID.\n *\n * @param id - The primary key value of the record to restore\n * @param options - Restorer options\n * @returns Result containing success status, strategy used, and restored record\n * @throws {Error} If record not found in trash or soft-deleted records\n * @throws {Error} If ID conflict and onIdConflict is \"fail\"\n */\n public async restore(\n id: string | number,\n options: RestorerOptions = {},\n ): Promise<RestorerResult> {\n const onIdConflict = options.onIdConflict ?? \"assignNew\";\n const skipEvents = options.skipEvents ?? false;\n\n // 1. Resolve strategy (options → model static → data source default)\n const strategy = this.resolveStrategy(options.strategy);\n\n // 2. Validate strategy (cannot restore permanent deletes)\n if (strategy === \"permanent\") {\n throw new Error(\n `Cannot restore ${this.ctor.name} with ${this.primaryKey} ${id}: permanently deleted records cannot be restored.`,\n );\n }\n\n // 3. Fetch record based on strategy\n const recordData = await this.fetchRecordByStrategy(id, strategy);\n\n if (!recordData) {\n throw new Error(\n `Cannot restore ${this.ctor.name} with ${this.primaryKey} ${id}: record not found in ${strategy === \"trash\" ? \"trash table\" : \"soft-deleted records\"}.`,\n );\n }\n\n // 4. Prepare record data (remove metadata fields)\n const restoredData = { ...recordData };\n delete restoredData.deletedAt;\n delete restoredData.originalTable;\n\n // 5. Check for ID conflict and handle\n const finalData = await this.handleIdConflict(restoredData, id, onIdConflict);\n\n // 6. Create temporary model instance for event emission\n // Note: Model is abstract, but at runtime this.ctor is a concrete subclass\n const model = new (this.ctor as any)(finalData) as Model;\n\n // 7. Emit restoring event (unless skipEvents)\n if (!skipEvents) {\n await model.emitEvent(\"restoring\");\n }\n\n // 8. Restore based on strategy\n if (strategy === \"trash\") {\n // Insert record back into original table (was moved to trash)\n await this.driver.insert(this.table, finalData);\n model.isNew = false;\n\n // Remove from trash table\n await this.driver.delete(this.resolveTrashTable(), {\n [this.primaryKey]: id,\n });\n } else if (strategy === \"soft\") {\n // Record still exists, just unset deletedAt (don't insert - would create duplicate!)\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (deletedAtColumn) {\n const filter = { [this.primaryKey]: id };\n const updateOperations: UpdateOperations = {\n $unset: { [deletedAtColumn]: 1 },\n };\n\n await this.driver.update(this.table, filter, updateOperations);\n model.isNew = false;\n }\n }\n\n // 10. Emit restored event (unless skipEvents)\n if (!skipEvents) {\n await model.emitEvent(\"restored\");\n }\n\n return {\n success: true,\n restoredCount: 1,\n strategy,\n restoredRecord: model,\n };\n }\n\n /**\n * Restore all deleted records for the model's table.\n *\n * @param options - Restorer options\n * @returns Result containing success status, strategy used, and aggregate counts\n */\n public async restoreAll(options: RestorerOptions = {}): Promise<RestorerResult> {\n const onIdConflict = options.onIdConflict ?? \"assignNew\";\n const skipEvents = options.skipEvents ?? false;\n\n // 1. Resolve strategy (options → model static → data source default)\n const strategy = this.resolveStrategy(options.strategy);\n\n // 2. Validate strategy (cannot restore permanent deletes)\n if (strategy === \"permanent\") {\n throw new Error(\n `Cannot restore all ${this.ctor.name} records: permanently deleted records cannot be restored.`,\n );\n }\n\n // 3. Fetch all records based on strategy\n const recordsToRestore = await this.fetchAllRecordsByStrategy(strategy);\n\n if (recordsToRestore.length === 0) {\n return {\n success: true,\n restoredCount: 0,\n strategy,\n };\n }\n\n // 4. Restore each record\n let restoredCount = 0;\n const conflicts: Array<{ id: string | number; reason: string }> = [];\n\n const restoredRecords: Model[] = [];\n\n for (const recordData of recordsToRestore) {\n const id = recordData[this.primaryKey] as string | number;\n\n try {\n // Prepare record data (remove metadata fields)\n const restoredData = { ...recordData };\n delete restoredData.deletedAt;\n delete restoredData.originalTable;\n\n // Check for ID conflict\n const idExists = await this.checkIdExists(id);\n if (idExists) {\n if (onIdConflict === \"fail\") {\n throw new Error(\n `Cannot restore ${this.ctor.name} with ${this.primaryKey} ${id}: ID already exists in target table.`,\n );\n }\n\n // Assign new ID\n const finalData = await this.assignNewId(restoredData);\n conflicts.push({\n id,\n reason: `ID ${id} already exists, assigned new ID ${finalData[this.primaryKey]}`,\n });\n\n // Create temporary model for events\n const model = new (this.ctor as any)(finalData) as Model;\n\n // Emit restoring event\n if (!skipEvents) {\n await model.emitEvent(\"restoring\");\n }\n\n // Restore based on strategy\n if (strategy === \"trash\") {\n // Insert with new ID (was moved to trash)\n await this.driver.insert(this.table, finalData);\n model.isNew = false;\n } else if (strategy === \"soft\") {\n // Record still exists, just unset deletedAt\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (deletedAtColumn) {\n const filter = { [this.primaryKey]: id };\n const updateOperations: UpdateOperations = {\n $unset: { [deletedAtColumn]: 1 },\n };\n await this.driver.update(this.table, filter, updateOperations);\n model.isNew = false;\n }\n }\n\n restoredRecords.push(model);\n\n // Emit restored event\n if (!skipEvents) {\n await model.emitEvent(\"restored\");\n }\n } else {\n // No conflict, restore with original ID\n const model = new (this.ctor as any)(restoredData) as Model;\n\n // Emit restoring event\n if (!skipEvents) {\n await model.emitEvent(\"restoring\");\n }\n\n // Restore based on strategy\n if (strategy === \"trash\") {\n // Insert with original ID (was moved to trash)\n await this.driver.insert(this.table, restoredData);\n model.isNew = false;\n } else if (strategy === \"soft\") {\n // Record still exists, just unset deletedAt\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (deletedAtColumn) {\n const filter = { [this.primaryKey]: id };\n const updateOperations: UpdateOperations = {\n $unset: { [deletedAtColumn]: 1 },\n };\n await this.driver.update(this.table, filter, updateOperations);\n model.isNew = false;\n }\n }\n\n restoredRecords.push(model);\n\n // Emit restored event\n if (!skipEvents) {\n await model.emitEvent(\"restored\");\n }\n }\n\n // Remove from trash (only for trash strategy)\n if (strategy === \"trash\") {\n const trashTable = this.resolveTrashTable();\n const trashFilter = { [this.primaryKey]: id };\n await this.driver.delete(trashTable, trashFilter);\n }\n\n restoredCount++;\n } catch (error) {\n if (onIdConflict === \"fail\") {\n throw error;\n }\n // Continue with next record if assignNew mode\n conflicts.push({\n id,\n reason: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return {\n success: true,\n restoredCount,\n restoredRecords,\n strategy,\n conflicts: conflicts.length > 0 ? conflicts : undefined,\n };\n }\n\n /**\n * Resolve the delete strategy.\n *\n * Priority: options → model static → data source default → \"permanent\"\n *\n * @param strategyOption - Optional strategy override from options\n * @returns The resolved delete strategy\n * @private\n */\n private resolveStrategy(strategyOption?: \"trash\" | \"soft\"): DeleteStrategy {\n return (\n strategyOption ??\n this.ctor.deleteStrategy ??\n this.dataSource.defaultDeleteStrategy ??\n \"permanent\"\n );\n }\n\n /**\n * Fetch a record by ID based on the delete strategy.\n *\n * @param id - The primary key value\n * @param strategy - The delete strategy to use\n * @returns The record data, or null if not found\n * @private\n */\n private async fetchRecordByStrategy(id: string | number, strategy: \"trash\" | \"soft\") {\n if (strategy === \"trash\") {\n const trashTable = this.resolveTrashTable();\n try {\n const trashQuery = await this.driver\n .queryBuilder(trashTable)\n .where(this.primaryKey, id)\n .first<Record<string, unknown>>();\n\n return trashQuery;\n } catch {\n return null;\n }\n } else if (strategy === \"soft\") {\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (!deletedAtColumn) {\n return null;\n }\n\n try {\n const softDeletedQuery = await this.driver\n .queryBuilder(this.table)\n .where(this.primaryKey, id)\n .whereNotNull(deletedAtColumn)\n .first<Record<string, unknown>>();\n\n return softDeletedQuery;\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n /**\n * Fetch all records based on the delete strategy.\n *\n * @param strategy - The delete strategy to use\n * @returns Array of record data\n * @private\n */\n private async fetchAllRecordsByStrategy(\n strategy: \"trash\" | \"soft\",\n ): Promise<Record<string, unknown>[]> {\n if (strategy === \"trash\") {\n const trashTable = this.resolveTrashTable();\n try {\n const trashQuery = this.driver\n .queryBuilder(trashTable)\n .where(\"originalTable\", this.table)\n .get();\n\n return (await trashQuery) as Record<string, unknown>[];\n } catch {\n return [];\n }\n } else if (strategy === \"soft\") {\n const deletedAtColumn = this.ctor.deletedAtColumn ?? \"deletedAt\";\n if (!deletedAtColumn) {\n return [];\n }\n\n try {\n const softDeletedQuery = this.driver\n .queryBuilder(this.table)\n .whereNotNull(deletedAtColumn)\n .get();\n\n return (await softDeletedQuery) as Record<string, unknown>[];\n } catch {\n return [];\n }\n }\n\n return [];\n }\n\n /**\n * Handle ID conflict by checking if ID exists and assigning new one if needed.\n *\n * @param recordData - The record data to restore\n * @param originalId - The original ID value\n * @param onIdConflict - Conflict resolution strategy\n * @returns Record data with potentially new ID\n * @private\n */\n private async handleIdConflict(\n recordData: Record<string, unknown>,\n originalId: string | number,\n onIdConflict: \"fail\" | \"assignNew\",\n ): Promise<Record<string, unknown>> {\n const idExists = await this.checkIdExists(originalId);\n\n if (idExists) {\n if (onIdConflict === \"fail\") {\n throw new Error(\n `Cannot restore ${this.ctor.name} with ${this.primaryKey} ${originalId}: ID already exists in target table.`,\n );\n }\n\n // Assign new ID\n return await this.assignNewId(recordData);\n }\n\n return recordData;\n }\n\n /**\n * Check if an ID already exists in the target table.\n *\n * @param id - The ID to check\n * @returns True if ID exists, false otherwise\n * @private\n */\n private async checkIdExists(id: string | number): Promise<boolean> {\n try {\n const query = this.driver.queryBuilder(this.table).where(this.primaryKey, id).exists();\n\n return await query;\n } catch {\n return false;\n }\n }\n\n /**\n * Assign a new ID to the record data.\n *\n * For MongoDB: Generates new ObjectId for `_id`, keeps `id` if it exists\n * For SQL: Removes `id` to let database auto-increment\n *\n * @param recordData - The record data\n * @returns Record data with new ID assigned\n * @private\n */\n private async assignNewId(recordData: Record<string, unknown>): Promise<Record<string, unknown>> {\n const isMongoDb = this.driver.name === \"mongodb\";\n const newData = { ...recordData };\n\n if (isMongoDb) {\n // MongoDB: Generate new ObjectId for _id\n if (this.primaryKey === \"_id\") {\n // Remove _id to let MongoDB generate new one\n delete newData._id;\n } else if (this.primaryKey === \"id\") {\n // Remove id to let ID generator create new one\n delete newData.id;\n }\n } else {\n // SQL: Remove primary key to let database auto-increment\n delete newData[this.primaryKey];\n }\n\n return newData;\n }\n\n /**\n * Resolve the trash table/collection name.\n *\n * Priority:\n * 1. Model.trashTable (if set)\n * 2. Data source defaultTrashTable (e.g., \"RecycleBin\" for MongoDB)\n * 3. Default pattern: `{table}Trash`\n *\n * @returns The trash table/collection name\n * @private\n */\n private resolveTrashTable(): string {\n if (this.ctor.trashTable) {\n return this.ctor.trashTable;\n }\n\n if (this.dataSource.defaultTrashTable) {\n return this.dataSource.defaultTrashTable;\n }\n\n return `${this.table}Trash`;\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { DatabaseDirtyTracker } from "./database-dirty-tracker.mjs";
2
2
 
3
- //#region ../@warlock.js/cascade/src/sql-database-dirty-tracker.d.ts
3
+ //#region ../cascade/src/sql-database-dirty-tracker.d.ts
4
4
  /**
5
5
  * A dirty tracker designed for SQL databases where nested objects shouldn't be flattened.
6
6
  * Since SQL drivers (like Postgres) replace the entire JSON column when updated,
@@ -1 +1 @@
1
- {"version":3,"file":"sql-database-dirty-tracker.d.mts","names":[],"sources":["../../../../../../@warlock.js/cascade/src/sql-database-dirty-tracker.ts"],"mappings":";;;;;AAOA;;;cAAa,uBAAA,SAAgC,oBAAA;EAIoB;;;EAAA,UAA5C,WAAA,CAAY,IAAA,EAAM,MAAA,oBAA0B,MAAA;AAAA"}
1
+ {"version":3,"file":"sql-database-dirty-tracker.d.mts","names":[],"sources":["../../../../../../cascade/src/sql-database-dirty-tracker.ts"],"mappings":";;;;;AAOA;;;cAAa,uBAAA,SAAgC,oBAAA;EAIoB;;;EAAA,UAA5C,WAAA,CAAY,IAAA,EAAM,MAAA,oBAA0B,MAAA;AAAA"}
@@ -1,6 +1,6 @@
1
1
  import { DatabaseDirtyTracker } from "./database-dirty-tracker.mjs";
2
2
 
3
- //#region ../@warlock.js/cascade/src/sql-database-dirty-tracker.ts
3
+ //#region ../cascade/src/sql-database-dirty-tracker.ts
4
4
  /**
5
5
  * A dirty tracker designed for SQL databases where nested objects shouldn't be flattened.
6
6
  * Since SQL drivers (like Postgres) replace the entire JSON column when updated,
@@ -1 +1 @@
1
- {"version":3,"file":"sql-database-dirty-tracker.mjs","names":[],"sources":["../../../../../../@warlock.js/cascade/src/sql-database-dirty-tracker.ts"],"sourcesContent":["import { DatabaseDirtyTracker } from \"./database-dirty-tracker\";\n\n/**\n * A dirty tracker designed for SQL databases where nested objects shouldn't be flattened.\n * Since SQL drivers (like Postgres) replace the entire JSON column when updated,\n * we keep the object intact instead of using dot-notation keys.\n */\nexport class SqlDatabaseDirtyTracker extends DatabaseDirtyTracker {\n /**\n * Overrides the default flattening behavior to keep the raw data structure.\n */\n protected override flattenData(data: Record<string, unknown>): Record<string, unknown> {\n // Return a shallow copy of the data without flattening nested objects\n return { ...data };\n }\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,0BAAb,cAA6C,qBAAqB;;;;CAIhE,AAAmB,YAAY,MAAwD;EAErF,OAAO,EAAE,GAAG,KAAK;CACnB;AACF"}
1
+ {"version":3,"file":"sql-database-dirty-tracker.mjs","names":[],"sources":["../../../../../../cascade/src/sql-database-dirty-tracker.ts"],"sourcesContent":["import { DatabaseDirtyTracker } from \"./database-dirty-tracker\";\n\n/**\n * A dirty tracker designed for SQL databases where nested objects shouldn't be flattened.\n * Since SQL drivers (like Postgres) replace the entire JSON column when updated,\n * we keep the object intact instead of using dot-notation keys.\n */\nexport class SqlDatabaseDirtyTracker extends DatabaseDirtyTracker {\n /**\n * Overrides the default flattening behavior to keep the raw data structure.\n */\n protected override flattenData(data: Record<string, unknown>): Record<string, unknown> {\n // Return a shallow copy of the data without flattening nested objects\n return { ...data };\n }\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,0BAAb,cAA6C,qBAAqB;;;;CAIhE,AAAmB,YAAY,MAAwD;EAErF,OAAO,EAAE,GAAG,KAAK;CACnB;AACF"}
@@ -1,4 +1,4 @@
1
- //#region ../@warlock.js/cascade/src/sync/model-events.ts
1
+ //#region ../cascade/src/sync/model-events.ts
2
2
  /**
3
3
  * Event name prefix for all model sync events.
4
4
  */
@@ -1 +1 @@
1
- {"version":3,"file":"model-events.mjs","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/sync/model-events.ts"],"sourcesContent":["/**\n * Model sync event name helpers.\n *\n * Provides type-safe event names for model sync operations.\n * Avoids hardcoded string literals throughout the codebase.\n *\n * @module cascade-next/sync/model-events\n */\n\nimport type { ChildModel, Model } from \"../model/model\";\n\n/**\n * Event name prefix for all model sync events.\n */\nexport const MODEL_EVENT_PREFIX = \"model\";\n\n/**\n * Model sync event types.\n */\nexport const ModelSyncEventType = {\n UPDATED: \"updated\",\n DELETED: \"deleted\",\n} as const;\n\nexport type ModelSyncEventTypeName = (typeof ModelSyncEventType)[keyof typeof ModelSyncEventType];\n\n/**\n * Get the event name for a model update.\n *\n * @param modelClass - The model class\n * @returns Event name string (e.g., \"model.Category.updated\")\n *\n * @example\n * ```typescript\n * const eventName = getModelUpdatedEvent(Category);\n * // Returns: \"model.Category.updated\"\n * ```\n */\nexport function getModelUpdatedEvent(modelClass: ChildModel<Model>): string {\n return `${MODEL_EVENT_PREFIX}.${modelClass.name}.${ModelSyncEventType.UPDATED}`;\n}\n\n/**\n * Get the event name for a model deletion.\n *\n * @param modelClass - The model class\n * @returns Event name string (e.g., \"model.Category.deleted\")\n *\n * @example\n * ```typescript\n * const eventName = getModelDeletedEvent(Category);\n * // Returns: \"model.Category.deleted\"\n * ```\n */\nexport function getModelDeletedEvent(modelClass: ChildModel<Model>): string {\n return `${MODEL_EVENT_PREFIX}.${modelClass.name}.${ModelSyncEventType.DELETED}`;\n}\n\n/**\n * Get the event name for a model by name string.\n *\n * @param modelName - The model class name\n * @param eventType - The event type\n * @returns Event name string\n *\n * @example\n * ```typescript\n * const eventName = getModelEvent(\"Category\", \"updated\");\n * // Returns: \"model.Category.updated\"\n * ```\n */\nexport function getModelEvent(modelName: string, eventType: ModelSyncEventTypeName): string {\n return `${MODEL_EVENT_PREFIX}.${modelName}.${eventType}`;\n}\n"],"mappings":";;;;AAcA,MAAa,qBAAqB;;;;AAKlC,MAAa,qBAAqB;CAChC,SAAS;CACT,SAAS;AACX;;;;;;;;;;;;;AAgBA,SAAgB,qBAAqB,YAAuC;CAC1E,OAAO,GAAG,mBAAmB,GAAG,WAAW,KAAK,GAAG,mBAAmB;AACxE;;;;;;;;;;;;;AAcA,SAAgB,qBAAqB,YAAuC;CAC1E,OAAO,GAAG,mBAAmB,GAAG,WAAW,KAAK,GAAG,mBAAmB;AACxE"}
1
+ {"version":3,"file":"model-events.mjs","names":[],"sources":["../../../../../../../cascade/src/sync/model-events.ts"],"sourcesContent":["/**\n * Model sync event name helpers.\n *\n * Provides type-safe event names for model sync operations.\n * Avoids hardcoded string literals throughout the codebase.\n *\n * @module cascade-next/sync/model-events\n */\n\nimport type { ChildModel, Model } from \"../model/model\";\n\n/**\n * Event name prefix for all model sync events.\n */\nexport const MODEL_EVENT_PREFIX = \"model\";\n\n/**\n * Model sync event types.\n */\nexport const ModelSyncEventType = {\n UPDATED: \"updated\",\n DELETED: \"deleted\",\n} as const;\n\nexport type ModelSyncEventTypeName = (typeof ModelSyncEventType)[keyof typeof ModelSyncEventType];\n\n/**\n * Get the event name for a model update.\n *\n * @param modelClass - The model class\n * @returns Event name string (e.g., \"model.Category.updated\")\n *\n * @example\n * ```typescript\n * const eventName = getModelUpdatedEvent(Category);\n * // Returns: \"model.Category.updated\"\n * ```\n */\nexport function getModelUpdatedEvent(modelClass: ChildModel<Model>): string {\n return `${MODEL_EVENT_PREFIX}.${modelClass.name}.${ModelSyncEventType.UPDATED}`;\n}\n\n/**\n * Get the event name for a model deletion.\n *\n * @param modelClass - The model class\n * @returns Event name string (e.g., \"model.Category.deleted\")\n *\n * @example\n * ```typescript\n * const eventName = getModelDeletedEvent(Category);\n * // Returns: \"model.Category.deleted\"\n * ```\n */\nexport function getModelDeletedEvent(modelClass: ChildModel<Model>): string {\n return `${MODEL_EVENT_PREFIX}.${modelClass.name}.${ModelSyncEventType.DELETED}`;\n}\n\n/**\n * Get the event name for a model by name string.\n *\n * @param modelName - The model class name\n * @param eventType - The event type\n * @returns Event name string\n *\n * @example\n * ```typescript\n * const eventName = getModelEvent(\"Category\", \"updated\");\n * // Returns: \"model.Category.updated\"\n * ```\n */\nexport function getModelEvent(modelName: string, eventType: ModelSyncEventTypeName): string {\n return `${MODEL_EVENT_PREFIX}.${modelName}.${eventType}`;\n}\n"],"mappings":";;;;AAcA,MAAa,qBAAqB;;;;AAKlC,MAAa,qBAAqB;CAChC,SAAS;CACT,SAAS;AACX;;;;;;;;;;;;;AAgBA,SAAgB,qBAAqB,YAAuC;CAC1E,OAAO,GAAG,mBAAmB,GAAG,WAAW,KAAK,GAAG,mBAAmB;AACxE;;;;;;;;;;;;;AAcA,SAAgB,qBAAqB,YAAuC;CAC1E,OAAO,GAAG,mBAAmB,GAAG,WAAW,KAAK,GAAG,mBAAmB;AACxE"}
@@ -2,7 +2,7 @@ import { ChildModel } from "../model/model.types.mjs";
2
2
  import { ModelSyncConfig, ModelSyncOperationContract } from "./types.mjs";
3
3
  import { Model } from "../model/model.mjs";
4
4
 
5
- //#region ../@warlock.js/cascade/src/sync/model-sync-operation.d.ts
5
+ //#region ../cascade/src/sync/model-sync-operation.d.ts
6
6
  /**
7
7
  * Manages a single model sync operation.
8
8
  *
@@ -1 +1 @@
1
- {"version":3,"file":"model-sync-operation.d.mts","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/sync/model-sync-operation.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;cAiCa,kBAAA,YAA8B,0BAAA;EA2BvC;;;EAAA,iBAvBe,MAAA;EA0EV;;;EAAA,iBArEU,aAAA;EAsGV;;;EAAA,QAjGC,YAAA;EA4IA;;;;;;;;cAjIN,gBAAA,EAAkB,UAAA,CAAW,KAAA,GAC7B,gBAAA,EAAkB,UAAA,CAAW,KAAA,GAC7B,WAAA,UACA,MAAA;EAkSkB;;;AAAwB;;;;;;;;EAjQrC,KAAA,CAAM,MAAA;;;;;;;;;;;;;EAiBN,UAAA,CAAW,KAAA;;;;;;;;;;;;EAgBX,QAAA,CAAS,KAAA;;;;;;;;;;;;;EAiBT,WAAA,CAAY,MAAA;;;;;;;;;;;EAeZ,aAAA;;;;;;;;;;;EAeA,cAAA;;;;;UAaC,SAAA;;;;;EAwBD,WAAA;;;;EAYA,QAAA;;;;;UAYC,kBAAA;;;;;UAsCA,kBAAA;;;;UAuCA,UAAA;;;;UAWA,eAAA;;;;UAiBM,qBAAA;;;;EAWP,SAAA,IAAa,QAAA,CAAS,eAAA;AAAA"}
1
+ {"version":3,"file":"model-sync-operation.d.mts","names":[],"sources":["../../../../../../../cascade/src/sync/model-sync-operation.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;cAiCa,kBAAA,YAA8B,0BAAA;EA2BvC;;;EAAA,iBAvBe,MAAA;EA0EV;;;EAAA,iBArEU,aAAA;EAsGV;;;EAAA,QAjGC,YAAA;EA4IA;;;;;;;;cAjIN,gBAAA,EAAkB,UAAA,CAAW,KAAA,GAC7B,gBAAA,EAAkB,UAAA,CAAW,KAAA,GAC7B,WAAA,UACA,MAAA;EAkSkB;;;AAAwB;;;;;;;;EAjQrC,KAAA,CAAM,MAAA;;;;;;;;;;;;;EAiBN,UAAA,CAAW,KAAA;;;;;;;;;;;;EAgBX,QAAA,CAAS,KAAA;;;;;;;;;;;;;EAiBT,WAAA,CAAY,MAAA;;;;;;;;;;;EAeZ,aAAA;;;;;;;;;;;EAeA,cAAA;;;;;UAaC,SAAA;;;;;EAwBD,WAAA;;;;EAYA,QAAA;;;;;UAYC,kBAAA;;;;;UAsCA,kBAAA;;;;UAuCA,UAAA;;;;UAWA,eAAA;;;;UAiBM,qBAAA;;;;EAWP,SAAA,IAAa,QAAA,CAAS,eAAA;AAAA"}
@@ -3,7 +3,7 @@ import { DEFAULT_MAX_SYNC_DEPTH } from "./sync-context.mjs";
3
3
  import { SyncManager } from "./sync-manager.mjs";
4
4
  import events from "@mongez/events";
5
5
 
6
- //#region ../@warlock.js/cascade/src/sync/model-sync-operation.ts
6
+ //#region ../cascade/src/sync/model-sync-operation.ts
7
7
  /**
8
8
  * Model sync operation class.
9
9
  *
@@ -1 +1 @@
1
- {"version":3,"file":"model-sync-operation.mjs","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/sync/model-sync-operation.ts"],"sourcesContent":["/**\n * Model sync operation class.\n *\n * Manages a single sync relationship between a source and target model.\n * Subscribes to source model events and triggers sync operations when\n * the source is updated or deleted.\n *\n * @module cascade-next/sync/model-sync-operation\n */\n\nimport events, { type EventSubscription } from \"@mongez/events\";\nimport type { ChildModel, Model } from \"../model/model\";\nimport { getModelDeletedEvent, getModelUpdatedEvent } from \"./model-events\";\nimport { DEFAULT_MAX_SYNC_DEPTH } from \"./sync-context\";\nimport { SyncManager } from \"./sync-manager\";\nimport type { ModelSyncConfig, ModelSyncOperationContract } from \"./types\";\n\n/**\n * Manages a single model sync operation.\n *\n * Subscribes to source model events (updated, deleted) and triggers\n * sync operations to update embedded data in target models.\n *\n * @example\n * ```typescript\n * // Created via modelSync.sync() or Model.sync()\n * const operation = new ModelSyncOperation(Category, Product, \"category\", false);\n * operation\n * .embed(\"embedMinimal\")\n * .watchFields([\"name\", \"slug\"])\n * .unsetOnDelete();\n * ```\n */\nexport class ModelSyncOperation implements ModelSyncOperationContract {\n /**\n * Configuration for this sync operation.\n */\n private readonly config: ModelSyncConfig;\n\n /**\n * Active event subscriptions for cleanup.\n */\n private readonly subscriptions: EventSubscription[] = [];\n\n /**\n * Whether this operation is currently subscribed to events.\n */\n private isSubscribed = false;\n\n /**\n * Create a new model sync operation.\n *\n * @param sourceModel - Source model class that triggers sync\n * @param targetModel - Target model class that receives data\n * @param targetField - Field path in target model\n * @param isMany - Whether this syncs to an array field\n */\n public constructor(\n sourceModelClass: ChildModel<Model>,\n targetModelClass: ChildModel<Model>,\n targetField: string,\n isMany: boolean,\n ) {\n this.config = {\n sourceModel: sourceModelClass,\n targetModel: targetModelClass,\n targetField,\n isMany,\n embedKey: \"embedData\",\n identifierField: \"id\",\n maxSyncDepth: DEFAULT_MAX_SYNC_DEPTH,\n watchFields: [],\n unsetOnDelete: false,\n removeOnDelete: false,\n };\n\n this.subscribe();\n }\n\n // ============================================================================\n // FLUENT CONFIGURATION\n // ============================================================================\n\n /**\n * Set the embed method to call on source model.\n *\n * @param embed - getter property name (e.g., \"embedData\", \"embedMinimal\") Or Array of fields\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Category.sync(Product, \"category\").embed(\"embedMinimal\");\n * ```\n */\n public embed(method: string | string[]): this {\n this.config.embedKey = method;\n return this;\n }\n\n /**\n * Set the identifier field for array matching.\n * Required when syncing to array fields (syncMany).\n *\n * @param field - Field name used as identifier (default: \"id\")\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Tag.syncMany(Post, \"tags\").identifyBy(\"tagId\");\n * ```\n */\n public identifyBy(field: string): this {\n this.config.identifierField = field;\n return this;\n }\n\n /**\n * Set the maximum sync depth for chained operations.\n *\n * @param depth - Maximum depth (default: 3)\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Category.sync(Product, \"category\").maxDepth(2);\n * ```\n */\n public maxDepth(depth: number): this {\n this.config.maxSyncDepth = depth;\n return this;\n }\n\n /**\n * Set which fields to watch for changes.\n * Sync only triggers when these fields change.\n *\n * @param fields - Array of field names to watch (empty = all)\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Category.sync(Product, \"category\").watchFields([\"name\", \"slug\"]);\n * ```\n */\n public watchFields(fields: string[]): this {\n this.config.watchFields = fields;\n return this;\n }\n\n /**\n * Unset the target field when source is deleted.\n *\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Category.sync(Product, \"category\").unsetOnDelete();\n * ```\n */\n public unsetOnDelete(): this {\n this.config.unsetOnDelete = true;\n return this;\n }\n\n /**\n * Delete target documents when source is deleted.\n *\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * User.sync(Profile, \"user\").removeOnDelete();\n * ```\n */\n public removeOnDelete(): this {\n this.config.removeOnDelete = true;\n return this;\n }\n\n // ============================================================================\n // LIFECYCLE\n // ============================================================================\n\n /**\n * Subscribe to source model events.\n * Called automatically in constructor.\n */\n private subscribe(): void {\n if (this.isSubscribed) {\n return;\n }\n\n const sourceModel = this.config.sourceModel;\n\n // Subscribe to model.updated event\n this.subscriptions.push(\n events.subscribe(getModelUpdatedEvent(sourceModel), this.handleModelUpdated),\n );\n\n // Subscribe to model.deleted event\n this.subscriptions.push(\n events.subscribe(getModelDeletedEvent(sourceModel), this.handleModelDeleted),\n );\n\n this.isSubscribed = true;\n }\n\n /**\n * Unsubscribe from all events and cleanup.\n * Called automatically when using modelSync.register().\n */\n public unsubscribe(): void {\n for (const subscription of this.subscriptions) {\n subscription.unsubscribe?.();\n }\n\n this.subscriptions.length = 0;\n this.isSubscribed = false;\n }\n\n /**\n * Cleanup the sync operations\n */\n public $cleanup() {\n return this.unsubscribe();\n }\n\n // ============================================================================\n // EVENT HANDLERS\n // ============================================================================\n\n /**\n * Handle model updated event.\n * Triggers sync to update embedded data in target models.\n */\n private handleModelUpdated = async (model: Model, changedFields: string[]): Promise<void> => {\n // Check if we should sync based on watched fields\n if (!this.shouldSync(changedFields)) {\n return;\n }\n\n try {\n const primaryKey = (this.config.sourceModel as any).primaryKey || \"id\";\n const sourceId = model.get(primaryKey) as string | number;\n\n if (!sourceId) {\n return;\n }\n\n // Get the driver from the source model's data source\n const dataSource = this.config.sourceModel.getDataSource();\n const driver = dataSource.driver;\n\n // Create sync manager and execute\n const syncManager = new SyncManager(this.config.sourceModel, driver);\n await syncManager.syncUpdateWithConfig(\n sourceId,\n model,\n changedFields,\n this.buildSyncConfig(),\n );\n } catch (error) {\n console.error(\n `[ModelSync] Failed to sync ${this.config.sourceModel.name} -> ${this.config.targetModel.name}:`,\n error,\n );\n }\n };\n\n /**\n * Handle model deleted event.\n * Triggers unset or remove based on configuration.\n */\n private handleModelDeleted = async (model: Model): Promise<void> => {\n if (!this.config.unsetOnDelete && !this.config.removeOnDelete) {\n return;\n }\n\n try {\n const primaryKey = (this.config.sourceModel as any).primaryKey || \"id\";\n const sourceId = model.get(primaryKey) as string | number;\n\n if (!sourceId) {\n return;\n }\n\n const dataSource = this.config.sourceModel.getDataSource();\n const driver = dataSource.driver;\n\n if (this.config.removeOnDelete) {\n // Delete all target documents that reference this source\n await this.removeTargetDocuments(sourceId, driver);\n } else if (this.config.unsetOnDelete) {\n // Unset the field in target documents\n const syncManager = new SyncManager(this.config.sourceModel, driver);\n await syncManager.syncDeleteWithConfig(sourceId, this.buildSyncConfig());\n }\n } catch (error) {\n console.error(\n `[ModelSync] Failed to handle delete for ${this.config.sourceModel.name}:`,\n error,\n );\n }\n };\n\n // ============================================================================\n // HELPERS\n // ============================================================================\n\n /**\n * Check if sync should proceed based on watched fields.\n */\n private shouldSync(changedFields: string[]): boolean {\n if (this.config.watchFields.length === 0) {\n return true; // Watch all fields\n }\n\n return this.config.watchFields.some((field) => changedFields.includes(field));\n }\n\n /**\n * Build sync config compatible with SyncManager.\n */\n private buildSyncConfig(): any {\n return {\n targetField: this.config.targetField,\n isMany: this.config.isMany,\n embedKey: this.config.embedKey,\n identifierField: this.config.identifierField,\n maxSyncDepth: this.config.maxSyncDepth,\n preventCircularSync: true,\n watchFields: this.config.watchFields,\n unsetOnDelete: this.config.unsetOnDelete,\n targetModelClass: this.config.targetModel,\n };\n }\n\n /**\n * Remove target documents that reference the deleted source.\n */\n private async removeTargetDocuments(sourceId: string | number, driver: any): Promise<void> {\n const filter = this.config.isMany\n ? { [`${this.config.targetField}.${this.config.identifierField}`]: sourceId }\n : { [`${this.config.targetField}.${this.config.identifierField}`]: sourceId };\n\n await driver.deleteMany(this.config.targetModel.table, filter);\n }\n\n /**\n * Get the current configuration (for debugging/testing).\n */\n public getConfig(): Readonly<ModelSyncConfig> {\n return { ...this.config };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"model-sync-operation.mjs","names":[],"sources":["../../../../../../../cascade/src/sync/model-sync-operation.ts"],"sourcesContent":["/**\n * Model sync operation class.\n *\n * Manages a single sync relationship between a source and target model.\n * Subscribes to source model events and triggers sync operations when\n * the source is updated or deleted.\n *\n * @module cascade-next/sync/model-sync-operation\n */\n\nimport events, { type EventSubscription } from \"@mongez/events\";\nimport type { ChildModel, Model } from \"../model/model\";\nimport { getModelDeletedEvent, getModelUpdatedEvent } from \"./model-events\";\nimport { DEFAULT_MAX_SYNC_DEPTH } from \"./sync-context\";\nimport { SyncManager } from \"./sync-manager\";\nimport type { ModelSyncConfig, ModelSyncOperationContract } from \"./types\";\n\n/**\n * Manages a single model sync operation.\n *\n * Subscribes to source model events (updated, deleted) and triggers\n * sync operations to update embedded data in target models.\n *\n * @example\n * ```typescript\n * // Created via modelSync.sync() or Model.sync()\n * const operation = new ModelSyncOperation(Category, Product, \"category\", false);\n * operation\n * .embed(\"embedMinimal\")\n * .watchFields([\"name\", \"slug\"])\n * .unsetOnDelete();\n * ```\n */\nexport class ModelSyncOperation implements ModelSyncOperationContract {\n /**\n * Configuration for this sync operation.\n */\n private readonly config: ModelSyncConfig;\n\n /**\n * Active event subscriptions for cleanup.\n */\n private readonly subscriptions: EventSubscription[] = [];\n\n /**\n * Whether this operation is currently subscribed to events.\n */\n private isSubscribed = false;\n\n /**\n * Create a new model sync operation.\n *\n * @param sourceModel - Source model class that triggers sync\n * @param targetModel - Target model class that receives data\n * @param targetField - Field path in target model\n * @param isMany - Whether this syncs to an array field\n */\n public constructor(\n sourceModelClass: ChildModel<Model>,\n targetModelClass: ChildModel<Model>,\n targetField: string,\n isMany: boolean,\n ) {\n this.config = {\n sourceModel: sourceModelClass,\n targetModel: targetModelClass,\n targetField,\n isMany,\n embedKey: \"embedData\",\n identifierField: \"id\",\n maxSyncDepth: DEFAULT_MAX_SYNC_DEPTH,\n watchFields: [],\n unsetOnDelete: false,\n removeOnDelete: false,\n };\n\n this.subscribe();\n }\n\n // ============================================================================\n // FLUENT CONFIGURATION\n // ============================================================================\n\n /**\n * Set the embed method to call on source model.\n *\n * @param embed - getter property name (e.g., \"embedData\", \"embedMinimal\") Or Array of fields\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Category.sync(Product, \"category\").embed(\"embedMinimal\");\n * ```\n */\n public embed(method: string | string[]): this {\n this.config.embedKey = method;\n return this;\n }\n\n /**\n * Set the identifier field for array matching.\n * Required when syncing to array fields (syncMany).\n *\n * @param field - Field name used as identifier (default: \"id\")\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Tag.syncMany(Post, \"tags\").identifyBy(\"tagId\");\n * ```\n */\n public identifyBy(field: string): this {\n this.config.identifierField = field;\n return this;\n }\n\n /**\n * Set the maximum sync depth for chained operations.\n *\n * @param depth - Maximum depth (default: 3)\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Category.sync(Product, \"category\").maxDepth(2);\n * ```\n */\n public maxDepth(depth: number): this {\n this.config.maxSyncDepth = depth;\n return this;\n }\n\n /**\n * Set which fields to watch for changes.\n * Sync only triggers when these fields change.\n *\n * @param fields - Array of field names to watch (empty = all)\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Category.sync(Product, \"category\").watchFields([\"name\", \"slug\"]);\n * ```\n */\n public watchFields(fields: string[]): this {\n this.config.watchFields = fields;\n return this;\n }\n\n /**\n * Unset the target field when source is deleted.\n *\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * Category.sync(Product, \"category\").unsetOnDelete();\n * ```\n */\n public unsetOnDelete(): this {\n this.config.unsetOnDelete = true;\n return this;\n }\n\n /**\n * Delete target documents when source is deleted.\n *\n * @returns This operation for chaining\n *\n * @example\n * ```typescript\n * User.sync(Profile, \"user\").removeOnDelete();\n * ```\n */\n public removeOnDelete(): this {\n this.config.removeOnDelete = true;\n return this;\n }\n\n // ============================================================================\n // LIFECYCLE\n // ============================================================================\n\n /**\n * Subscribe to source model events.\n * Called automatically in constructor.\n */\n private subscribe(): void {\n if (this.isSubscribed) {\n return;\n }\n\n const sourceModel = this.config.sourceModel;\n\n // Subscribe to model.updated event\n this.subscriptions.push(\n events.subscribe(getModelUpdatedEvent(sourceModel), this.handleModelUpdated),\n );\n\n // Subscribe to model.deleted event\n this.subscriptions.push(\n events.subscribe(getModelDeletedEvent(sourceModel), this.handleModelDeleted),\n );\n\n this.isSubscribed = true;\n }\n\n /**\n * Unsubscribe from all events and cleanup.\n * Called automatically when using modelSync.register().\n */\n public unsubscribe(): void {\n for (const subscription of this.subscriptions) {\n subscription.unsubscribe?.();\n }\n\n this.subscriptions.length = 0;\n this.isSubscribed = false;\n }\n\n /**\n * Cleanup the sync operations\n */\n public $cleanup() {\n return this.unsubscribe();\n }\n\n // ============================================================================\n // EVENT HANDLERS\n // ============================================================================\n\n /**\n * Handle model updated event.\n * Triggers sync to update embedded data in target models.\n */\n private handleModelUpdated = async (model: Model, changedFields: string[]): Promise<void> => {\n // Check if we should sync based on watched fields\n if (!this.shouldSync(changedFields)) {\n return;\n }\n\n try {\n const primaryKey = (this.config.sourceModel as any).primaryKey || \"id\";\n const sourceId = model.get(primaryKey) as string | number;\n\n if (!sourceId) {\n return;\n }\n\n // Get the driver from the source model's data source\n const dataSource = this.config.sourceModel.getDataSource();\n const driver = dataSource.driver;\n\n // Create sync manager and execute\n const syncManager = new SyncManager(this.config.sourceModel, driver);\n await syncManager.syncUpdateWithConfig(\n sourceId,\n model,\n changedFields,\n this.buildSyncConfig(),\n );\n } catch (error) {\n console.error(\n `[ModelSync] Failed to sync ${this.config.sourceModel.name} -> ${this.config.targetModel.name}:`,\n error,\n );\n }\n };\n\n /**\n * Handle model deleted event.\n * Triggers unset or remove based on configuration.\n */\n private handleModelDeleted = async (model: Model): Promise<void> => {\n if (!this.config.unsetOnDelete && !this.config.removeOnDelete) {\n return;\n }\n\n try {\n const primaryKey = (this.config.sourceModel as any).primaryKey || \"id\";\n const sourceId = model.get(primaryKey) as string | number;\n\n if (!sourceId) {\n return;\n }\n\n const dataSource = this.config.sourceModel.getDataSource();\n const driver = dataSource.driver;\n\n if (this.config.removeOnDelete) {\n // Delete all target documents that reference this source\n await this.removeTargetDocuments(sourceId, driver);\n } else if (this.config.unsetOnDelete) {\n // Unset the field in target documents\n const syncManager = new SyncManager(this.config.sourceModel, driver);\n await syncManager.syncDeleteWithConfig(sourceId, this.buildSyncConfig());\n }\n } catch (error) {\n console.error(\n `[ModelSync] Failed to handle delete for ${this.config.sourceModel.name}:`,\n error,\n );\n }\n };\n\n // ============================================================================\n // HELPERS\n // ============================================================================\n\n /**\n * Check if sync should proceed based on watched fields.\n */\n private shouldSync(changedFields: string[]): boolean {\n if (this.config.watchFields.length === 0) {\n return true; // Watch all fields\n }\n\n return this.config.watchFields.some((field) => changedFields.includes(field));\n }\n\n /**\n * Build sync config compatible with SyncManager.\n */\n private buildSyncConfig(): any {\n return {\n targetField: this.config.targetField,\n isMany: this.config.isMany,\n embedKey: this.config.embedKey,\n identifierField: this.config.identifierField,\n maxSyncDepth: this.config.maxSyncDepth,\n preventCircularSync: true,\n watchFields: this.config.watchFields,\n unsetOnDelete: this.config.unsetOnDelete,\n targetModelClass: this.config.targetModel,\n };\n }\n\n /**\n * Remove target documents that reference the deleted source.\n */\n private async removeTargetDocuments(sourceId: string | number, driver: any): Promise<void> {\n const filter = this.config.isMany\n ? { [`${this.config.targetField}.${this.config.identifierField}`]: sourceId }\n : { [`${this.config.targetField}.${this.config.identifierField}`]: sourceId };\n\n await driver.deleteMany(this.config.targetModel.table, filter);\n }\n\n /**\n * Get the current configuration (for debugging/testing).\n */\n public getConfig(): Readonly<ModelSyncConfig> {\n return { ...this.config };\n }\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { ChildModel } from "../model/model.types.mjs";
2
2
  import { ModelSyncContract, ModelSyncOperationContract } from "./types.mjs";
3
3
  import { Model } from "../model/model.mjs";
4
4
 
5
- //#region ../@warlock.js/cascade/src/sync/model-sync.d.ts
5
+ //#region ../cascade/src/sync/model-sync.d.ts
6
6
  /**
7
7
  * ModelSync facade implementation.
8
8
  *
@@ -1 +1 @@
1
- {"version":3,"file":"model-sync.d.mts","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/sync/model-sync.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;cA6BM,eAAA,YAA2B,iBAAA;EAuC7B;;;EAAA,iBAnCe,UAAA;EAqCf;;;;EAAA,iBA/Be,iBAAA;EAwDf;;;;;;;;;;;;AAkFoB;AA0BxB;;;;AAA8C;;;;;EAxIrC,IAAA,CACL,MAAA,EAAQ,UAAA,CAAW,KAAA,GACnB,MAAA,EAAQ,UAAA,CAAW,KAAA,GACnB,KAAA,WACC,0BAAA;;;;;;;;;;;;;;;;;;EAuBI,QAAA,CACL,MAAA,EAAQ,UAAA,CAAW,KAAA,GACnB,MAAA,EAAQ,UAAA,CAAW,KAAA,GACnB,KAAA,WACC,0BAAA;;;;;;;;;;;;;;;;;;;;;EA0BI,QAAA,CAAS,QAAA;;;;;EA+BT,KAAA;;;;;MAWI,KAAA;;;;UAWH,cAAA;AAAA;;;;;;;;;;;;;;;cA0BG,SAAA,EAAS,eAAwB"}
1
+ {"version":3,"file":"model-sync.d.mts","names":[],"sources":["../../../../../../../cascade/src/sync/model-sync.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;cA6BM,eAAA,YAA2B,iBAAA;EAuC7B;;;EAAA,iBAnCe,UAAA;EAqCf;;;;EAAA,iBA/Be,iBAAA;EAwDf;;;;;;;;;;;;AAkFoB;AA0BxB;;;;AAA8C;;;;;EAxIrC,IAAA,CACL,MAAA,EAAQ,UAAA,CAAW,KAAA,GACnB,MAAA,EAAQ,UAAA,CAAW,KAAA,GACnB,KAAA,WACC,0BAAA;;;;;;;;;;;;;;;;;;EAuBI,QAAA,CACL,MAAA,EAAQ,UAAA,CAAW,KAAA,GACnB,MAAA,EAAQ,UAAA,CAAW,KAAA,GACnB,KAAA,WACC,0BAAA;;;;;;;;;;;;;;;;;;;;;EA0BI,QAAA,CAAS,QAAA;;;;;EA+BT,KAAA;;;;;MAWI,KAAA;;;;UAWH,cAAA;AAAA;;;;;;;;;;;;;;;cA0BG,SAAA,EAAS,eAAwB"}
@@ -1,6 +1,6 @@
1
1
  import { ModelSyncOperation } from "./model-sync-operation.mjs";
2
2
 
3
- //#region ../@warlock.js/cascade/src/sync/model-sync.ts
3
+ //#region ../cascade/src/sync/model-sync.ts
4
4
  /**
5
5
  * ModelSync facade implementation.
6
6
  *
@@ -1 +1 @@
1
- {"version":3,"file":"model-sync.mjs","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/sync/model-sync.ts"],"sourcesContent":["/**\n * ModelSync facade for registering sync operations.\n *\n * Provides a clean API for defining sync relationships between models\n * with automatic cleanup support for HMR (Hot Module Replacement).\n *\n * @module cascade-next/sync/model-sync\n */\n\nimport type { ChildModel, Model } from \"../model/model\";\nimport { ModelSyncOperation } from \"./model-sync-operation\";\nimport type { ModelSyncContract, ModelSyncOperationContract } from \"./types\";\n\n/**\n * ModelSync facade implementation.\n *\n * Manages sync operation registration with scoped cleanup support.\n * Uses a registration stack to track operations created during\n * a `register()` callback for proper HMR cleanup.\n *\n * @example\n * ```typescript\n * // In events file\n * export const cleanup = modelSync.register(() => {\n * Category.sync(Product, \"category\");\n * Tag.syncMany(Post, \"tags\").identifyBy(\"id\");\n * });\n * ```\n */\nclass ModelSyncFacade implements ModelSyncContract {\n /**\n * All active sync operations.\n */\n private readonly operations: ModelSyncOperation[] = [];\n\n /**\n * Stack for tracking operations during register() callbacks.\n * Each element is an array of operations created in that scope.\n */\n private readonly registrationStack: ModelSyncOperation[][] = [];\n\n // ============================================================================\n // PUBLIC API\n // ============================================================================\n\n /**\n * Create a sync operation for a single embedded document.\n *\n * When the source model is updated, the target model's field\n * will be updated with the embedded data.\n *\n * @param source - Source model class that triggers sync\n * @param target - Target model class that receives data\n * @param field - Field path in target model\n * @returns Sync operation for chaining configuration\n *\n * @example\n * ```typescript\n * // When Category updates, update Product.category\n * modelSync.sync(Category, Product, \"category\");\n *\n * // With configuration\n * modelSync.sync(Category, Product, \"category\")\n * .embed(\"embedMinimal\")\n * .watchFields([\"name\", \"slug\"]);\n * ```\n */\n public sync(\n source: ChildModel<Model>,\n target: ChildModel<Model>,\n field: string,\n ): ModelSyncOperationContract {\n const operation = new ModelSyncOperation(source, target, field, false);\n this.trackOperation(operation);\n return operation;\n }\n\n /**\n * Create a sync operation for an array of embedded documents.\n *\n * When the source model is updated, the corresponding element\n * in the target model's array field will be updated.\n *\n * @param source - Source model class that triggers sync\n * @param target - Target model class that receives data\n * @param field - Array field path in target model\n * @returns Sync operation for chaining configuration\n *\n * @example\n * ```typescript\n * // When Tag updates, update Post.tags[i] where tags[i].id matches\n * modelSync.syncMany(Tag, Post, \"tags\").identifyBy(\"id\");\n * ```\n */\n public syncMany(\n source: ChildModel<Model>,\n target: ChildModel<Model>,\n field: string,\n ): ModelSyncOperationContract {\n const operation = new ModelSyncOperation(source, target, field, true);\n this.trackOperation(operation);\n return operation;\n }\n\n /**\n * Register sync operations with automatic cleanup.\n *\n * Executes the callback function which should contain sync registrations.\n * Returns a cleanup function that unsubscribes all operations created\n * during the callback - perfect for HMR module cleanup.\n *\n * @param callback - Function that registers sync operations\n * @returns Cleanup function that unsubscribes all registered operations\n *\n * @example\n * ```typescript\n * // In src/app/blog/events/sync.ts\n * export const cleanup = modelSync.register(() => {\n * Category.sync(Product, \"category\");\n * Tag.syncMany(Post, \"tags\").identifyBy(\"id\");\n * Author.sync(Article, \"author\").unsetOnDelete();\n * });\n * ```\n */\n public register(callback: () => void): () => void {\n // Create a new scope for tracking operations\n const scopedOperations: ModelSyncOperation[] = [];\n this.registrationStack.push(scopedOperations);\n\n try {\n // Execute callback - operations are tracked via trackOperation()\n callback();\n } finally {\n // Remove the scope from stack\n this.registrationStack.pop();\n }\n\n // Return cleanup function for just these scoped operations\n return () => {\n for (const operation of scopedOperations) {\n operation.unsubscribe();\n\n // Remove from global operations array\n const index = this.operations.indexOf(operation);\n if (index !== -1) {\n this.operations.splice(index, 1);\n }\n }\n };\n }\n\n /**\n * Clear all registered sync operations.\n * Useful for testing or complete reset.\n */\n public clear(): void {\n for (const operation of this.operations) {\n operation.unsubscribe();\n }\n this.operations.length = 0;\n }\n\n /**\n * Get count of active sync operations.\n * Useful for debugging and testing.\n */\n public get count(): number {\n return this.operations.length;\n }\n\n // ============================================================================\n // PRIVATE HELPERS\n // ============================================================================\n\n /**\n * Track a new operation in global list and current registration scope.\n */\n private trackOperation(operation: ModelSyncOperation): void {\n // Always add to global operations\n this.operations.push(operation);\n\n // If inside a register() callback, also track in the current scope\n const currentScope = this.registrationStack[this.registrationStack.length - 1];\n if (currentScope) {\n currentScope.push(operation);\n }\n }\n}\n\n/**\n * Global modelSync facade instance.\n *\n * Use this to register sync operations between models.\n *\n * @example\n * ```typescript\n * import { modelSync } from \"@warlock.js/cascade\";\n *\n * export const cleanup = modelSync.register(() => {\n * Category.sync(Product, \"category\");\n * });\n * ```\n */\nexport const modelSync = new ModelSyncFacade();\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6BA,IAAM,kBAAN,MAAmD;;;;CAIjD,AAAiB,aAAmC,CAAC;;;;;CAMrD,AAAiB,oBAA4C,CAAC;;;;;;;;;;;;;;;;;;;;;;;CA4B9D,AAAO,KACL,QACA,QACA,OAC4B;EAC5B,MAAM,YAAY,IAAI,mBAAmB,QAAQ,QAAQ,OAAO,KAAK;EACrE,KAAK,eAAe,SAAS;EAC7B,OAAO;CACT;;;;;;;;;;;;;;;;;;CAmBA,AAAO,SACL,QACA,QACA,OAC4B;EAC5B,MAAM,YAAY,IAAI,mBAAmB,QAAQ,QAAQ,OAAO,IAAI;EACpE,KAAK,eAAe,SAAS;EAC7B,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;CAsBA,AAAO,SAAS,UAAkC;EAEhD,MAAM,mBAAyC,CAAC;EAChD,KAAK,kBAAkB,KAAK,gBAAgB;EAE5C,IAAI;GAEF,SAAS;EACX,UAAU;GAER,KAAK,kBAAkB,IAAI;EAC7B;EAGA,aAAa;GACX,KAAK,MAAM,aAAa,kBAAkB;IACxC,UAAU,YAAY;IAGtB,MAAM,QAAQ,KAAK,WAAW,QAAQ,SAAS;IAC/C,IAAI,UAAU,IACZ,KAAK,WAAW,OAAO,OAAO,CAAC;GAEnC;EACF;CACF;;;;;CAMA,AAAO,QAAc;EACnB,KAAK,MAAM,aAAa,KAAK,YAC3B,UAAU,YAAY;EAExB,KAAK,WAAW,SAAS;CAC3B;;;;;CAMA,IAAW,QAAgB;EACzB,OAAO,KAAK,WAAW;CACzB;;;;CASA,AAAQ,eAAe,WAAqC;EAE1D,KAAK,WAAW,KAAK,SAAS;EAG9B,MAAM,eAAe,KAAK,kBAAkB,KAAK,kBAAkB,SAAS;EAC5E,IAAI,cACF,aAAa,KAAK,SAAS;CAE/B;AACF;;;;;;;;;;;;;;;AAgBA,MAAa,YAAY,IAAI,gBAAgB"}
1
+ {"version":3,"file":"model-sync.mjs","names":[],"sources":["../../../../../../../cascade/src/sync/model-sync.ts"],"sourcesContent":["/**\n * ModelSync facade for registering sync operations.\n *\n * Provides a clean API for defining sync relationships between models\n * with automatic cleanup support for HMR (Hot Module Replacement).\n *\n * @module cascade-next/sync/model-sync\n */\n\nimport type { ChildModel, Model } from \"../model/model\";\nimport { ModelSyncOperation } from \"./model-sync-operation\";\nimport type { ModelSyncContract, ModelSyncOperationContract } from \"./types\";\n\n/**\n * ModelSync facade implementation.\n *\n * Manages sync operation registration with scoped cleanup support.\n * Uses a registration stack to track operations created during\n * a `register()` callback for proper HMR cleanup.\n *\n * @example\n * ```typescript\n * // In events file\n * export const cleanup = modelSync.register(() => {\n * Category.sync(Product, \"category\");\n * Tag.syncMany(Post, \"tags\").identifyBy(\"id\");\n * });\n * ```\n */\nclass ModelSyncFacade implements ModelSyncContract {\n /**\n * All active sync operations.\n */\n private readonly operations: ModelSyncOperation[] = [];\n\n /**\n * Stack for tracking operations during register() callbacks.\n * Each element is an array of operations created in that scope.\n */\n private readonly registrationStack: ModelSyncOperation[][] = [];\n\n // ============================================================================\n // PUBLIC API\n // ============================================================================\n\n /**\n * Create a sync operation for a single embedded document.\n *\n * When the source model is updated, the target model's field\n * will be updated with the embedded data.\n *\n * @param source - Source model class that triggers sync\n * @param target - Target model class that receives data\n * @param field - Field path in target model\n * @returns Sync operation for chaining configuration\n *\n * @example\n * ```typescript\n * // When Category updates, update Product.category\n * modelSync.sync(Category, Product, \"category\");\n *\n * // With configuration\n * modelSync.sync(Category, Product, \"category\")\n * .embed(\"embedMinimal\")\n * .watchFields([\"name\", \"slug\"]);\n * ```\n */\n public sync(\n source: ChildModel<Model>,\n target: ChildModel<Model>,\n field: string,\n ): ModelSyncOperationContract {\n const operation = new ModelSyncOperation(source, target, field, false);\n this.trackOperation(operation);\n return operation;\n }\n\n /**\n * Create a sync operation for an array of embedded documents.\n *\n * When the source model is updated, the corresponding element\n * in the target model's array field will be updated.\n *\n * @param source - Source model class that triggers sync\n * @param target - Target model class that receives data\n * @param field - Array field path in target model\n * @returns Sync operation for chaining configuration\n *\n * @example\n * ```typescript\n * // When Tag updates, update Post.tags[i] where tags[i].id matches\n * modelSync.syncMany(Tag, Post, \"tags\").identifyBy(\"id\");\n * ```\n */\n public syncMany(\n source: ChildModel<Model>,\n target: ChildModel<Model>,\n field: string,\n ): ModelSyncOperationContract {\n const operation = new ModelSyncOperation(source, target, field, true);\n this.trackOperation(operation);\n return operation;\n }\n\n /**\n * Register sync operations with automatic cleanup.\n *\n * Executes the callback function which should contain sync registrations.\n * Returns a cleanup function that unsubscribes all operations created\n * during the callback - perfect for HMR module cleanup.\n *\n * @param callback - Function that registers sync operations\n * @returns Cleanup function that unsubscribes all registered operations\n *\n * @example\n * ```typescript\n * // In src/app/blog/events/sync.ts\n * export const cleanup = modelSync.register(() => {\n * Category.sync(Product, \"category\");\n * Tag.syncMany(Post, \"tags\").identifyBy(\"id\");\n * Author.sync(Article, \"author\").unsetOnDelete();\n * });\n * ```\n */\n public register(callback: () => void): () => void {\n // Create a new scope for tracking operations\n const scopedOperations: ModelSyncOperation[] = [];\n this.registrationStack.push(scopedOperations);\n\n try {\n // Execute callback - operations are tracked via trackOperation()\n callback();\n } finally {\n // Remove the scope from stack\n this.registrationStack.pop();\n }\n\n // Return cleanup function for just these scoped operations\n return () => {\n for (const operation of scopedOperations) {\n operation.unsubscribe();\n\n // Remove from global operations array\n const index = this.operations.indexOf(operation);\n if (index !== -1) {\n this.operations.splice(index, 1);\n }\n }\n };\n }\n\n /**\n * Clear all registered sync operations.\n * Useful for testing or complete reset.\n */\n public clear(): void {\n for (const operation of this.operations) {\n operation.unsubscribe();\n }\n this.operations.length = 0;\n }\n\n /**\n * Get count of active sync operations.\n * Useful for debugging and testing.\n */\n public get count(): number {\n return this.operations.length;\n }\n\n // ============================================================================\n // PRIVATE HELPERS\n // ============================================================================\n\n /**\n * Track a new operation in global list and current registration scope.\n */\n private trackOperation(operation: ModelSyncOperation): void {\n // Always add to global operations\n this.operations.push(operation);\n\n // If inside a register() callback, also track in the current scope\n const currentScope = this.registrationStack[this.registrationStack.length - 1];\n if (currentScope) {\n currentScope.push(operation);\n }\n }\n}\n\n/**\n * Global modelSync facade instance.\n *\n * Use this to register sync operations between models.\n *\n * @example\n * ```typescript\n * import { modelSync } from \"@warlock.js/cascade\";\n *\n * export const cleanup = modelSync.register(() => {\n * Category.sync(Product, \"category\");\n * });\n * ```\n */\nexport const modelSync = new ModelSyncFacade();\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6BA,IAAM,kBAAN,MAAmD;;;;CAIjD,AAAiB,aAAmC,CAAC;;;;;CAMrD,AAAiB,oBAA4C,CAAC;;;;;;;;;;;;;;;;;;;;;;;CA4B9D,AAAO,KACL,QACA,QACA,OAC4B;EAC5B,MAAM,YAAY,IAAI,mBAAmB,QAAQ,QAAQ,OAAO,KAAK;EACrE,KAAK,eAAe,SAAS;EAC7B,OAAO;CACT;;;;;;;;;;;;;;;;;;CAmBA,AAAO,SACL,QACA,QACA,OAC4B;EAC5B,MAAM,YAAY,IAAI,mBAAmB,QAAQ,QAAQ,OAAO,IAAI;EACpE,KAAK,eAAe,SAAS;EAC7B,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;CAsBA,AAAO,SAAS,UAAkC;EAEhD,MAAM,mBAAyC,CAAC;EAChD,KAAK,kBAAkB,KAAK,gBAAgB;EAE5C,IAAI;GAEF,SAAS;EACX,UAAU;GAER,KAAK,kBAAkB,IAAI;EAC7B;EAGA,aAAa;GACX,KAAK,MAAM,aAAa,kBAAkB;IACxC,UAAU,YAAY;IAGtB,MAAM,QAAQ,KAAK,WAAW,QAAQ,SAAS;IAC/C,IAAI,UAAU,IACZ,KAAK,WAAW,OAAO,OAAO,CAAC;GAEnC;EACF;CACF;;;;;CAMA,AAAO,QAAc;EACnB,KAAK,MAAM,aAAa,KAAK,YAC3B,UAAU,YAAY;EAExB,KAAK,WAAW,SAAS;CAC3B;;;;;CAMA,IAAW,QAAgB;EACzB,OAAO,KAAK,WAAW;CACzB;;;;CASA,AAAQ,eAAe,WAAqC;EAE1D,KAAK,WAAW,KAAK,SAAS;EAG9B,MAAM,eAAe,KAAK,kBAAkB,KAAK,kBAAkB,SAAS;EAC5E,IAAI,cACF,aAAa,KAAK,SAAS;CAE/B;AACF;;;;;;;;;;;;;;;AAgBA,MAAa,YAAY,IAAI,gBAAgB"}
@@ -1,6 +1,6 @@
1
1
  import { SyncContext, SyncInstruction } from "./types.mjs";
2
2
 
3
- //#region ../@warlock.js/cascade/src/sync/sync-context.d.ts
3
+ //#region ../cascade/src/sync/sync-context.d.ts
4
4
  /**
5
5
  * Default maximum sync depth.
6
6
  * Prevents infinite sync chains.
@@ -1 +1 @@
1
- {"version":3,"file":"sync-context.d.mts","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/sync/sync-context.ts"],"mappings":";;;;AAYmC;AAKnC;;cALa,sBAAA;;;;cAKA,kBAAA;EAUT;;;;;;;EAAA,OAFY,aAAA,CACZ,WAAA,EAAa,eAAA,EACb,aAAA,WACC,WAAW;EA8BT;;;;;;;;;;EAAA,OANS,QAAA,CACZ,KAAA,UACA,KAAA,YACA,WAAA,UACA,QAAA,UACA,eAAA;IACG,KAAA;IAAgB,KAAA;EAAA;EA6DH;;;;;;;EAAA,OAlCJ,QAAA,CAAS,KAAA,YAAiB,WAAA;;;;;;;;SAW1B,WAAA,CAAY,KAAA,YAAiB,SAAA;;;;;;;SAU7B,WAAA,CAAY,KAAA;;;;;;;;SAWZ,aAAA,CACZ,YAAA,UACA,QAAA;AAAA"}
1
+ {"version":3,"file":"sync-context.d.mts","names":[],"sources":["../../../../../../../cascade/src/sync/sync-context.ts"],"mappings":";;;;AAYmC;AAKnC;;cALa,sBAAA;;;;cAKA,kBAAA;EAUT;;;;;;;EAAA,OAFY,aAAA,CACZ,WAAA,EAAa,eAAA,EACb,aAAA,WACC,WAAW;EA8BT;;;;;;;;;;EAAA,OANS,QAAA,CACZ,KAAA,UACA,KAAA,YACA,WAAA,UACA,QAAA,UACA,eAAA;IACG,KAAA;IAAgB,KAAA;EAAA;EA6DH;;;;;;;EAAA,OAlCJ,QAAA,CAAS,KAAA,YAAiB,WAAA;;;;;;;;SAW1B,WAAA,CAAY,KAAA,YAAiB,SAAA;;;;;;;SAU7B,WAAA,CAAY,KAAA;;;;;;;;SAWZ,aAAA,CACZ,YAAA,UACA,QAAA;AAAA"}
@@ -1,4 +1,4 @@
1
- //#region ../@warlock.js/cascade/src/sync/sync-context.ts
1
+ //#region ../cascade/src/sync/sync-context.ts
2
2
  /**
3
3
  * Default maximum sync depth.
4
4
  * Prevents infinite sync chains.
@@ -1 +1 @@
1
- {"version":3,"file":"sync-context.mjs","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/sync/sync-context.ts"],"sourcesContent":["/**\n * Sync context management and validation.\n *\n * @module cascade-next/sync/sync-context\n */\n\nimport type { SyncContext, SyncInstruction } from \"./types\";\n\n/**\n * Default maximum sync depth.\n * Prevents infinite sync chains.\n */\nexport const DEFAULT_MAX_SYNC_DEPTH = 3;\n\n/**\n * Manages sync context and provides validation utilities.\n */\nexport class SyncContextManager {\n /**\n * Creates a new sync context from a sync instruction.\n *\n * @param instruction - The sync instruction to create context from\n * @param affectedCount - Number of documents affected\n * @returns A new sync context\n */\n public static createContext(\n instruction: SyncInstruction,\n affectedCount: number,\n ): SyncContext {\n return {\n sourceModel: instruction.sourceModel,\n sourceId: instruction.sourceId,\n currentDepth: instruction.depth,\n syncChain: [...instruction.chain],\n targetModel: instruction.targetModel,\n filter: { ...instruction.filter },\n update: { ...instruction.update },\n affectedCount,\n timestamp: new Date(),\n };\n }\n\n /**\n * Validates if a sync operation can proceed based on depth and cycle detection.\n *\n * @param depth - Current sync depth\n * @param chain - Current sync chain\n * @param targetModel - Target model name\n * @param maxDepth - Maximum allowed depth\n * @param preventCircular - Whether to prevent circular references\n * @returns Validation result with success flag and optional error message\n */\n public static validate(\n depth: number,\n chain: string[],\n targetModel: string,\n maxDepth: number,\n preventCircular: boolean,\n ): { valid: boolean; error?: string } {\n // Check depth limit\n if (depth > maxDepth) {\n return {\n valid: false,\n error: `Sync depth limit exceeded: ${depth} > ${maxDepth}. Chain: ${chain.join(\" → \")}`,\n };\n }\n\n // Check for circular references\n if (preventCircular && this.hasCycle(chain, targetModel)) {\n return {\n valid: false,\n error: `Circular sync detected: ${targetModel} already exists in chain [${chain.join(\" → \")}]`,\n };\n }\n\n return { valid: true };\n }\n\n /**\n * Checks if adding a target model would create a cycle in the sync chain.\n *\n * @param chain - Current sync chain\n * @param targetModel - Model to be added to the chain\n * @returns True if adding the model would create a cycle\n */\n public static hasCycle(chain: string[], targetModel: string): boolean {\n return chain.includes(targetModel);\n }\n\n /**\n * Creates a new sync chain by appending a model name.\n *\n * @param chain - Current sync chain\n * @param modelName - Model name to append\n * @returns New sync chain array\n */\n public static extendChain(chain: string[], modelName: string): string[] {\n return [...chain, modelName];\n }\n\n /**\n * Formats a sync chain for display.\n *\n * @param chain - Sync chain to format\n * @returns Formatted string (e.g., \"Category → Product → Module\")\n */\n public static formatChain(chain: string[]): string {\n return chain.join(\" → \");\n }\n\n /**\n * Checks if the current depth allows for further syncing.\n *\n * @param currentDepth - Current depth in the chain\n * @param maxDepth - Maximum allowed depth\n * @returns True if more syncing is allowed\n */\n public static canSyncDeeper(\n currentDepth: number,\n maxDepth: number,\n ): boolean {\n return currentDepth < maxDepth;\n }\n}\n\n"],"mappings":";;;;;AAYA,MAAa,yBAAyB;;;;AAKtC,IAAa,qBAAb,MAAgC;;;;;;;;CAQ9B,OAAc,cACZ,aACA,eACa;EACb,OAAO;GACL,aAAa,YAAY;GACzB,UAAU,YAAY;GACtB,cAAc,YAAY;GAC1B,WAAW,CAAC,GAAG,YAAY,KAAK;GAChC,aAAa,YAAY;GACzB,QAAQ,EAAE,GAAG,YAAY,OAAO;GAChC,QAAQ,EAAE,GAAG,YAAY,OAAO;GAChC;GACA,2BAAW,IAAI,KAAK;EACtB;CACF;;;;;;;;;;;CAYA,OAAc,SACZ,OACA,OACA,aACA,UACA,iBACoC;EAEpC,IAAI,QAAQ,UACV,OAAO;GACL,OAAO;GACP,OAAO,8BAA8B,MAAM,KAAK,SAAS,WAAW,MAAM,KAAK,KAAK;EACtF;EAIF,IAAI,mBAAmB,KAAK,SAAS,OAAO,WAAW,GACrD,OAAO;GACL,OAAO;GACP,OAAO,2BAA2B,YAAY,4BAA4B,MAAM,KAAK,KAAK,EAAE;EAC9F;EAGF,OAAO,EAAE,OAAO,KAAK;CACvB;;;;;;;;CASA,OAAc,SAAS,OAAiB,aAA8B;EACpE,OAAO,MAAM,SAAS,WAAW;CACnC;;;;;;;;CASA,OAAc,YAAY,OAAiB,WAA6B;EACtE,OAAO,CAAC,GAAG,OAAO,SAAS;CAC7B;;;;;;;CAQA,OAAc,YAAY,OAAyB;EACjD,OAAO,MAAM,KAAK,KAAK;CACzB;;;;;;;;CASA,OAAc,cACZ,cACA,UACS;EACT,OAAO,eAAe;CACxB;AACF"}
1
+ {"version":3,"file":"sync-context.mjs","names":[],"sources":["../../../../../../../cascade/src/sync/sync-context.ts"],"sourcesContent":["/**\n * Sync context management and validation.\n *\n * @module cascade-next/sync/sync-context\n */\n\nimport type { SyncContext, SyncInstruction } from \"./types\";\n\n/**\n * Default maximum sync depth.\n * Prevents infinite sync chains.\n */\nexport const DEFAULT_MAX_SYNC_DEPTH = 3;\n\n/**\n * Manages sync context and provides validation utilities.\n */\nexport class SyncContextManager {\n /**\n * Creates a new sync context from a sync instruction.\n *\n * @param instruction - The sync instruction to create context from\n * @param affectedCount - Number of documents affected\n * @returns A new sync context\n */\n public static createContext(\n instruction: SyncInstruction,\n affectedCount: number,\n ): SyncContext {\n return {\n sourceModel: instruction.sourceModel,\n sourceId: instruction.sourceId,\n currentDepth: instruction.depth,\n syncChain: [...instruction.chain],\n targetModel: instruction.targetModel,\n filter: { ...instruction.filter },\n update: { ...instruction.update },\n affectedCount,\n timestamp: new Date(),\n };\n }\n\n /**\n * Validates if a sync operation can proceed based on depth and cycle detection.\n *\n * @param depth - Current sync depth\n * @param chain - Current sync chain\n * @param targetModel - Target model name\n * @param maxDepth - Maximum allowed depth\n * @param preventCircular - Whether to prevent circular references\n * @returns Validation result with success flag and optional error message\n */\n public static validate(\n depth: number,\n chain: string[],\n targetModel: string,\n maxDepth: number,\n preventCircular: boolean,\n ): { valid: boolean; error?: string } {\n // Check depth limit\n if (depth > maxDepth) {\n return {\n valid: false,\n error: `Sync depth limit exceeded: ${depth} > ${maxDepth}. Chain: ${chain.join(\" → \")}`,\n };\n }\n\n // Check for circular references\n if (preventCircular && this.hasCycle(chain, targetModel)) {\n return {\n valid: false,\n error: `Circular sync detected: ${targetModel} already exists in chain [${chain.join(\" → \")}]`,\n };\n }\n\n return { valid: true };\n }\n\n /**\n * Checks if adding a target model would create a cycle in the sync chain.\n *\n * @param chain - Current sync chain\n * @param targetModel - Model to be added to the chain\n * @returns True if adding the model would create a cycle\n */\n public static hasCycle(chain: string[], targetModel: string): boolean {\n return chain.includes(targetModel);\n }\n\n /**\n * Creates a new sync chain by appending a model name.\n *\n * @param chain - Current sync chain\n * @param modelName - Model name to append\n * @returns New sync chain array\n */\n public static extendChain(chain: string[], modelName: string): string[] {\n return [...chain, modelName];\n }\n\n /**\n * Formats a sync chain for display.\n *\n * @param chain - Sync chain to format\n * @returns Formatted string (e.g., \"Category → Product → Module\")\n */\n public static formatChain(chain: string[]): string {\n return chain.join(\" → \");\n }\n\n /**\n * Checks if the current depth allows for further syncing.\n *\n * @param currentDepth - Current depth in the chain\n * @param maxDepth - Maximum allowed depth\n * @returns True if more syncing is allowed\n */\n public static canSyncDeeper(\n currentDepth: number,\n maxDepth: number,\n ): boolean {\n return currentDepth < maxDepth;\n }\n}\n\n"],"mappings":";;;;;AAYA,MAAa,yBAAyB;;;;AAKtC,IAAa,qBAAb,MAAgC;;;;;;;;CAQ9B,OAAc,cACZ,aACA,eACa;EACb,OAAO;GACL,aAAa,YAAY;GACzB,UAAU,YAAY;GACtB,cAAc,YAAY;GAC1B,WAAW,CAAC,GAAG,YAAY,KAAK;GAChC,aAAa,YAAY;GACzB,QAAQ,EAAE,GAAG,YAAY,OAAO;GAChC,QAAQ,EAAE,GAAG,YAAY,OAAO;GAChC;GACA,2BAAW,IAAI,KAAK;EACtB;CACF;;;;;;;;;;;CAYA,OAAc,SACZ,OACA,OACA,aACA,UACA,iBACoC;EAEpC,IAAI,QAAQ,UACV,OAAO;GACL,OAAO;GACP,OAAO,8BAA8B,MAAM,KAAK,SAAS,WAAW,MAAM,KAAK,KAAK;EACtF;EAIF,IAAI,mBAAmB,KAAK,SAAS,OAAO,WAAW,GACrD,OAAO;GACL,OAAO;GACP,OAAO,2BAA2B,YAAY,4BAA4B,MAAM,KAAK,KAAK,EAAE;EAC9F;EAGF,OAAO,EAAE,OAAO,KAAK;CACvB;;;;;;;;CASA,OAAc,SAAS,OAAiB,aAA8B;EACpE,OAAO,MAAM,SAAS,WAAW;CACnC;;;;;;;;CASA,OAAc,YAAY,OAAiB,WAA6B;EACtE,OAAO,CAAC,GAAG,OAAO,SAAS;CAC7B;;;;;;;CAQA,OAAc,YAAY,OAAyB;EACjD,OAAO,MAAM,KAAK,KAAK;CACzB;;;;;;;;CASA,OAAc,cACZ,cACA,UACS;EACT,OAAO,eAAe;CACxB;AACF"}
@@ -3,7 +3,7 @@ import { SyncConfig, SyncResult } from "./types.mjs";
3
3
  import { Model } from "../model/model.mjs";
4
4
  import { DriverContract } from "../contracts/database-driver.contract.mjs";
5
5
 
6
- //#region ../@warlock.js/cascade/src/sync/sync-manager.d.ts
6
+ //#region ../cascade/src/sync/sync-manager.d.ts
7
7
  /**
8
8
  * Manages sync operations across models with multi-level support.
9
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"sync-manager.d.mts","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/sync/sync-manager.ts"],"mappings":";;;;;;;;;cAqBa,WAAA;EAAA,iBACM,WAAA;EAAA,iBACA,MAAA;EAyBN;;;;;;cAjBQ,WAAA,EAAa,UAAA,CAAW,KAAA,GAAQ,MAAA,EAAQ,cAAA;EAkJjD;;;;;;;;EArIG,UAAA,CACX,QAAA,mBACA,WAAA,EAAa,MAAA,oBAA0B,KAAA,EACvC,aAAA,aACC,OAAA,CAAQ,UAAA;;;;;;;;;;;EAgEE,oBAAA,CACX,QAAA,mBACA,WAAA,EAAa,MAAA,oBAA0B,KAAA,EACvC,aAAA,YACA,MAAA,EAAQ,UAAA,GACP,OAAA,CAAQ,UAAA;EAtET;;;;;;;;EAgIW,oBAAA,CACX,QAAA,mBACA,MAAA,EAAQ,UAAA,GACP,OAAA,CAAQ,UAAA;EA9DD;;;;;;EAoHG,UAAA,CAAW,QAAA,oBAA4B,OAAA,CAAQ,UAAA;EAvD1D;;;;;;EAAA,QA6GY,mBAAA;EAAA;;;;;;;;EAAA,QA6DA,yBAAA;EAyQA;;;;;;;;;;EAAA,QA9NA,4BAAA;EAgdW;AAAA;;;;;;;;EAAA,QAtajB,sBAAA;;;;;;;;;UAwCA,sBAAA;;;;;;;;UA4BA,WAAA;;;;;;;;UAiBA,WAAA;;;;;;;;UAoBM,mBAAA;;;;;;;UAgDA,YAAA;;;;;;;;UA2BA,iBAAA;;;;;;;;UAuDN,eAAA;;;;;;;UAmBA,YAAA;;;;;;;UAqBA,YAAA;;;;;;;;UAqBA,UAAA;;;;;;;;UAeM,YAAA;;;;;;UA8BN,cAAA;;;;;;;UAkBA,sBAAA;;;;;;UAiBM,gBAAA;;;;;;UAuBA,eAAA;;;;;;UAuBN,iBAAA;AAAA"}
1
+ {"version":3,"file":"sync-manager.d.mts","names":[],"sources":["../../../../../../../cascade/src/sync/sync-manager.ts"],"mappings":";;;;;;;;;cAqBa,WAAA;EAAA,iBACM,WAAA;EAAA,iBACA,MAAA;EAyBN;;;;;;cAjBQ,WAAA,EAAa,UAAA,CAAW,KAAA,GAAQ,MAAA,EAAQ,cAAA;EAkJjD;;;;;;;;EArIG,UAAA,CACX,QAAA,mBACA,WAAA,EAAa,MAAA,oBAA0B,KAAA,EACvC,aAAA,aACC,OAAA,CAAQ,UAAA;;;;;;;;;;;EAgEE,oBAAA,CACX,QAAA,mBACA,WAAA,EAAa,MAAA,oBAA0B,KAAA,EACvC,aAAA,YACA,MAAA,EAAQ,UAAA,GACP,OAAA,CAAQ,UAAA;EAtET;;;;;;;;EAgIW,oBAAA,CACX,QAAA,mBACA,MAAA,EAAQ,UAAA,GACP,OAAA,CAAQ,UAAA;EA9DD;;;;;;EAoHG,UAAA,CAAW,QAAA,oBAA4B,OAAA,CAAQ,UAAA;EAvD1D;;;;;;EAAA,QA6GY,mBAAA;EAAA;;;;;;;;EAAA,QA6DA,yBAAA;EAyQA;;;;;;;;;;EAAA,QA9NA,4BAAA;EAgdW;AAAA;;;;;;;;EAAA,QAtajB,sBAAA;;;;;;;;;UAwCA,sBAAA;;;;;;;;UA4BA,WAAA;;;;;;;;UAiBA,WAAA;;;;;;;;UAoBM,mBAAA;;;;;;;UAgDA,YAAA;;;;;;;;UA2BA,iBAAA;;;;;;;;UAuDN,eAAA;;;;;;;UAmBA,YAAA;;;;;;;UAqBA,YAAA;;;;;;;;UAqBA,UAAA;;;;;;;;UAeM,YAAA;;;;;;UA8BN,cAAA;;;;;;;UAkBA,sBAAA;;;;;;UAiBM,gBAAA;;;;;;UAuBA,eAAA;;;;;;UAuBN,iBAAA;AAAA"}
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_MAX_SYNC_DEPTH, SyncContextManager } from "./sync-context.mjs";
2
2
  import { Model } from "../model/model.mjs";
3
3
 
4
- //#region ../@warlock.js/cascade/src/sync/sync-manager.ts
4
+ //#region ../cascade/src/sync/sync-manager.ts
5
5
  /**
6
6
  * Manages sync operations across models with multi-level support.
7
7
  */
@@ -1 +1 @@
1
- {"version":3,"file":"sync-manager.mjs","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/sync/sync-manager.ts"],"sourcesContent":["/**\n * Sync manager service for handling multi-level sync operations.\n *\n * @module cascade-next/sync/sync-manager\n */\n\nimport type { DriverContract } from \"../contracts/database-driver.contract\";\nimport { ChildModel, Model } from \"../model/model\";\nimport { DEFAULT_MAX_SYNC_DEPTH, SyncContextManager } from \"./sync-context\";\nimport type {\n CollectInstructionsPayload,\n SyncConfig,\n SyncEventPayload,\n SyncInstruction,\n SyncInstructionOptions,\n SyncResult,\n} from \"./types\";\n\n/**\n * Manages sync operations across models with multi-level support.\n */\nexport class SyncManager {\n private readonly sourceModel: ChildModel<Model>;\n private readonly driver: DriverContract;\n\n /**\n * Creates a new sync manager.\n *\n * @param sourceModel - The source model class\n * @param driver - The database driver\n */\n public constructor(sourceModel: ChildModel<Model>, driver: DriverContract) {\n this.sourceModel = sourceModel;\n this.driver = driver;\n }\n\n /**\n * Executes sync operations for a model update.\n *\n * @param sourceId - The source model ID\n * @param updatedData - The updated data to sync (Model instance or plain data)\n * @param changedFields - Fields that were changed (for filtering)\n * @returns Sync result with success status and details\n */\n public async syncUpdate(\n sourceId: string | number,\n updatedData: Record<string, unknown> | Model,\n changedFields: string[],\n ): Promise<SyncResult> {\n try {\n const syncConfigs = this.getSyncConfigs();\n\n if (syncConfigs.length === 0) {\n return this.createEmptyResult();\n }\n\n const options: SyncInstructionOptions = {\n currentDepth: 1,\n syncChain: [this.sourceModel.name],\n maxDepth: DEFAULT_MAX_SYNC_DEPTH,\n preventCircular: true,\n };\n\n const instructions = await this.collectInstructions({\n sourceId,\n updatedData,\n changedFields,\n syncConfigs,\n options,\n });\n\n return await this.executeInstructions(instructions);\n } catch (error) {\n // Catch-all error handler for unexpected failures\n console.error(`Sync update failed for ${this.sourceModel.name}#${sourceId}:`, error);\n\n return {\n success: false,\n attempted: 0,\n succeeded: 0,\n failed: 1,\n errors: [\n {\n instruction: {\n targetTable: \"\",\n targetModel: \"\",\n filter: {},\n update: {},\n depth: 0,\n chain: [this.sourceModel.name],\n sourceModel: this.sourceModel.name,\n sourceId,\n },\n error: error instanceof Error ? error : new Error(String(error)),\n },\n ],\n depthReached: 0,\n contexts: [],\n };\n }\n }\n\n /**\n * Executes sync operations for a model update with a specific config.\n * Used by ModelSyncOperation for event-based sync.\n *\n * @param sourceId - The source model ID\n * @param updatedData - The updated data to sync\n * @param changedFields - Fields that were changed\n * @param config - The sync configuration to use\n * @returns Sync result with success status and details\n */\n public async syncUpdateWithConfig(\n sourceId: string | number,\n updatedData: Record<string, unknown> | Model,\n changedFields: string[],\n config: SyncConfig,\n ): Promise<SyncResult> {\n try {\n const options: SyncInstructionOptions = {\n currentDepth: 1,\n syncChain: [this.sourceModel.name],\n maxDepth: config.maxSyncDepth,\n preventCircular: config.preventCircularSync,\n };\n\n const instructions = await this.collectInstructions({\n sourceId,\n updatedData,\n changedFields,\n syncConfigs: [config],\n options,\n });\n\n return await this.executeInstructions(instructions);\n } catch (error) {\n console.error(\n `Sync update with config failed for ${this.sourceModel.name}#${sourceId}:`,\n error,\n );\n\n return {\n success: false,\n attempted: 0,\n succeeded: 0,\n failed: 1,\n errors: [\n {\n instruction: {\n targetTable: \"\",\n targetModel: \"\",\n filter: {},\n update: {},\n depth: 0,\n chain: [this.sourceModel.name],\n sourceModel: this.sourceModel.name,\n sourceId,\n },\n error: error instanceof Error ? error : new Error(String(error)),\n },\n ],\n depthReached: 0,\n contexts: [],\n };\n }\n }\n\n /**\n * Executes sync delete operations with a specific config.\n * Used by ModelSyncOperation for event-based sync.\n *\n * @param sourceId - The source model ID\n * @param config - The sync configuration to use\n * @returns Sync result with success status and details\n */\n public async syncDeleteWithConfig(\n sourceId: string | number,\n config: SyncConfig,\n ): Promise<SyncResult> {\n try {\n if (!config.unsetOnDelete) {\n return this.createEmptyResult();\n }\n\n const options: SyncInstructionOptions = {\n currentDepth: 1,\n syncChain: [this.sourceModel.name],\n maxDepth: config.maxSyncDepth,\n preventCircular: config.preventCircularSync,\n };\n\n const instructions = await this.collectDeleteInstructions(sourceId, [config], options);\n\n return await this.executeInstructions(instructions);\n } catch (error) {\n console.error(\n `Sync delete with config failed for ${this.sourceModel.name}#${sourceId}:`,\n error,\n );\n\n return {\n success: false,\n attempted: 0,\n succeeded: 0,\n failed: 1,\n errors: [\n {\n instruction: {\n targetTable: \"\",\n targetModel: \"\",\n filter: {},\n update: {},\n depth: 0,\n chain: [this.sourceModel.name],\n sourceModel: this.sourceModel.name,\n sourceId,\n },\n error: error instanceof Error ? error : new Error(String(error)),\n },\n ],\n depthReached: 0,\n contexts: [],\n };\n }\n }\n\n /**\n * Executes sync operations for a model deletion.\n *\n * @param sourceId - The source model ID\n * @returns Sync result with success status and details\n */\n public async syncDelete(sourceId: string | number): Promise<SyncResult> {\n try {\n const syncConfigs = this.getSyncConfigs();\n\n if (syncConfigs.length === 0) {\n return this.createEmptyResult();\n }\n\n const options: SyncInstructionOptions = {\n currentDepth: 1,\n syncChain: [this.sourceModel.name],\n maxDepth: DEFAULT_MAX_SYNC_DEPTH,\n preventCircular: true,\n };\n\n const instructions = await this.collectDeleteInstructions(sourceId, syncConfigs, options);\n\n return await this.executeInstructions(instructions);\n } catch (error) {\n // Catch-all error handler for unexpected failures\n console.error(`Sync delete failed for ${this.sourceModel.name}#${sourceId}:`, error);\n\n return {\n success: false,\n attempted: 0,\n succeeded: 0,\n failed: 1,\n errors: [\n {\n instruction: {\n targetTable: \"\",\n targetModel: \"\",\n filter: {},\n update: {},\n depth: 0,\n chain: [this.sourceModel.name],\n sourceModel: this.sourceModel.name,\n sourceId,\n },\n error: error instanceof Error ? error : new Error(String(error)),\n },\n ],\n depthReached: 0,\n contexts: [],\n };\n }\n }\n\n /**\n * Collects sync instructions recursively with depth limiting.\n *\n * @param payload - Data payload\n * @returns Array of sync instructions\n */\n private async collectInstructions(\n payload: CollectInstructionsPayload,\n ): Promise<SyncInstruction[]> {\n const { sourceId, updatedData, changedFields, syncConfigs, options } = payload;\n const instructions: SyncInstruction[] = [];\n\n for (const config of syncConfigs) {\n // Check if we should sync based on watched fields\n if (!this.shouldSync(config, changedFields)) {\n continue;\n }\n\n // Validate depth and circular references\n const validation = SyncContextManager.validate(\n options.currentDepth,\n options.syncChain,\n config.targetModelClass.name,\n config.maxSyncDepth,\n config.preventCircularSync,\n );\n\n if (!validation.valid) {\n console.warn(`Sync validation failed: ${validation.error}`);\n continue;\n }\n\n // Get embedded data (handle both Model instance and plain data)\n const embedData = await this.getEmbedData(updatedData, config);\n\n // Build instruction\n const instruction = this.buildUpdateInstruction(sourceId, config, embedData, options);\n\n instructions.push(instruction);\n\n // Emit syncing event\n await this.emitSyncingEvent(instruction);\n\n // Recursively collect instructions for next level\n if (SyncContextManager.canSyncDeeper(options.currentDepth, config.maxSyncDepth)) {\n const nextLevelInstructions = await this.collectNextLevelInstructions(\n instruction,\n embedData,\n changedFields,\n config,\n options,\n );\n instructions.push(...nextLevelInstructions);\n }\n }\n\n return instructions;\n }\n\n /**\n * Collects delete sync instructions.\n *\n * @param sourceId - Source model ID\n * @param syncConfigs - Sync configurations\n * @param options - Instruction options\n * @returns Array of sync instructions\n */\n private async collectDeleteInstructions(\n sourceId: string | number,\n syncConfigs: SyncConfig[],\n options: SyncInstructionOptions,\n ): Promise<SyncInstruction[]> {\n const instructions: SyncInstruction[] = [];\n\n for (const config of syncConfigs) {\n if (!config.unsetOnDelete) {\n continue;\n }\n\n const validation = SyncContextManager.validate(\n options.currentDepth,\n options.syncChain,\n config.targetModelClass.name,\n config.maxSyncDepth,\n config.preventCircularSync,\n );\n\n if (!validation.valid) {\n continue;\n }\n\n const instruction = this.buildDeleteInstruction(sourceId, config, options);\n instructions.push(instruction);\n\n await this.emitSyncingEvent(instruction);\n }\n\n return instructions;\n }\n\n /**\n * Collects instructions for the next level in the sync chain.\n *\n * @param parentInstruction - The parent instruction\n * @param embedData - Embedded data from parent\n * @param changedFields - Changed fields\n * @param parentConfig - Parent sync config\n * @param parentOptions - Parent instruction options\n * @returns Array of next-level sync instructions\n */\n private async collectNextLevelInstructions(\n parentInstruction: SyncInstruction,\n embedData: Record<string, unknown>,\n changedFields: string[],\n parentConfig: SyncConfig,\n parentOptions: SyncInstructionOptions,\n ): Promise<SyncInstruction[]> {\n const targetModelClass = parentConfig.targetModelClass;\n const targetSyncConfigs = this.getSyncConfigsForModel(targetModelClass);\n\n if (targetSyncConfigs.length === 0) {\n return [];\n }\n\n const nextOptions: SyncInstructionOptions = {\n currentDepth: parentOptions.currentDepth + 1,\n syncChain: SyncContextManager.extendChain(parentOptions.syncChain, targetModelClass.name),\n maxDepth: Math.min(parentConfig.maxSyncDepth, parentOptions.maxDepth),\n preventCircular: parentOptions.preventCircular && parentConfig.preventCircularSync,\n };\n\n // Get the ID from embed data for next level\n const sourceId = embedData[parentConfig.identifierField] as string | number;\n\n return await this.collectInstructions({\n sourceId,\n updatedData: embedData,\n changedFields,\n syncConfigs: targetSyncConfigs,\n options: nextOptions,\n });\n }\n\n /**\n * Builds an update sync instruction.\n *\n * @param sourceId - Source model ID\n * @param config - Sync configuration\n * @param embedData - Embedded data to sync\n * @param options - Instruction options\n * @returns Sync instruction\n */\n private buildUpdateInstruction(\n sourceId: string | number,\n config: SyncConfig,\n embedData: Record<string, unknown>,\n options: SyncInstructionOptions,\n ): SyncInstruction {\n const targetModelClass = config.targetModelClass;\n const filter = this.buildFilter(sourceId, config);\n const update = this.buildUpdate(embedData, config);\n\n const instruction: SyncInstruction = {\n targetTable: targetModelClass.table,\n targetModel: targetModelClass.name,\n filter,\n update,\n depth: options.currentDepth,\n chain: [...options.syncChain],\n sourceModel: this.sourceModel.name,\n sourceId,\n };\n\n // Add array update metadata if needed\n if (config.isMany) {\n instruction.isArrayUpdate = true;\n instruction.arrayField = config.targetField;\n instruction.identifierField = config.identifierField;\n instruction.identifierValue = sourceId;\n }\n\n return instruction;\n }\n\n /**\n * Builds a delete sync instruction.\n *\n * @param sourceId - Source model ID\n * @param config - Sync configuration\n * @param options - Instruction options\n * @returns Sync instruction\n */\n private buildDeleteInstruction(\n sourceId: string | number,\n config: SyncConfig,\n options: SyncInstructionOptions,\n ): SyncInstruction {\n const targetModelClass = config.targetModelClass;\n const filter = this.buildFilter(sourceId, config);\n const update = { $unset: { [config.targetField]: 1 } };\n\n return {\n targetTable: targetModelClass.table,\n targetModel: targetModelClass.name,\n filter,\n update,\n depth: options.currentDepth,\n chain: [...options.syncChain],\n sourceModel: this.sourceModel.name,\n sourceId,\n };\n }\n\n /**\n * Builds a filter for identifying target documents.\n *\n * @param sourceId - Source model ID\n * @param config - Sync configuration\n * @returns Filter object\n */\n private buildFilter(sourceId: string | number, config: SyncConfig): Record<string, unknown> {\n if (config.isMany) {\n // For arrays: { \"products.id\": productId }\n return { [`${config.targetField}.${config.identifierField}`]: sourceId };\n } else {\n // For single: { \"category.id\": categoryId }\n return { [`${config.targetField}.${config.identifierField}`]: sourceId };\n }\n }\n\n /**\n * Builds an update operation for syncing data.\n *\n * @param embedData - Embedded data to sync\n * @param config - Sync configuration\n * @returns Update operation object\n */\n private buildUpdate(\n embedData: Record<string, unknown>,\n config: SyncConfig,\n ): Record<string, unknown> {\n if (config.isMany) {\n // For arrays: use positional operator $\n return { $set: { [`${config.targetField}.$`]: embedData } };\n } else {\n // For single: direct set\n return { $set: { [config.targetField]: embedData } };\n }\n }\n\n /**\n * Executes sync instructions with batch optimization.\n * Groups by depth and target table for optimal batching.\n *\n * @param instructions - Array of sync instructions\n * @returns Sync result\n */\n private async executeInstructions(instructions: SyncInstruction[]): Promise<SyncResult> {\n const result: SyncResult = {\n success: true,\n attempted: instructions.length,\n succeeded: 0,\n failed: 0,\n errors: [],\n depthReached: 0,\n contexts: [],\n };\n\n if (instructions.length === 0) {\n return result;\n }\n\n // Group instructions by depth for sequential execution\n const instructionsByDepth = this.groupByDepth(instructions);\n\n for (const [depth, depthInstructions] of instructionsByDepth) {\n result.depthReached = Math.max(result.depthReached, depth);\n\n // Further group by target table for better batching\n const instructionsByTable = this.groupByTable(depthInstructions);\n\n for (const [table, tableInstructions] of instructionsByTable) {\n try {\n // Try batch execution first (all instructions for this table)\n await this.executeBatch(tableInstructions, result);\n } catch (batchError) {\n // Fallback to individual execution on batch failure\n console.warn(\n `Batch execution failed for table ${table} at depth ${depth}, falling back to individual execution`,\n );\n await this.executeIndividual(tableInstructions, result);\n }\n }\n }\n\n result.success = result.failed === 0;\n return result;\n }\n\n /**\n * Executes instructions in batch.\n *\n * @param instructions - Instructions to execute\n * @param result - Result object to update\n */\n private async executeBatch(instructions: SyncInstruction[], result: SyncResult): Promise<void> {\n for (const instruction of instructions) {\n try {\n const updateResult = await this.driver.updateMany(\n instruction.targetTable,\n instruction.filter,\n instruction.update,\n );\n\n const context = SyncContextManager.createContext(instruction, updateResult.modifiedCount);\n result.contexts.push(context);\n result.succeeded++;\n\n await this.emitSyncedEvent(context);\n } catch (error) {\n throw error; // Re-throw for batch fallback\n }\n }\n }\n\n /**\n * Executes instructions individually (fallback).\n * Provides detailed error reporting for each failed instruction.\n *\n * @param instructions - Instructions to execute\n * @param result - Result object to update\n */\n private async executeIndividual(\n instructions: SyncInstruction[],\n result: SyncResult,\n ): Promise<void> {\n for (const instruction of instructions) {\n try {\n const updateResult = await this.driver.updateMany(\n instruction.targetTable,\n instruction.filter,\n instruction.update,\n );\n\n const context = SyncContextManager.createContext(instruction, updateResult.modifiedCount);\n result.contexts.push(context);\n result.succeeded++;\n\n await this.emitSyncedEvent(context);\n } catch (error) {\n result.failed++;\n\n const errorMessage = this.formatSyncError(instruction, error);\n const syncError = new Error(errorMessage);\n\n // Preserve original error stack if available\n if (error instanceof Error && error.stack) {\n syncError.stack = error.stack;\n }\n\n result.errors.push({\n instruction,\n error: syncError,\n });\n\n // Log detailed error for debugging\n console.error(`Sync operation failed:`, {\n sourceModel: instruction.sourceModel,\n sourceId: instruction.sourceId,\n targetModel: instruction.targetModel,\n targetTable: instruction.targetTable,\n depth: instruction.depth,\n chain: SyncContextManager.formatChain(instruction.chain),\n filter: instruction.filter,\n error: errorMessage,\n });\n }\n }\n }\n\n /**\n * Formats a sync error with detailed context.\n *\n * @param instruction - The failed instruction\n * @param error - The error that occurred\n * @returns Formatted error message\n */\n private formatSyncError(instruction: SyncInstruction, error: unknown): string {\n const baseMessage = error instanceof Error ? error.message : String(error);\n const chain = SyncContextManager.formatChain(instruction.chain);\n\n return [\n `Sync failed at depth ${instruction.depth}:`,\n `Chain: ${chain} → ${instruction.targetModel}`,\n `Source: ${instruction.sourceModel}#${instruction.sourceId}`,\n `Target: ${instruction.targetTable}`,\n `Error: ${baseMessage}`,\n ].join(\" | \");\n }\n\n /**\n * Groups instructions by depth for batch processing.\n *\n * @param instructions - Instructions to group\n * @returns Map of depth to instructions (sorted ascending)\n */\n private groupByDepth(instructions: SyncInstruction[]): Map<number, SyncInstruction[]> {\n const grouped = new Map<number, SyncInstruction[]>();\n\n for (const instruction of instructions) {\n const depth = instruction.depth;\n if (!grouped.has(depth)) {\n grouped.set(depth, []);\n }\n grouped.get(depth)!.push(instruction);\n }\n\n // Sort by depth (ascending) for sequential execution\n return new Map([...grouped.entries()].sort((a, b) => a[0] - b[0]));\n }\n\n /**\n * Groups instructions by target table for batch optimization.\n *\n * @param instructions - Instructions to group\n * @returns Map of table name to instructions\n */\n private groupByTable(instructions: SyncInstruction[]): Map<string, SyncInstruction[]> {\n const grouped = new Map<string, SyncInstruction[]>();\n\n for (const instruction of instructions) {\n const table = instruction.targetTable;\n if (!grouped.has(table)) {\n grouped.set(table, []);\n }\n grouped.get(table)!.push(instruction);\n }\n\n return grouped;\n }\n\n /**\n * Checks if sync should proceed based on watched fields.\n *\n * @param config - Sync configuration\n * @param changedFields - Fields that changed\n * @returns True if sync should proceed\n */\n private shouldSync(config: SyncConfig, changedFields: string[]): boolean {\n if (config.watchFields.length === 0) {\n return true; // Watch all fields\n }\n\n return config.watchFields.some((field) => changedFields.includes(field));\n }\n\n /**\n * Gets embedded data from the source model.\n *\n * @param data - Source model data or Model instance\n * @param config - Sync configuration\n * @returns Embedded data\n */\n private async getEmbedData(\n data: Record<string, unknown> | Model,\n config: SyncConfig,\n ): Promise<Record<string, unknown>> {\n // If data is a Model instance, call the embed method on it\n if (data instanceof Model) {\n if (Array.isArray(config.embedKey)) {\n return data.only(config.embedKey);\n }\n\n if (typeof data[config.embedKey as keyof Model] !== \"function\") {\n return data[config.embedKey as keyof Model] as Record<string, unknown>;\n }\n // Fallback: use embedData() if available\n if (typeof data.embedData === \"function\") {\n return data.embedData;\n }\n // Last resort: return model data\n return data.data;\n }\n\n // Otherwise, return the data as-is\n return data as Record<string, unknown>;\n }\n\n /**\n * Gets sync configurations from the source model.\n *\n * @returns Array of sync configurations\n */\n private getSyncConfigs(): SyncConfig[] {\n const syncWith = (this.sourceModel as any).syncWith;\n\n if (!syncWith || !Array.isArray(syncWith)) {\n return [];\n }\n\n return syncWith.map((builder: any) =>\n typeof builder.build === \"function\" ? builder.build() : builder,\n );\n }\n\n /**\n * Gets sync configurations for a specific model.\n *\n * @param modelClass - The model class\n * @returns Array of sync configurations\n */\n private getSyncConfigsForModel(modelClass: ChildModel<Model>): SyncConfig[] {\n const syncWith = (modelClass as any).syncWith;\n\n if (!syncWith || !Array.isArray(syncWith)) {\n return [];\n }\n\n return syncWith.map((builder: any) =>\n typeof builder.build === \"function\" ? builder.build() : builder,\n );\n }\n\n /**\n * Emits a syncing event.\n *\n * @param instruction - The sync instruction\n */\n private async emitSyncingEvent(instruction: SyncInstruction): Promise<void> {\n const payload: SyncEventPayload = {\n sourceModel: instruction.sourceModel,\n sourceId: instruction.sourceId,\n targetModel: instruction.targetModel,\n filter: instruction.filter,\n update: instruction.update,\n affectedCount: 0, // Not yet known\n depth: instruction.depth,\n chain: instruction.chain,\n };\n\n // Emit on source model\n if (typeof (this.sourceModel as any).emitSyncEvent === \"function\") {\n await (this.sourceModel as any).emitSyncEvent(\"syncing\", payload);\n }\n }\n\n /**\n * Emits a synced event.\n *\n * @param context - The sync context\n */\n private async emitSyncedEvent(context: any): Promise<void> {\n const payload: SyncEventPayload = {\n sourceModel: context.sourceModel,\n sourceId: context.sourceId,\n targetModel: context.targetModel,\n filter: context.filter,\n update: context.update,\n affectedCount: context.affectedCount,\n depth: context.currentDepth,\n chain: context.syncChain,\n };\n\n // Emit on source model\n if (typeof (this.sourceModel as any).emitSyncEvent === \"function\") {\n await (this.sourceModel as any).emitSyncEvent(\"synced\", payload);\n }\n }\n\n /**\n * Creates an empty sync result.\n *\n * @returns Empty sync result\n */\n private createEmptyResult(): SyncResult {\n return {\n success: true,\n attempted: 0,\n succeeded: 0,\n failed: 0,\n errors: [],\n depthReached: 0,\n contexts: [],\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sync-manager.mjs","names":[],"sources":["../../../../../../../cascade/src/sync/sync-manager.ts"],"sourcesContent":["/**\n * Sync manager service for handling multi-level sync operations.\n *\n * @module cascade-next/sync/sync-manager\n */\n\nimport type { DriverContract } from \"../contracts/database-driver.contract\";\nimport { ChildModel, Model } from \"../model/model\";\nimport { DEFAULT_MAX_SYNC_DEPTH, SyncContextManager } from \"./sync-context\";\nimport type {\n CollectInstructionsPayload,\n SyncConfig,\n SyncEventPayload,\n SyncInstruction,\n SyncInstructionOptions,\n SyncResult,\n} from \"./types\";\n\n/**\n * Manages sync operations across models with multi-level support.\n */\nexport class SyncManager {\n private readonly sourceModel: ChildModel<Model>;\n private readonly driver: DriverContract;\n\n /**\n * Creates a new sync manager.\n *\n * @param sourceModel - The source model class\n * @param driver - The database driver\n */\n public constructor(sourceModel: ChildModel<Model>, driver: DriverContract) {\n this.sourceModel = sourceModel;\n this.driver = driver;\n }\n\n /**\n * Executes sync operations for a model update.\n *\n * @param sourceId - The source model ID\n * @param updatedData - The updated data to sync (Model instance or plain data)\n * @param changedFields - Fields that were changed (for filtering)\n * @returns Sync result with success status and details\n */\n public async syncUpdate(\n sourceId: string | number,\n updatedData: Record<string, unknown> | Model,\n changedFields: string[],\n ): Promise<SyncResult> {\n try {\n const syncConfigs = this.getSyncConfigs();\n\n if (syncConfigs.length === 0) {\n return this.createEmptyResult();\n }\n\n const options: SyncInstructionOptions = {\n currentDepth: 1,\n syncChain: [this.sourceModel.name],\n maxDepth: DEFAULT_MAX_SYNC_DEPTH,\n preventCircular: true,\n };\n\n const instructions = await this.collectInstructions({\n sourceId,\n updatedData,\n changedFields,\n syncConfigs,\n options,\n });\n\n return await this.executeInstructions(instructions);\n } catch (error) {\n // Catch-all error handler for unexpected failures\n console.error(`Sync update failed for ${this.sourceModel.name}#${sourceId}:`, error);\n\n return {\n success: false,\n attempted: 0,\n succeeded: 0,\n failed: 1,\n errors: [\n {\n instruction: {\n targetTable: \"\",\n targetModel: \"\",\n filter: {},\n update: {},\n depth: 0,\n chain: [this.sourceModel.name],\n sourceModel: this.sourceModel.name,\n sourceId,\n },\n error: error instanceof Error ? error : new Error(String(error)),\n },\n ],\n depthReached: 0,\n contexts: [],\n };\n }\n }\n\n /**\n * Executes sync operations for a model update with a specific config.\n * Used by ModelSyncOperation for event-based sync.\n *\n * @param sourceId - The source model ID\n * @param updatedData - The updated data to sync\n * @param changedFields - Fields that were changed\n * @param config - The sync configuration to use\n * @returns Sync result with success status and details\n */\n public async syncUpdateWithConfig(\n sourceId: string | number,\n updatedData: Record<string, unknown> | Model,\n changedFields: string[],\n config: SyncConfig,\n ): Promise<SyncResult> {\n try {\n const options: SyncInstructionOptions = {\n currentDepth: 1,\n syncChain: [this.sourceModel.name],\n maxDepth: config.maxSyncDepth,\n preventCircular: config.preventCircularSync,\n };\n\n const instructions = await this.collectInstructions({\n sourceId,\n updatedData,\n changedFields,\n syncConfigs: [config],\n options,\n });\n\n return await this.executeInstructions(instructions);\n } catch (error) {\n console.error(\n `Sync update with config failed for ${this.sourceModel.name}#${sourceId}:`,\n error,\n );\n\n return {\n success: false,\n attempted: 0,\n succeeded: 0,\n failed: 1,\n errors: [\n {\n instruction: {\n targetTable: \"\",\n targetModel: \"\",\n filter: {},\n update: {},\n depth: 0,\n chain: [this.sourceModel.name],\n sourceModel: this.sourceModel.name,\n sourceId,\n },\n error: error instanceof Error ? error : new Error(String(error)),\n },\n ],\n depthReached: 0,\n contexts: [],\n };\n }\n }\n\n /**\n * Executes sync delete operations with a specific config.\n * Used by ModelSyncOperation for event-based sync.\n *\n * @param sourceId - The source model ID\n * @param config - The sync configuration to use\n * @returns Sync result with success status and details\n */\n public async syncDeleteWithConfig(\n sourceId: string | number,\n config: SyncConfig,\n ): Promise<SyncResult> {\n try {\n if (!config.unsetOnDelete) {\n return this.createEmptyResult();\n }\n\n const options: SyncInstructionOptions = {\n currentDepth: 1,\n syncChain: [this.sourceModel.name],\n maxDepth: config.maxSyncDepth,\n preventCircular: config.preventCircularSync,\n };\n\n const instructions = await this.collectDeleteInstructions(sourceId, [config], options);\n\n return await this.executeInstructions(instructions);\n } catch (error) {\n console.error(\n `Sync delete with config failed for ${this.sourceModel.name}#${sourceId}:`,\n error,\n );\n\n return {\n success: false,\n attempted: 0,\n succeeded: 0,\n failed: 1,\n errors: [\n {\n instruction: {\n targetTable: \"\",\n targetModel: \"\",\n filter: {},\n update: {},\n depth: 0,\n chain: [this.sourceModel.name],\n sourceModel: this.sourceModel.name,\n sourceId,\n },\n error: error instanceof Error ? error : new Error(String(error)),\n },\n ],\n depthReached: 0,\n contexts: [],\n };\n }\n }\n\n /**\n * Executes sync operations for a model deletion.\n *\n * @param sourceId - The source model ID\n * @returns Sync result with success status and details\n */\n public async syncDelete(sourceId: string | number): Promise<SyncResult> {\n try {\n const syncConfigs = this.getSyncConfigs();\n\n if (syncConfigs.length === 0) {\n return this.createEmptyResult();\n }\n\n const options: SyncInstructionOptions = {\n currentDepth: 1,\n syncChain: [this.sourceModel.name],\n maxDepth: DEFAULT_MAX_SYNC_DEPTH,\n preventCircular: true,\n };\n\n const instructions = await this.collectDeleteInstructions(sourceId, syncConfigs, options);\n\n return await this.executeInstructions(instructions);\n } catch (error) {\n // Catch-all error handler for unexpected failures\n console.error(`Sync delete failed for ${this.sourceModel.name}#${sourceId}:`, error);\n\n return {\n success: false,\n attempted: 0,\n succeeded: 0,\n failed: 1,\n errors: [\n {\n instruction: {\n targetTable: \"\",\n targetModel: \"\",\n filter: {},\n update: {},\n depth: 0,\n chain: [this.sourceModel.name],\n sourceModel: this.sourceModel.name,\n sourceId,\n },\n error: error instanceof Error ? error : new Error(String(error)),\n },\n ],\n depthReached: 0,\n contexts: [],\n };\n }\n }\n\n /**\n * Collects sync instructions recursively with depth limiting.\n *\n * @param payload - Data payload\n * @returns Array of sync instructions\n */\n private async collectInstructions(\n payload: CollectInstructionsPayload,\n ): Promise<SyncInstruction[]> {\n const { sourceId, updatedData, changedFields, syncConfigs, options } = payload;\n const instructions: SyncInstruction[] = [];\n\n for (const config of syncConfigs) {\n // Check if we should sync based on watched fields\n if (!this.shouldSync(config, changedFields)) {\n continue;\n }\n\n // Validate depth and circular references\n const validation = SyncContextManager.validate(\n options.currentDepth,\n options.syncChain,\n config.targetModelClass.name,\n config.maxSyncDepth,\n config.preventCircularSync,\n );\n\n if (!validation.valid) {\n console.warn(`Sync validation failed: ${validation.error}`);\n continue;\n }\n\n // Get embedded data (handle both Model instance and plain data)\n const embedData = await this.getEmbedData(updatedData, config);\n\n // Build instruction\n const instruction = this.buildUpdateInstruction(sourceId, config, embedData, options);\n\n instructions.push(instruction);\n\n // Emit syncing event\n await this.emitSyncingEvent(instruction);\n\n // Recursively collect instructions for next level\n if (SyncContextManager.canSyncDeeper(options.currentDepth, config.maxSyncDepth)) {\n const nextLevelInstructions = await this.collectNextLevelInstructions(\n instruction,\n embedData,\n changedFields,\n config,\n options,\n );\n instructions.push(...nextLevelInstructions);\n }\n }\n\n return instructions;\n }\n\n /**\n * Collects delete sync instructions.\n *\n * @param sourceId - Source model ID\n * @param syncConfigs - Sync configurations\n * @param options - Instruction options\n * @returns Array of sync instructions\n */\n private async collectDeleteInstructions(\n sourceId: string | number,\n syncConfigs: SyncConfig[],\n options: SyncInstructionOptions,\n ): Promise<SyncInstruction[]> {\n const instructions: SyncInstruction[] = [];\n\n for (const config of syncConfigs) {\n if (!config.unsetOnDelete) {\n continue;\n }\n\n const validation = SyncContextManager.validate(\n options.currentDepth,\n options.syncChain,\n config.targetModelClass.name,\n config.maxSyncDepth,\n config.preventCircularSync,\n );\n\n if (!validation.valid) {\n continue;\n }\n\n const instruction = this.buildDeleteInstruction(sourceId, config, options);\n instructions.push(instruction);\n\n await this.emitSyncingEvent(instruction);\n }\n\n return instructions;\n }\n\n /**\n * Collects instructions for the next level in the sync chain.\n *\n * @param parentInstruction - The parent instruction\n * @param embedData - Embedded data from parent\n * @param changedFields - Changed fields\n * @param parentConfig - Parent sync config\n * @param parentOptions - Parent instruction options\n * @returns Array of next-level sync instructions\n */\n private async collectNextLevelInstructions(\n parentInstruction: SyncInstruction,\n embedData: Record<string, unknown>,\n changedFields: string[],\n parentConfig: SyncConfig,\n parentOptions: SyncInstructionOptions,\n ): Promise<SyncInstruction[]> {\n const targetModelClass = parentConfig.targetModelClass;\n const targetSyncConfigs = this.getSyncConfigsForModel(targetModelClass);\n\n if (targetSyncConfigs.length === 0) {\n return [];\n }\n\n const nextOptions: SyncInstructionOptions = {\n currentDepth: parentOptions.currentDepth + 1,\n syncChain: SyncContextManager.extendChain(parentOptions.syncChain, targetModelClass.name),\n maxDepth: Math.min(parentConfig.maxSyncDepth, parentOptions.maxDepth),\n preventCircular: parentOptions.preventCircular && parentConfig.preventCircularSync,\n };\n\n // Get the ID from embed data for next level\n const sourceId = embedData[parentConfig.identifierField] as string | number;\n\n return await this.collectInstructions({\n sourceId,\n updatedData: embedData,\n changedFields,\n syncConfigs: targetSyncConfigs,\n options: nextOptions,\n });\n }\n\n /**\n * Builds an update sync instruction.\n *\n * @param sourceId - Source model ID\n * @param config - Sync configuration\n * @param embedData - Embedded data to sync\n * @param options - Instruction options\n * @returns Sync instruction\n */\n private buildUpdateInstruction(\n sourceId: string | number,\n config: SyncConfig,\n embedData: Record<string, unknown>,\n options: SyncInstructionOptions,\n ): SyncInstruction {\n const targetModelClass = config.targetModelClass;\n const filter = this.buildFilter(sourceId, config);\n const update = this.buildUpdate(embedData, config);\n\n const instruction: SyncInstruction = {\n targetTable: targetModelClass.table,\n targetModel: targetModelClass.name,\n filter,\n update,\n depth: options.currentDepth,\n chain: [...options.syncChain],\n sourceModel: this.sourceModel.name,\n sourceId,\n };\n\n // Add array update metadata if needed\n if (config.isMany) {\n instruction.isArrayUpdate = true;\n instruction.arrayField = config.targetField;\n instruction.identifierField = config.identifierField;\n instruction.identifierValue = sourceId;\n }\n\n return instruction;\n }\n\n /**\n * Builds a delete sync instruction.\n *\n * @param sourceId - Source model ID\n * @param config - Sync configuration\n * @param options - Instruction options\n * @returns Sync instruction\n */\n private buildDeleteInstruction(\n sourceId: string | number,\n config: SyncConfig,\n options: SyncInstructionOptions,\n ): SyncInstruction {\n const targetModelClass = config.targetModelClass;\n const filter = this.buildFilter(sourceId, config);\n const update = { $unset: { [config.targetField]: 1 } };\n\n return {\n targetTable: targetModelClass.table,\n targetModel: targetModelClass.name,\n filter,\n update,\n depth: options.currentDepth,\n chain: [...options.syncChain],\n sourceModel: this.sourceModel.name,\n sourceId,\n };\n }\n\n /**\n * Builds a filter for identifying target documents.\n *\n * @param sourceId - Source model ID\n * @param config - Sync configuration\n * @returns Filter object\n */\n private buildFilter(sourceId: string | number, config: SyncConfig): Record<string, unknown> {\n if (config.isMany) {\n // For arrays: { \"products.id\": productId }\n return { [`${config.targetField}.${config.identifierField}`]: sourceId };\n } else {\n // For single: { \"category.id\": categoryId }\n return { [`${config.targetField}.${config.identifierField}`]: sourceId };\n }\n }\n\n /**\n * Builds an update operation for syncing data.\n *\n * @param embedData - Embedded data to sync\n * @param config - Sync configuration\n * @returns Update operation object\n */\n private buildUpdate(\n embedData: Record<string, unknown>,\n config: SyncConfig,\n ): Record<string, unknown> {\n if (config.isMany) {\n // For arrays: use positional operator $\n return { $set: { [`${config.targetField}.$`]: embedData } };\n } else {\n // For single: direct set\n return { $set: { [config.targetField]: embedData } };\n }\n }\n\n /**\n * Executes sync instructions with batch optimization.\n * Groups by depth and target table for optimal batching.\n *\n * @param instructions - Array of sync instructions\n * @returns Sync result\n */\n private async executeInstructions(instructions: SyncInstruction[]): Promise<SyncResult> {\n const result: SyncResult = {\n success: true,\n attempted: instructions.length,\n succeeded: 0,\n failed: 0,\n errors: [],\n depthReached: 0,\n contexts: [],\n };\n\n if (instructions.length === 0) {\n return result;\n }\n\n // Group instructions by depth for sequential execution\n const instructionsByDepth = this.groupByDepth(instructions);\n\n for (const [depth, depthInstructions] of instructionsByDepth) {\n result.depthReached = Math.max(result.depthReached, depth);\n\n // Further group by target table for better batching\n const instructionsByTable = this.groupByTable(depthInstructions);\n\n for (const [table, tableInstructions] of instructionsByTable) {\n try {\n // Try batch execution first (all instructions for this table)\n await this.executeBatch(tableInstructions, result);\n } catch (batchError) {\n // Fallback to individual execution on batch failure\n console.warn(\n `Batch execution failed for table ${table} at depth ${depth}, falling back to individual execution`,\n );\n await this.executeIndividual(tableInstructions, result);\n }\n }\n }\n\n result.success = result.failed === 0;\n return result;\n }\n\n /**\n * Executes instructions in batch.\n *\n * @param instructions - Instructions to execute\n * @param result - Result object to update\n */\n private async executeBatch(instructions: SyncInstruction[], result: SyncResult): Promise<void> {\n for (const instruction of instructions) {\n try {\n const updateResult = await this.driver.updateMany(\n instruction.targetTable,\n instruction.filter,\n instruction.update,\n );\n\n const context = SyncContextManager.createContext(instruction, updateResult.modifiedCount);\n result.contexts.push(context);\n result.succeeded++;\n\n await this.emitSyncedEvent(context);\n } catch (error) {\n throw error; // Re-throw for batch fallback\n }\n }\n }\n\n /**\n * Executes instructions individually (fallback).\n * Provides detailed error reporting for each failed instruction.\n *\n * @param instructions - Instructions to execute\n * @param result - Result object to update\n */\n private async executeIndividual(\n instructions: SyncInstruction[],\n result: SyncResult,\n ): Promise<void> {\n for (const instruction of instructions) {\n try {\n const updateResult = await this.driver.updateMany(\n instruction.targetTable,\n instruction.filter,\n instruction.update,\n );\n\n const context = SyncContextManager.createContext(instruction, updateResult.modifiedCount);\n result.contexts.push(context);\n result.succeeded++;\n\n await this.emitSyncedEvent(context);\n } catch (error) {\n result.failed++;\n\n const errorMessage = this.formatSyncError(instruction, error);\n const syncError = new Error(errorMessage);\n\n // Preserve original error stack if available\n if (error instanceof Error && error.stack) {\n syncError.stack = error.stack;\n }\n\n result.errors.push({\n instruction,\n error: syncError,\n });\n\n // Log detailed error for debugging\n console.error(`Sync operation failed:`, {\n sourceModel: instruction.sourceModel,\n sourceId: instruction.sourceId,\n targetModel: instruction.targetModel,\n targetTable: instruction.targetTable,\n depth: instruction.depth,\n chain: SyncContextManager.formatChain(instruction.chain),\n filter: instruction.filter,\n error: errorMessage,\n });\n }\n }\n }\n\n /**\n * Formats a sync error with detailed context.\n *\n * @param instruction - The failed instruction\n * @param error - The error that occurred\n * @returns Formatted error message\n */\n private formatSyncError(instruction: SyncInstruction, error: unknown): string {\n const baseMessage = error instanceof Error ? error.message : String(error);\n const chain = SyncContextManager.formatChain(instruction.chain);\n\n return [\n `Sync failed at depth ${instruction.depth}:`,\n `Chain: ${chain} → ${instruction.targetModel}`,\n `Source: ${instruction.sourceModel}#${instruction.sourceId}`,\n `Target: ${instruction.targetTable}`,\n `Error: ${baseMessage}`,\n ].join(\" | \");\n }\n\n /**\n * Groups instructions by depth for batch processing.\n *\n * @param instructions - Instructions to group\n * @returns Map of depth to instructions (sorted ascending)\n */\n private groupByDepth(instructions: SyncInstruction[]): Map<number, SyncInstruction[]> {\n const grouped = new Map<number, SyncInstruction[]>();\n\n for (const instruction of instructions) {\n const depth = instruction.depth;\n if (!grouped.has(depth)) {\n grouped.set(depth, []);\n }\n grouped.get(depth)!.push(instruction);\n }\n\n // Sort by depth (ascending) for sequential execution\n return new Map([...grouped.entries()].sort((a, b) => a[0] - b[0]));\n }\n\n /**\n * Groups instructions by target table for batch optimization.\n *\n * @param instructions - Instructions to group\n * @returns Map of table name to instructions\n */\n private groupByTable(instructions: SyncInstruction[]): Map<string, SyncInstruction[]> {\n const grouped = new Map<string, SyncInstruction[]>();\n\n for (const instruction of instructions) {\n const table = instruction.targetTable;\n if (!grouped.has(table)) {\n grouped.set(table, []);\n }\n grouped.get(table)!.push(instruction);\n }\n\n return grouped;\n }\n\n /**\n * Checks if sync should proceed based on watched fields.\n *\n * @param config - Sync configuration\n * @param changedFields - Fields that changed\n * @returns True if sync should proceed\n */\n private shouldSync(config: SyncConfig, changedFields: string[]): boolean {\n if (config.watchFields.length === 0) {\n return true; // Watch all fields\n }\n\n return config.watchFields.some((field) => changedFields.includes(field));\n }\n\n /**\n * Gets embedded data from the source model.\n *\n * @param data - Source model data or Model instance\n * @param config - Sync configuration\n * @returns Embedded data\n */\n private async getEmbedData(\n data: Record<string, unknown> | Model,\n config: SyncConfig,\n ): Promise<Record<string, unknown>> {\n // If data is a Model instance, call the embed method on it\n if (data instanceof Model) {\n if (Array.isArray(config.embedKey)) {\n return data.only(config.embedKey);\n }\n\n if (typeof data[config.embedKey as keyof Model] !== \"function\") {\n return data[config.embedKey as keyof Model] as Record<string, unknown>;\n }\n // Fallback: use embedData() if available\n if (typeof data.embedData === \"function\") {\n return data.embedData;\n }\n // Last resort: return model data\n return data.data;\n }\n\n // Otherwise, return the data as-is\n return data as Record<string, unknown>;\n }\n\n /**\n * Gets sync configurations from the source model.\n *\n * @returns Array of sync configurations\n */\n private getSyncConfigs(): SyncConfig[] {\n const syncWith = (this.sourceModel as any).syncWith;\n\n if (!syncWith || !Array.isArray(syncWith)) {\n return [];\n }\n\n return syncWith.map((builder: any) =>\n typeof builder.build === \"function\" ? builder.build() : builder,\n );\n }\n\n /**\n * Gets sync configurations for a specific model.\n *\n * @param modelClass - The model class\n * @returns Array of sync configurations\n */\n private getSyncConfigsForModel(modelClass: ChildModel<Model>): SyncConfig[] {\n const syncWith = (modelClass as any).syncWith;\n\n if (!syncWith || !Array.isArray(syncWith)) {\n return [];\n }\n\n return syncWith.map((builder: any) =>\n typeof builder.build === \"function\" ? builder.build() : builder,\n );\n }\n\n /**\n * Emits a syncing event.\n *\n * @param instruction - The sync instruction\n */\n private async emitSyncingEvent(instruction: SyncInstruction): Promise<void> {\n const payload: SyncEventPayload = {\n sourceModel: instruction.sourceModel,\n sourceId: instruction.sourceId,\n targetModel: instruction.targetModel,\n filter: instruction.filter,\n update: instruction.update,\n affectedCount: 0, // Not yet known\n depth: instruction.depth,\n chain: instruction.chain,\n };\n\n // Emit on source model\n if (typeof (this.sourceModel as any).emitSyncEvent === \"function\") {\n await (this.sourceModel as any).emitSyncEvent(\"syncing\", payload);\n }\n }\n\n /**\n * Emits a synced event.\n *\n * @param context - The sync context\n */\n private async emitSyncedEvent(context: any): Promise<void> {\n const payload: SyncEventPayload = {\n sourceModel: context.sourceModel,\n sourceId: context.sourceId,\n targetModel: context.targetModel,\n filter: context.filter,\n update: context.update,\n affectedCount: context.affectedCount,\n depth: context.currentDepth,\n chain: context.syncChain,\n };\n\n // Emit on source model\n if (typeof (this.sourceModel as any).emitSyncEvent === \"function\") {\n await (this.sourceModel as any).emitSyncEvent(\"synced\", payload);\n }\n }\n\n /**\n * Creates an empty sync result.\n *\n * @returns Empty sync result\n */\n private createEmptyResult(): SyncResult {\n return {\n success: true,\n attempted: 0,\n succeeded: 0,\n failed: 0,\n errors: [],\n depthReached: 0,\n contexts: [],\n };\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { ChildModel } from "../model/model.types.mjs";
2
2
  import { Model } from "../model/model.mjs";
3
3
 
4
- //#region ../@warlock.js/cascade/src/sync/types.d.ts
4
+ //#region ../cascade/src/sync/types.d.ts
5
5
  /**
6
6
  * Context information for a sync operation.
7
7
  * Tracks the source, target, chain, and results of a sync operation.