@noy-db/hub 0.7.0-pre.12 → 0.7.0-pre.14

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 (254) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/README.md +4 -3
  3. package/dist/{api-UE2XQKDN.js → api-JPZT6BDD.js} +1 -2
  4. package/dist/as/index.js +3 -4
  5. package/dist/as/index.js.map +1 -1
  6. package/dist/at/index.js +0 -1
  7. package/dist/attestation/index.js +6 -7
  8. package/dist/attestation/index.js.map +1 -1
  9. package/dist/{backup-R6A4VZRX.js → backup-AKWIMYWU.js} +1 -2
  10. package/dist/{backup-R6A4VZRX.js.map → backup-AKWIMYWU.js.map} +1 -1
  11. package/dist/blobs/index.js +0 -1
  12. package/dist/blobs/index.js.map +1 -1
  13. package/dist/broker/index.js +3 -4
  14. package/dist/broker/index.js.map +1 -1
  15. package/dist/by/index.js +0 -1
  16. package/dist/cargo/index.js +10 -11
  17. package/dist/{chunk-5FUULQ6F.js → chunk-2HVILGVR.js} +6 -6
  18. package/dist/{chunk-DJ3LNNZQ.js → chunk-3LK2XZCW.js} +2 -2
  19. package/dist/chunk-4SZMZAYN.js +1652 -0
  20. package/dist/chunk-4SZMZAYN.js.map +1 -0
  21. package/dist/chunk-66TQOPFM.js +37 -0
  22. package/dist/chunk-66TQOPFM.js.map +1 -0
  23. package/dist/{chunk-PQKG5AHH.js → chunk-6X6SEMEE.js} +4 -2
  24. package/dist/{chunk-PQKG5AHH.js.map → chunk-6X6SEMEE.js.map} +1 -1
  25. package/dist/{chunk-E3EBMC4G.js → chunk-AMODH7AU.js} +2 -2
  26. package/dist/{chunk-B2ZSGYSF.js → chunk-B2KCRDJM.js} +3 -3
  27. package/dist/{chunk-TO3EQ7HF.js → chunk-BLN54W3L.js} +2 -2
  28. package/dist/{chunk-WQF3QABE.js → chunk-BOBFL5TW.js} +9 -79
  29. package/dist/chunk-BOBFL5TW.js.map +1 -0
  30. package/dist/{chunk-XMF3GKUR.js → chunk-DRB6IHPS.js} +90 -910
  31. package/dist/chunk-DRB6IHPS.js.map +1 -0
  32. package/dist/{chunk-H7XCIM3Z.js → chunk-EKVNVJVV.js} +26 -16
  33. package/dist/chunk-EKVNVJVV.js.map +1 -0
  34. package/dist/{chunk-ZR2YMODS.js → chunk-EKZ6VZYF.js} +2 -2
  35. package/dist/{chunk-RTYJRFQO.js → chunk-EWPRPP3M.js} +1 -1
  36. package/dist/{chunk-JJR2JIN4.js → chunk-FFBYRWDJ.js} +2 -2
  37. package/dist/{chunk-VGICZPVE.js → chunk-FUJSIP4K.js} +2 -2
  38. package/dist/chunk-MGOXJIBV.js +176 -0
  39. package/dist/chunk-MGOXJIBV.js.map +1 -0
  40. package/dist/{chunk-PKJ43CMD.js → chunk-NLOLF6LD.js} +2 -2
  41. package/dist/{chunk-7NTBVFGD.js → chunk-NOSRK3ZZ.js} +5 -5
  42. package/dist/{chunk-HN4FNA4X.js → chunk-OMB2ZKIT.js} +8 -8
  43. package/dist/{chunk-Y4MJQ2UG.js → chunk-QQ2YMQZU.js} +2 -2
  44. package/dist/{chunk-URHRIAV6.js → chunk-REDOOY2Q.js} +2 -2
  45. package/dist/chunk-REDOOY2Q.js.map +1 -0
  46. package/dist/{chunk-YCXICMOY.js → chunk-RJ7NN2UR.js} +2 -2
  47. package/dist/chunk-RTBJ5IOO.js +87 -0
  48. package/dist/chunk-RTBJ5IOO.js.map +1 -0
  49. package/dist/{chunk-KL7AS3HV.js → chunk-SMH6SRX7.js} +6 -3
  50. package/dist/chunk-SMH6SRX7.js.map +1 -0
  51. package/dist/{chunk-QHW35ZON.js → chunk-TGPST6FK.js} +2 -2
  52. package/dist/{chunk-THNJ3IKU.js → chunk-ULUMAZ5S.js} +2 -2
  53. package/dist/{chunk-E5OHKSBD.js → chunk-V2PNPF6J.js} +3 -3
  54. package/dist/{chunk-NO272T7Q.js → chunk-V4PXLYWM.js} +2 -31
  55. package/dist/chunk-V4PXLYWM.js.map +1 -0
  56. package/dist/{chunk-BERJ4AWW.js → chunk-VGXZT72I.js} +2 -2
  57. package/dist/{chunk-2Z47NMFK.js → chunk-WZW37ZHL.js} +2 -2
  58. package/dist/{chunk-SWVNZQVR.js → chunk-YPXNAP72.js} +2 -169
  59. package/dist/chunk-YPXNAP72.js.map +1 -0
  60. package/dist/classified/index.js +1 -2
  61. package/dist/{classified-marker-67HBIJMZ.js → classified-marker-OO65UYXN.js} +3 -5
  62. package/dist/{classified-marker-67HBIJMZ.js.map → classified-marker-OO65UYXN.js.map} +1 -1
  63. package/dist/{collection-facade-MGGHQ3HU.js → collection-facade-VOWW732S.js} +4 -4
  64. package/dist/{computed-LSEZYRJS.js → computed-VPOKQK4N.js} +1 -2
  65. package/dist/consent/index.js +0 -1
  66. package/dist/consent/index.js.map +1 -1
  67. package/dist/cover/index.js +0 -1
  68. package/dist/crdt/index.js +0 -1
  69. package/dist/crdt/index.js.map +1 -1
  70. package/dist/custody/index.js +1 -2
  71. package/dist/{dead-filter-M3NZUWBZ.js → dead-filter-OMMVBAKZ.js} +2 -3
  72. package/dist/{dead-filter-M3NZUWBZ.js.map → dead-filter-OMMVBAKZ.js.map} +1 -1
  73. package/dist/debug/index.js +0 -1
  74. package/dist/debug/index.js.map +1 -1
  75. package/dist/{delegation-OLJUWTVA.js → delegation-VRYMQRCF.js} +1 -2
  76. package/dist/{delegation-OLJUWTVA.js.map → delegation-VRYMQRCF.js.map} +1 -1
  77. package/dist/derivations/index.js +7 -4
  78. package/dist/{derive-LY47HG7Q.js → derive-HHBRFS2U.js} +2 -3
  79. package/dist/directory/index.js +0 -1
  80. package/dist/{dispatch-XEBHUGBV.js → dispatch-DC3LQJ3S.js} +14 -10
  81. package/dist/dispatch-DC3LQJ3S.js.map +1 -0
  82. package/dist/{dispatch-44J4TL72.js → dispatch-GGFBWQU7.js} +5 -7
  83. package/dist/{dispatch-44J4TL72.js.map → dispatch-GGFBWQU7.js.map} +1 -1
  84. package/dist/{enclave-4YBQK77G.js → enclave-VM2YQONI.js} +1 -2
  85. package/dist/{executor-TOLPBHOC.js → executor-5NIMLSKZ.js} +1 -2
  86. package/dist/{executor-KSPGNVDO.js → executor-5XJNV5H5.js} +4 -4
  87. package/dist/{executor-MZJXBWNM.js → executor-AQVXV5GZ.js} +1 -2
  88. package/dist/{export-accessible-LGV6IAFX.js → export-accessible-2LOBM67T.js} +3 -4
  89. package/dist/{extract-partition-RYJZIKQZ.js → extract-partition-AZGSEMIF.js} +3 -4
  90. package/dist/{fanout-sidecar-TSFPXY5C.js → fanout-sidecar-HJIVUPTD.js} +1 -2
  91. package/dist/{fanout-sidecar-TSFPXY5C.js.map → fanout-sidecar-HJIVUPTD.js.map} +1 -1
  92. package/dist/{fence-watcher-ZVSMLKQD.js → fence-watcher-U2PW6JU5.js} +1 -3
  93. package/dist/{fence-watcher-ZVSMLKQD.js.map → fence-watcher-U2PW6JU5.js.map} +1 -1
  94. package/dist/{find-LC7P63MX.js → find-2ABFCZX4.js} +1 -2
  95. package/dist/forget/index.js +0 -1
  96. package/dist/forget/index.js.map +1 -1
  97. package/dist/guards/index.js +0 -1
  98. package/dist/history/index.js +0 -1
  99. package/dist/history/index.js.map +1 -1
  100. package/dist/i18n/index.js +0 -1
  101. package/dist/i18n/index.js.map +1 -1
  102. package/dist/index.js +43 -40
  103. package/dist/index.js.map +1 -1
  104. package/dist/indexing/index.js +0 -1
  105. package/dist/indexing/index.js.map +1 -1
  106. package/dist/introspection/index.js +2 -3
  107. package/dist/{issue-IOXM3RSJ.js → issue-RH4H733Z.js} +1 -2
  108. package/dist/{json-schema-I22JCYCG.js → json-schema-V3M5MUCM.js} +1 -3
  109. package/dist/{json-schema-I22JCYCG.js.map → json-schema-V3M5MUCM.js.map} +1 -1
  110. package/dist/kernel/collection-config.d.ts +34 -0
  111. package/dist/lazy/index.js +3 -3
  112. package/dist/{ledger-HXHIOD36.js → ledger-454SU3VE.js} +1 -2
  113. package/dist/{liberate-4APIV7FR.js → liberate-AAZSP2BT.js} +5 -6
  114. package/dist/{link-set-6RAHEKZN.js → link-set-67CAMUAL.js} +1 -2
  115. package/dist/{managed-secret-P3JQ7ZG4.js → managed-secret-NHRYG7CD.js} +1 -2
  116. package/dist/materialized-views/index.js +5 -5
  117. package/dist/{migrate-cek-25KNTS5U.js → migrate-cek-5ENH64AA.js} +1 -2
  118. package/dist/{migrate-cek-25KNTS5U.js.map → migrate-cek-5ENH64AA.js.map} +1 -1
  119. package/dist/money/index.js +0 -1
  120. package/dist/{noydb-JJXPICIC.js → noydb-Y53CQHZK.js} +17 -15
  121. package/dist/overlay-views/index.js +0 -1
  122. package/dist/periods/index.js +1 -2
  123. package/dist/pod/index.js +18 -16
  124. package/dist/{pod-handle-6II7KFBR.js → pod-handle-S7AMCJIF.js} +2 -3
  125. package/dist/{pod-handle-6II7KFBR.js.map → pod-handle-S7AMCJIF.js.map} +1 -1
  126. package/dist/policy/index.js +0 -1
  127. package/dist/portability/index.js +6 -7
  128. package/dist/portability/index.js.map +1 -1
  129. package/dist/{post-register-BPJ36A6D.js → post-register-HIYY4Y5S.js} +4 -5
  130. package/dist/{post-register-BPJ36A6D.js.map → post-register-HIYY4Y5S.js.map} +1 -1
  131. package/dist/{purge-scope-FGNNLWHD.js → purge-scope-4HEFPDOW.js} +1 -3
  132. package/dist/{purge-scope-FGNNLWHD.js.map → purge-scope-4HEFPDOW.js.map} +1 -1
  133. package/dist/query/index.js +0 -1
  134. package/dist/{read-only-facade-CHCKSEPD.js → read-only-facade-BIGO37U3.js} +1 -2
  135. package/dist/reduce/index.js +0 -1
  136. package/dist/reduce/index.js.map +1 -1
  137. package/dist/{register-AEOOGKNO.js → register-7OJWAWT5.js} +3 -4
  138. package/dist/{registry-SKSOUXNF.js → registry-23WF3TGH.js} +1 -2
  139. package/dist/{registry-SHMGHEK4.js → registry-4AYVUNXK.js} +1 -2
  140. package/dist/{registry-I6FP4677.js → registry-5MVJGPXW.js} +1 -2
  141. package/dist/{registry-BZQIVNNO.js → registry-HDINK5QW.js} +8 -5
  142. package/dist/{request-withdrawal-6HJT4ODF.js → request-withdrawal-P5EQATZ7.js} +5 -6
  143. package/dist/{reveal-DDQBDWPM.js → reveal-IMGQ2EE5.js} +1 -2
  144. package/dist/{reveal-DDQBDWPM.js.map → reveal-IMGQ2EE5.js.map} +1 -1
  145. package/dist/{revoke-U7U3FMXU.js → revoke-POHP4GPB.js} +1 -2
  146. package/dist/satellites/index.js +0 -1
  147. package/dist/schema-update/index.js +0 -1
  148. package/dist/sealed-record/index.js +3 -4
  149. package/dist/sealed-record/index.js.map +1 -1
  150. package/dist/search/index.js +1 -2
  151. package/dist/{seed-3RXT4VGN.js → seed-UYR6YG3V.js} +1 -2
  152. package/dist/{seed-3RXT4VGN.js.map → seed-UYR6YG3V.js.map} +1 -1
  153. package/dist/sequence/index.js +0 -1
  154. package/dist/session/index.js +0 -1
  155. package/dist/session/index.js.map +1 -1
  156. package/dist/shadow/index.js +0 -1
  157. package/dist/shadow/index.js.map +1 -1
  158. package/dist/share-link/index.js +0 -1
  159. package/dist/share-link/index.js.map +1 -1
  160. package/dist/{signer-7NK7TMWU.js → signer-5K3WDHOC.js} +1 -2
  161. package/dist/snapshots/index.js +1 -2
  162. package/dist/snapshots/index.js.map +1 -1
  163. package/dist/{stale-KSPG6LFX.js → stale-AU74OPUW.js} +2 -3
  164. package/dist/{storage-WP4M7IFO.js → storage-AVJA5BTW.js} +1 -2
  165. package/dist/store/index.js +0 -1
  166. package/dist/{store-coordination-provider-O2VUEMJH.js → store-coordination-provider-RX2CGWXV.js} +1 -2
  167. package/dist/{store-coordination-provider-O2VUEMJH.js.map → store-coordination-provider-RX2CGWXV.js.map} +1 -1
  168. package/dist/sync/index.js +0 -1
  169. package/dist/sync/index.js.map +1 -1
  170. package/dist/team/index.js +0 -1
  171. package/dist/tiers/index.js +1 -2
  172. package/dist/to/index.js +0 -1
  173. package/dist/to/index.js.map +1 -1
  174. package/dist/transactions/index.js +2 -3
  175. package/dist/transactions/index.js.map +1 -1
  176. package/dist/{ulid-VQW6ERQN.js → ulid-4MKXG325.js} +1 -2
  177. package/dist/util/index.js +0 -1
  178. package/dist/util/index.js.map +1 -1
  179. package/dist/{vault-diff-FE5J35NJ.js → vault-diff-C6WUJFNP.js} +1 -2
  180. package/dist/vault-head/index.js +0 -1
  181. package/dist/vault-head/index.js.map +1 -1
  182. package/dist/{verify-JBXCAGDL.js → verify-KOV2G5D6.js} +1 -2
  183. package/dist/{verify-JBXCAGDL.js.map → verify-KOV2G5D6.js.map} +1 -1
  184. package/dist/{walk-P3E6QSNC.js → walk-ERAJ5RHO.js} +3 -4
  185. package/dist/with-formula/derivations/registry.d.ts +6 -1
  186. package/dist/with-shape/introspection/describe.d.ts +2 -33
  187. package/dist/with-shape/introspection/field-meta.d.ts +86 -0
  188. package/dist/{withdraw-accessible-6Q7NE4EY.js → withdraw-accessible-42D32Z7G.js} +4 -5
  189. package/package.json +7 -3
  190. package/dist/chunk-7BE7ZDVQ.js +0 -722
  191. package/dist/chunk-7BE7ZDVQ.js.map +0 -1
  192. package/dist/chunk-H7XCIM3Z.js.map +0 -1
  193. package/dist/chunk-KL7AS3HV.js.map +0 -1
  194. package/dist/chunk-NO272T7Q.js.map +0 -1
  195. package/dist/chunk-PZ5AY32C.js +0 -10
  196. package/dist/chunk-SWVNZQVR.js.map +0 -1
  197. package/dist/chunk-URHRIAV6.js.map +0 -1
  198. package/dist/chunk-WQF3QABE.js.map +0 -1
  199. package/dist/chunk-XMF3GKUR.js.map +0 -1
  200. package/dist/dispatch-XEBHUGBV.js.map +0 -1
  201. package/dist/withdraw-accessible-6Q7NE4EY.js.map +0 -1
  202. package/dist/zod-HAJM7LMS.js +0 -14763
  203. package/dist/zod-HAJM7LMS.js.map +0 -1
  204. /package/dist/{api-UE2XQKDN.js.map → api-JPZT6BDD.js.map} +0 -0
  205. /package/dist/{chunk-5FUULQ6F.js.map → chunk-2HVILGVR.js.map} +0 -0
  206. /package/dist/{chunk-DJ3LNNZQ.js.map → chunk-3LK2XZCW.js.map} +0 -0
  207. /package/dist/{chunk-E3EBMC4G.js.map → chunk-AMODH7AU.js.map} +0 -0
  208. /package/dist/{chunk-B2ZSGYSF.js.map → chunk-B2KCRDJM.js.map} +0 -0
  209. /package/dist/{chunk-TO3EQ7HF.js.map → chunk-BLN54W3L.js.map} +0 -0
  210. /package/dist/{chunk-ZR2YMODS.js.map → chunk-EKZ6VZYF.js.map} +0 -0
  211. /package/dist/{chunk-RTYJRFQO.js.map → chunk-EWPRPP3M.js.map} +0 -0
  212. /package/dist/{chunk-JJR2JIN4.js.map → chunk-FFBYRWDJ.js.map} +0 -0
  213. /package/dist/{chunk-VGICZPVE.js.map → chunk-FUJSIP4K.js.map} +0 -0
  214. /package/dist/{chunk-PKJ43CMD.js.map → chunk-NLOLF6LD.js.map} +0 -0
  215. /package/dist/{chunk-7NTBVFGD.js.map → chunk-NOSRK3ZZ.js.map} +0 -0
  216. /package/dist/{chunk-HN4FNA4X.js.map → chunk-OMB2ZKIT.js.map} +0 -0
  217. /package/dist/{chunk-Y4MJQ2UG.js.map → chunk-QQ2YMQZU.js.map} +0 -0
  218. /package/dist/{chunk-YCXICMOY.js.map → chunk-RJ7NN2UR.js.map} +0 -0
  219. /package/dist/{chunk-QHW35ZON.js.map → chunk-TGPST6FK.js.map} +0 -0
  220. /package/dist/{chunk-THNJ3IKU.js.map → chunk-ULUMAZ5S.js.map} +0 -0
  221. /package/dist/{chunk-E5OHKSBD.js.map → chunk-V2PNPF6J.js.map} +0 -0
  222. /package/dist/{chunk-BERJ4AWW.js.map → chunk-VGXZT72I.js.map} +0 -0
  223. /package/dist/{chunk-2Z47NMFK.js.map → chunk-WZW37ZHL.js.map} +0 -0
  224. /package/dist/{chunk-PZ5AY32C.js.map → collection-facade-VOWW732S.js.map} +0 -0
  225. /package/dist/{collection-facade-MGGHQ3HU.js.map → computed-VPOKQK4N.js.map} +0 -0
  226. /package/dist/{computed-LSEZYRJS.js.map → derive-HHBRFS2U.js.map} +0 -0
  227. /package/dist/{derive-LY47HG7Q.js.map → enclave-VM2YQONI.js.map} +0 -0
  228. /package/dist/{enclave-4YBQK77G.js.map → executor-5NIMLSKZ.js.map} +0 -0
  229. /package/dist/{executor-KSPGNVDO.js.map → executor-5XJNV5H5.js.map} +0 -0
  230. /package/dist/{executor-MZJXBWNM.js.map → executor-AQVXV5GZ.js.map} +0 -0
  231. /package/dist/{executor-TOLPBHOC.js.map → export-accessible-2LOBM67T.js.map} +0 -0
  232. /package/dist/{export-accessible-LGV6IAFX.js.map → extract-partition-AZGSEMIF.js.map} +0 -0
  233. /package/dist/{extract-partition-RYJZIKQZ.js.map → find-2ABFCZX4.js.map} +0 -0
  234. /package/dist/{find-LC7P63MX.js.map → issue-RH4H733Z.js.map} +0 -0
  235. /package/dist/{issue-IOXM3RSJ.js.map → ledger-454SU3VE.js.map} +0 -0
  236. /package/dist/{ledger-HXHIOD36.js.map → liberate-AAZSP2BT.js.map} +0 -0
  237. /package/dist/{liberate-4APIV7FR.js.map → link-set-67CAMUAL.js.map} +0 -0
  238. /package/dist/{link-set-6RAHEKZN.js.map → managed-secret-NHRYG7CD.js.map} +0 -0
  239. /package/dist/{managed-secret-P3JQ7ZG4.js.map → noydb-Y53CQHZK.js.map} +0 -0
  240. /package/dist/{noydb-JJXPICIC.js.map → read-only-facade-BIGO37U3.js.map} +0 -0
  241. /package/dist/{read-only-facade-CHCKSEPD.js.map → register-7OJWAWT5.js.map} +0 -0
  242. /package/dist/{register-AEOOGKNO.js.map → registry-23WF3TGH.js.map} +0 -0
  243. /package/dist/{registry-BZQIVNNO.js.map → registry-4AYVUNXK.js.map} +0 -0
  244. /package/dist/{registry-I6FP4677.js.map → registry-5MVJGPXW.js.map} +0 -0
  245. /package/dist/{registry-SHMGHEK4.js.map → registry-HDINK5QW.js.map} +0 -0
  246. /package/dist/{registry-SKSOUXNF.js.map → request-withdrawal-P5EQATZ7.js.map} +0 -0
  247. /package/dist/{request-withdrawal-6HJT4ODF.js.map → revoke-POHP4GPB.js.map} +0 -0
  248. /package/dist/{revoke-U7U3FMXU.js.map → signer-5K3WDHOC.js.map} +0 -0
  249. /package/dist/{signer-7NK7TMWU.js.map → stale-AU74OPUW.js.map} +0 -0
  250. /package/dist/{stale-KSPG6LFX.js.map → storage-AVJA5BTW.js.map} +0 -0
  251. /package/dist/{storage-WP4M7IFO.js.map → ulid-4MKXG325.js.map} +0 -0
  252. /package/dist/{ulid-VQW6ERQN.js.map → vault-diff-C6WUJFNP.js.map} +0 -0
  253. /package/dist/{vault-diff-FE5J35NJ.js.map → walk-ERAJ5RHO.js.map} +0 -0
  254. /package/dist/{walk-P3E6QSNC.js.map → withdraw-accessible-42D32Z7G.js.map} +0 -0
@@ -3,8 +3,9 @@ import {
3
3
  } from "./chunk-TPHAGLUH.js";
4
4
  import {
5
5
  ViaGraph,
6
+ matchTargetFieldNames,
6
7
  schemaFieldKeys
7
- } from "./chunk-7BE7ZDVQ.js";
8
+ } from "./chunk-4SZMZAYN.js";
8
9
  import {
9
10
  sha256Hex
10
11
  } from "./chunk-SL3JAAKN.js";
@@ -99,34 +100,38 @@ var DerivationRegistry = class {
99
100
  * own strategies rather than taken as a parameter — a field this
100
101
  * collection's own derivations write via `denorm` is never a typo.
101
102
  */
102
- validateFieldsFor(collectionName, schema, configKeys) {
103
+ validateFieldsFor(collectionName, schema, configKeys, viaSources = void 0) {
104
+ const targets = matchTargetFieldNames(viaSources ?? {});
105
+ const virtual = new Set(targets.virtual);
103
106
  const shapeKeys = schemaFieldKeys(schema);
104
- if (shapeKeys === void 0) return;
105
- const keys = /* @__PURE__ */ new Set([...shapeKeys, ...configKeys]);
107
+ if (shapeKeys === void 0 && virtual.size === 0) return;
108
+ const keys = shapeKeys === void 0 ? void 0 : /* @__PURE__ */ new Set([...shapeKeys, ...configKeys, ...targets.declared]);
106
109
  const denormExempt = /* @__PURE__ */ new Set();
107
- for (const reg of new Set([...this._bySource.values()].flat())) {
110
+ const regs = new Set([...this._bySource.values()].flat());
111
+ for (const reg of regs) {
108
112
  if (reg.spec.source !== collectionName) continue;
109
113
  for (const out of Object.values(reg.spec.outputs)) {
110
114
  if (out.collection === collectionName) for (const d of out.denorm ?? []) denormExempt.add(d);
111
115
  }
112
116
  }
113
- const regs = new Set([...this._bySource.values()].flat());
117
+ const unknown = (f) => keys !== void 0 && !keys.has(f) && !denormExempt.has(f);
114
118
  for (const reg of regs) {
119
+ const name = reg.spec.name ?? reg.spec.source;
115
120
  for (const t of reg.triggers) {
116
- if (reg.spec.source === collectionName) {
117
- for (const p of t.match) {
118
- if (!keys.has(p.to) && !denormExempt.has(p.to)) {
121
+ for (const p of t.match) {
122
+ if (reg.spec.source === collectionName) {
123
+ if (virtual.has(p.to)) throw new ValidationError(this._virtualMatchMessage(name, collectionName, p.to, "to"));
124
+ if (unknown(p.to)) {
119
125
  throw new ValidationError(
120
- `derivation "${reg.spec.name ?? reg.spec.source}": triggerBy match names source field "${p.to}", which "${collectionName}" does not declare \u2014 a typo here silently matches nothing forever`
126
+ `derivation "${name}": triggerBy match names source field "${p.to}", which "${collectionName}" does not declare \u2014 a typo here silently matches nothing forever`
121
127
  );
122
128
  }
123
129
  }
124
- }
125
- if (t.collection === collectionName) {
126
- for (const p of t.match) {
127
- if (p.from !== "id" && !keys.has(p.from) && !denormExempt.has(p.from)) {
130
+ if (t.collection === collectionName && p.from !== "id") {
131
+ if (virtual.has(p.from)) throw new ValidationError(this._virtualMatchMessage(name, collectionName, p.from, "from"));
132
+ if (unknown(p.from)) {
128
133
  throw new ValidationError(
129
- `derivation "${reg.spec.name ?? reg.spec.source}": triggerBy match reads "${p.from}" from written "${collectionName}" records, which that collection does not declare \u2014 a typo here silently matches nothing forever`
134
+ `derivation "${name}": triggerBy match reads "${p.from}" from written "${collectionName}" records, which that collection does not declare \u2014 a typo here silently matches nothing forever`
130
135
  );
131
136
  }
132
137
  }
@@ -134,6 +139,11 @@ var DerivationRegistry = class {
134
139
  }
135
140
  }
136
141
  }
142
+ /** #1266 — one message shape for both match sides; see {@link matchTargetFieldNames}. */
143
+ _virtualMatchMessage(name, collectionName, field, side) {
144
+ const where = side === "to" ? `names source field "${field}", which "${collectionName}"` : `reads "${field}" from written "${collectionName}" records, which that collection`;
145
+ return `derivation "${name}": triggerBy match ${where} declares as a VIRTUAL computed field \u2014 virtual fields are evaluated on read and never stored, so the matcher would read every candidate record and match nothing, forever. Declare it as \`mode: 'materialized'\` (stored, indexable) or match on a stored field.`;
146
+ }
137
147
  /**
138
148
  * All registered strategies as a flat, deduplicated array.
139
149
  * Each strategy is indexed once per source (not once per output key),
@@ -225,4 +235,4 @@ var DerivationRegistry = class {
225
235
  export {
226
236
  DerivationRegistry
227
237
  };
228
- //# sourceMappingURL=chunk-H7XCIM3Z.js.map
238
+ //# sourceMappingURL=chunk-EKVNVJVV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-formula/derivations/strategy-hash.ts","../src/with-formula/derivations/registry.ts"],"sourcesContent":["/**\n * Deterministic hash of a derivation strategy's \"shape\": source\n * collection, declared sibling sources, output keys, derive function\n * source. Used to detect\n * strategy drift: a record whose `_derivedFrom.strategyHash` doesn't\n * match the current strategy is considered stale.\n *\n * Web Crypto SHA-256 — no extra deps.\n */\nimport { sha256Hex } from '../../kernel/enclave/index.js'\n\nexport async function computeStrategyHash(\n source: string,\n outputKeys: readonly string[],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n derive: (...args: any[]) => any,\n sources?: ReadonlyArray<string>,\n): Promise<string> {\n const canonical = JSON.stringify({\n source,\n outputs: [...outputKeys].sort(),\n derive: derive.toString(),\n // Declared sibling sources — adding/removing a trigger\n // collection invalidates cached derived records. Omitted when empty\n // so strategies without siblings keep their existing hash.\n ...(sources?.length ? { sources: [...sources].sort() } : {}),\n })\n const bytes = new TextEncoder().encode(canonical)\n return sha256Hex(bytes)\n}\n","import { matchTargetFieldNames } from '../../kernel/collection-config.js'\nimport { DerivationCycleError, DuplicateBehaviorNameError, ValidationError } from '../../kernel/errors.js'\nimport { ViaGraph, type FieldRef, type EdgeKind, type Grain } from '../../kernel/via/graph.js'\nimport { schemaFieldKeys } from '../../with-shape/introspection/describe.js'\nimport { computeStrategyHash } from './strategy-hash.js'\nimport { normalizeTriggerBy, type NormalizedTrigger } from './trigger-match.js'\nimport type { DerivationSpec } from './types.js'\n\n/**\n * Whole-record artifact-grain field marker (#638 Task 2). A derivation's\n * `derive()`/a rollup's `compute()` reads the WHOLE source record — there is\n * no single declared field to point at — so a trigger/output collection is\n * modelled as one artifact node per collection (`kernel/via/graph.ts`'s\n * \"Artifact-grain targets... modelled as a field node whose field is the\n * artifact key\" convention). MUST match `materialized-views/registry.ts`'s\n * and `vault.ts`'s overlay-edge marker so cross-registry edges (a derivation\n * feeding into / out of an MV or overlay collection) resolve to the SAME\n * graph node.\n */\nconst WHOLE_RECORD = '*'\n\n/** Strip this registry's `.${WHOLE_RECORD}` artifact suffix off a graph cycle\n * path entry, recovering the bare collection name — preserves the exact\n * pre-#638 `DerivationCycleError` message shape (behavior lock) for a pure\n * record-shape derivation cycle. A rollup's real-field target (not suffixed\n * `.${WHOLE_RECORD}`) is left untouched. */\nfunction stripArtifactSuffix(displayId: string): string {\n const suffix = `.${WHOLE_RECORD}`\n return displayId.endsWith(suffix) ? displayId.slice(0, -suffix.length) : displayId\n}\n\ninterface RegisteredStrategy {\n // Type-erased to allow the registry to hold heterogeneous strategies.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n spec: DerivationSpec<any, any>\n strategyHash: string\n readonly triggers: ReadonlyArray<NormalizedTrigger>\n}\n\n/**\n * Vault-internal registry of derivation strategies. Owned by `Vault`;\n * not exported.\n *\n * @internal\n */\nexport class DerivationRegistry {\n private readonly _bySource = new Map<string, RegisteredStrategy[]>()\n private readonly _byOutput = new Map<string, RegisteredStrategy[]>()\n private readonly _byName = new Map<string, RegisteredStrategy>()\n\n /**\n * Register a derivation strategy. If `spec.name` is given and already\n * registered by another derivation in this vault, throws\n * {@link DuplicateBehaviorNameError} before indexing.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async register(spec: DerivationSpec<any, any>): Promise<void> {\n if (spec.name !== undefined && this._byName.has(spec.name)) {\n throw new DuplicateBehaviorNameError(spec.name, 'derivation')\n }\n\n const outputKeys = Object.keys(spec.outputs)\n const strategyHash = await computeStrategyHash(spec.source, outputKeys, spec.derive, spec.sources)\n const triggers = normalizeTriggerBy(spec.triggerBy)\n const reg: RegisteredStrategy = { spec, strategyHash, triggers }\n\n if (spec.name !== undefined) this._byName.set(spec.name, reg)\n\n const fromSource = this._bySource.get(spec.source)\n if (fromSource) fromSource.push(reg)\n else this._bySource.set(spec.source, [reg])\n\n // Declared sibling sources index the SAME `reg` under each\n // extra collection so `strategiesForSource(extra)` returns it and a\n // sibling write re-fires the derivation. Sibling keys also enter\n // `_bySource`, so `validate()`'s cycle DFS walks them automatically.\n for (const extra of spec.sources ?? []) {\n const fromExtra = this._bySource.get(extra)\n if (fromExtra) fromExtra.push(reg)\n else this._bySource.set(extra, [reg])\n }\n\n // FK triggers index the SAME `reg` under each parent collection so\n // a parent write re-fires the derivation (fanned out to matching source\n // records in `dispatchDerivations`). Like sources[], these keys enter\n // `_bySource` so the cycle DFS walks the trigger→output edge.\n for (const t of triggers) {\n const fromTrigger = this._bySource.get(t.collection)\n if (fromTrigger) fromTrigger.push(reg)\n else this._bySource.set(t.collection, [reg])\n }\n\n // Rollup: a write to the child `from` collection recomputes\n // the parent (= spec.source = into) at id child[key]. Index under `from`\n // so a child write fires it; spec.source (into) is already indexed above,\n // so a parent write also recomputes its own aggregate (covers the\n // parent-created-after-children case). The from→into edge enters the cycle\n // DFS automatically.\n if (spec.rollup) {\n const fromRollup = this._bySource.get(spec.rollup.from)\n if (fromRollup) fromRollup.push(reg)\n else this._bySource.set(spec.rollup.from, [reg])\n }\n\n for (const key of outputKeys) {\n const output = spec.outputs[key]\n if (!output) continue\n const outputCollection = output.collection\n const arr = this._byOutput.get(outputCollection)\n if (arr) arr.push(reg)\n else this._byOutput.set(outputCollection, [reg])\n }\n }\n\n strategiesForSource(source: string): ReadonlyArray<RegisteredStrategy> {\n return this._bySource.get(source) ?? []\n }\n\n strategiesProducingOutput(collection: string): ReadonlyArray<RegisteredStrategy> {\n return this._byOutput.get(collection) ?? []\n }\n\n /**\n * The #1253-pattern typo guard for match fields (#1249): a misspelt\n * `to`/`from` silently matches nothing forever, so validate against the\n * collection's enumerable field set at the earliest point it exists.\n * `schemaFieldKeys(schema) === undefined` (TS-generic collection,\n * unreadable validator) is DELIBERATELY silent — those fields are real\n * and unenumerable. `configKeys` folds in the collection's other\n * non-schema field declarations (fieldMeta/moneyFields/dictKeyFields/\n * refs/computed); `denormExempt` is derived here from this registry's\n * own strategies rather than taken as a parameter — a field this\n * collection's own derivations write via `denorm` is never a typo.\n */\n validateFieldsFor(\n collectionName: string,\n schema: unknown,\n configKeys: ReadonlyArray<string>,\n viaSources: {\n readonly computed?: Readonly<Record<string, unknown>> | undefined\n readonly viaFields?: Readonly<Record<string, unknown>> | undefined\n } | undefined = undefined,\n ): void {\n const targets = matchTargetFieldNames(viaSources ?? {})\n const virtual = new Set(targets.virtual)\n const shapeKeys = schemaFieldKeys(schema)\n // The two checks have DIFFERENT preconditions, which is why one loop runs\n // both rather than an early return covering both. The typo check needs a\n // readable field list and must stay silent without one — a false \"unknown\n // field\" on a TS-generic collection would be worse than no check. The\n // virtual check needs nothing but the declaration in hand: \"declared, and\n // never stored\" is provable from the declaration alone (#1266).\n if (shapeKeys === undefined && virtual.size === 0) return\n const keys = shapeKeys === undefined ? undefined\n : new Set([...shapeKeys, ...configKeys, ...targets.declared])\n const denormExempt = new Set<string>()\n const regs = new Set([...this._bySource.values()].flat())\n for (const reg of regs) {\n if (reg.spec.source !== collectionName) continue\n for (const out of Object.values(reg.spec.outputs) as Array<{ collection?: string; denorm?: readonly string[] }>) {\n if (out.collection === collectionName) for (const d of out.denorm ?? []) denormExempt.add(d)\n }\n }\n const unknown = (f: string): boolean =>\n keys !== undefined && !keys.has(f) && !denormExempt.has(f)\n for (const reg of regs) {\n const name = reg.spec.name ?? reg.spec.source\n for (const t of reg.triggers) {\n for (const p of t.match) {\n // `to` reads the SOURCE record; `from` reads the WRITTEN record.\n // Both are the stored shape, so both refuse a virtual target.\n if (reg.spec.source === collectionName) {\n if (virtual.has(p.to)) throw new ValidationError(this._virtualMatchMessage(name, collectionName, p.to, 'to'))\n if (unknown(p.to)) {\n throw new ValidationError(\n `derivation \"${name}\": triggerBy match names source field \"${p.to}\", which \"${collectionName}\" does not declare — a typo here silently matches nothing forever`)\n }\n }\n if (t.collection === collectionName && p.from !== 'id') {\n if (virtual.has(p.from)) throw new ValidationError(this._virtualMatchMessage(name, collectionName, p.from, 'from'))\n if (unknown(p.from)) {\n throw new ValidationError(\n `derivation \"${name}\": triggerBy match reads \"${p.from}\" from written \"${collectionName}\" records, which that collection does not declare — a typo here silently matches nothing forever`)\n }\n }\n }\n }\n }\n }\n\n /** #1266 — one message shape for both match sides; see {@link matchTargetFieldNames}. */\n private _virtualMatchMessage(name: string, collectionName: string, field: string, side: 'to' | 'from'): string {\n const where = side === 'to'\n ? `names source field \"${field}\", which \"${collectionName}\"`\n : `reads \"${field}\" from written \"${collectionName}\" records, which that collection`\n return `derivation \"${name}\": triggerBy match ${where} declares as a VIRTUAL computed field — virtual fields are evaluated on read and never stored, so the matcher would read every candidate record and match nothing, forever. Declare it as \\`mode: 'materialized'\\` (stored, indexable) or match on a stored field.`\n }\n\n /**\n * All registered strategies as a flat, deduplicated array.\n * Each strategy is indexed once per source (not once per output key),\n * so iterating `_bySource.values()` naturally yields each strategy\n * exactly once per source — deduplication is handled by flattening\n * the per-source arrays and collecting into a Set by identity.\n *\n * Used by `dumpSchema()` / `describeDerivations()` in the introspection\n * walker to populate the derivations map.\n */\n all(): ReadonlyArray<RegisteredStrategy> {\n const seen = new Set<RegisteredStrategy>()\n for (const strategies of this._bySource.values()) {\n for (const s of strategies) seen.add(s)\n }\n return [...seen]\n }\n\n /**\n * Graph edges for #638 Task 2: one `'derivation'`/`'record'` edge per\n * (non-self-write) output key — target = the output collection (a\n * `WHOLE_RECORD` artifact node), sources = every collection that can\n * trigger this strategy (`source`, `sources[]`, `triggerBy[].collection`,\n * and — for a rollup — `rollup.from`), mirroring EXACTLY the trigger keys\n * `register()` indexes under `_bySource`. The self-write reverse-denorm\n * output (`output.collection === source` with `denorm` declared) is\n * skipped — the SAME condition the old local DFS used; it is not a cycle\n * (see the comment `validate()` used to carry, now on this skip).\n * Rollups ADDITIONALLY get a dedicated `'rollup'`/`'aggregate'` edge\n * targeting the real `rollup.field` on the parent (`source`) — the\n * self-write output above only covers the cycle-DFS skip, not\n * dispatch/taint (Tasks 3/4/6), which need the real field.\n */\n edges(): ReadonlyArray<{ readonly target: FieldRef; readonly sources: readonly FieldRef[]; readonly kind: EdgeKind; readonly grain: Grain }> {\n const out: Array<{ target: FieldRef; sources: FieldRef[]; kind: EdgeKind; grain: Grain }> = []\n for (const reg of this.all()) {\n const spec = reg.spec\n const triggerCollections = [\n spec.source,\n ...(spec.sources ?? []),\n ...(spec.triggerBy ?? []).map((t) => t.collection),\n ...(spec.rollup ? [spec.rollup.from] : []),\n ]\n const sources: FieldRef[] = triggerCollections.map((c) => ({ collection: c, field: WHOLE_RECORD }))\n for (const key of Object.keys(spec.outputs)) {\n const output = spec.outputs[key]\n if (!output) continue\n if (output.shape === 'record' && output.collection === spec.source && output.denorm !== undefined) continue\n out.push({ target: { collection: output.collection, field: WHOLE_RECORD }, sources, kind: 'derivation', grain: 'record' })\n }\n if (spec.rollup) {\n out.push({\n target: { collection: spec.source, field: spec.rollup.field },\n sources: [{ collection: spec.rollup.from, field: WHOLE_RECORD }],\n kind: 'rollup',\n grain: 'aggregate',\n })\n }\n }\n return out\n }\n\n /**\n * Cycle detection, delegated to `ViaGraph.assertAcyclic()` (#638 Task 2 —\n * retires the local DFS). Call after all `register()` calls complete (i.e.\n * at vault open). Throws `DerivationCycleError` on the first cycle found —\n * SAME class/timing as before; the message/path is byte-identical to the\n * pre-#638 shape for a pure record-shape derivation cycle (the\n * `WHOLE_RECORD` artifact suffix is stripped back to a bare collection\n * name before re-throwing).\n *\n * `graph` is the caller's shared per-vault graph, ALREADY carrying this\n * registry's `edges()` (the caller registers them — see `Vault._initDerivations`)\n * — omit it (e.g. this registry's own unit tests) to validate this\n * registry's edges in isolation against a throwaway graph.\n */\n validate(graph?: ViaGraph): void {\n const g = graph ?? new ViaGraph()\n if (!graph) {\n for (const edge of this.edges()) g.registerDerived(edge.target, edge.sources, edge.kind, edge.grain)\n }\n try {\n g.assertAcyclic()\n } catch (e) {\n if (e instanceof DerivationCycleError) throw new DerivationCycleError(e.path.map(stripArtifactSuffix))\n throw e\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  writePod
3
- } from "./chunk-PKJ43CMD.js";
3
+ } from "./chunk-NLOLF6LD.js";
4
4
 
5
5
  // src/with-audit/portability/export-accessible.ts
6
6
  function resolveAccessibleCollections(keyring, scope, writableOnly = false) {
@@ -42,4 +42,4 @@ export {
42
42
  buildAccessibleBundle,
43
43
  exportAccessibleData
44
44
  };
45
- //# sourceMappingURL=chunk-ZR2YMODS.js.map
45
+ //# sourceMappingURL=chunk-EKZ6VZYF.js.map
@@ -435,4 +435,4 @@ export {
435
435
  demote,
436
436
  classifySealedShred
437
437
  };
438
- //# sourceMappingURL=chunk-RTYJRFQO.js.map
438
+ //# sourceMappingURL=chunk-EWPRPP3M.js.map
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-VXANTWBJ.js";
11
11
  import {
12
12
  putDerivedOutput
13
- } from "./chunk-SWVNZQVR.js";
13
+ } from "./chunk-MGOXJIBV.js";
14
14
  import {
15
15
  DEFAULT_JOIN_MAX_ROWS
16
16
  } from "./chunk-ZORTV6NH.js";
@@ -342,4 +342,4 @@ async function listOutputIds(outputColl) {
342
342
  export {
343
343
  MaterializedViewExecutor
344
344
  };
345
- //# sourceMappingURL=chunk-JJR2JIN4.js.map
345
+ //# sourceMappingURL=chunk-FFBYRWDJ.js.map
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-VYYM3MEN.js";
7
7
  import {
8
8
  derivePersistedSchema
9
- } from "./chunk-URHRIAV6.js";
9
+ } from "./chunk-REDOOY2Q.js";
10
10
  import {
11
11
  loadPersistedSchema
12
12
  } from "./chunk-WWNYSV7S.js";
@@ -240,4 +240,4 @@ function summariseAggregateOp(value) {
240
240
  export {
241
241
  dumpVaultSchema
242
242
  };
243
- //# sourceMappingURL=chunk-VGICZPVE.js.map
243
+ //# sourceMappingURL=chunk-FUJSIP4K.js.map
@@ -0,0 +1,176 @@
1
+ import {
2
+ matchesReferencingValue
3
+ } from "./chunk-YPXNAP72.js";
4
+ import {
5
+ PeriodClosedError
6
+ } from "./chunk-7O3Y4PPD.js";
7
+
8
+ // src/kernel/via/dispatch.ts
9
+ function touchFor(batch, collection) {
10
+ let t = batch.get(collection);
11
+ if (!t) {
12
+ t = { puts: /* @__PURE__ */ new Set(), deletes: /* @__PURE__ */ new Map() };
13
+ batch.set(collection, t);
14
+ }
15
+ return t;
16
+ }
17
+ function resolveRollupDeleteIntents(registry, collectionName, deleted) {
18
+ if (!registry) return [];
19
+ const intents = [];
20
+ for (const { spec } of registry.strategiesForSource(collectionName)) {
21
+ if (!spec.rollup || spec.rollup.from !== collectionName) continue;
22
+ const kv = deleted[spec.rollup.key];
23
+ if (typeof kv === "string" || typeof kv === "number") intents.push({ into: spec.source, parentId: String(kv), field: spec.rollup.field });
24
+ }
25
+ return intents;
26
+ }
27
+ function findRollupSpecForIntent(registry, collectionName, intent) {
28
+ return registry?.strategiesForSource(collectionName).find((s) => s.spec.source === intent.into && s.spec.rollup?.field === intent.field && s.spec.rollup?.from === collectionName)?.spec;
29
+ }
30
+ var WaveContext = class {
31
+ _seen = /* @__PURE__ */ new Set();
32
+ seen(targetKey) {
33
+ if (this._seen.has(targetKey)) return true;
34
+ this._seen.add(targetKey);
35
+ return false;
36
+ }
37
+ };
38
+ async function runGraphDispatchWave(vault, batch) {
39
+ const wave = new WaveContext();
40
+ for (const [collectionName, touch] of batch) {
41
+ if (vault.graph.dependentsOf(collectionName).length === 0) continue;
42
+ const coll = vault._getCollection(collectionName);
43
+ if (!coll) continue;
44
+ for (const id of touch.puts) {
45
+ try {
46
+ const stored = await coll._getStoredRecordForDispatch(id);
47
+ if (!stored) continue;
48
+ await coll.dispatchDerivations(id, stored.record, stored.version, wave);
49
+ await coll.dispatchMaterializedViews(id, stored.record, wave);
50
+ } catch (err) {
51
+ console.warn(`[via-dispatch] wave recompute failed for ${collectionName}/${id}:`, err);
52
+ vault._emit("derivation:wave-error", { collection: collectionName, id, error: err });
53
+ }
54
+ }
55
+ for (const [id, intents] of touch.deletes) {
56
+ try {
57
+ await coll._recomputeDeletedRollups(intents, wave);
58
+ await coll.dispatchMaterializedViewsOnDelete(id);
59
+ await coll.dispatchArrayDerivationsOnDelete(id);
60
+ } catch (err) {
61
+ console.warn(`[via-dispatch] wave delete-recompute failed for ${collectionName}/${id}:`, err);
62
+ vault._emit("derivation:wave-error", { collection: collectionName, id, error: err });
63
+ }
64
+ }
65
+ }
66
+ }
67
+ async function putDerivedOutput(outColl, id, value, ctx, options) {
68
+ try {
69
+ await outColl.put(id, value, options);
70
+ return "written";
71
+ } catch (err) {
72
+ if (!(err instanceof PeriodClosedError)) throw err;
73
+ const targetCollection = outColl.name;
74
+ const event = {
75
+ source: ctx.source,
76
+ target: { collection: targetCollection, id },
77
+ period: err.periodName,
78
+ endDate: err.endDate
79
+ };
80
+ ctx.emit("derivation:skipped-frozen", event);
81
+ if (ctx.audit) await ctx.audit(event);
82
+ return "skipped-frozen";
83
+ }
84
+ }
85
+ function ledgerAuditHook(ledger, actor) {
86
+ if (!ledger) return void 0;
87
+ return async (e) => {
88
+ await ledger.append({
89
+ op: "lifecycle",
90
+ collection: e.target.collection,
91
+ id: e.target.id,
92
+ version: 0,
93
+ actor,
94
+ payloadHash: "",
95
+ reason: JSON.stringify({ event: "derivation-skipped-frozen", ...e })
96
+ });
97
+ };
98
+ }
99
+ async function forgetDerivedFanout(vault, ref, envelope, stats, lookupCompareKeys) {
100
+ const coll = vault._getCollection(ref.collection);
101
+ if (coll) {
102
+ const mv = await coll.dispatchMaterializedViewsOnDelete(ref.id);
103
+ stats.recordsErased += mv.deleted;
104
+ stats.derivedResidueUndecodable.push(...mv.residueUndecodable);
105
+ stats.derivedResidueDeclined.push(...mv.residueDeclined);
106
+ }
107
+ const edges = vault.graph.derivedArtifactsOf(ref.collection);
108
+ if (edges.length === 0 || !coll) return;
109
+ if (edges.some((e) => e.kind === "ref")) {
110
+ const { cascaded, nullified, residue } = await applyLookupRefsFanout(vault, ref.collection, ref.id, lookupCompareKeys);
111
+ stats.lookupReferencesCascaded += cascaded;
112
+ stats.lookupReferencesNullified += nullified;
113
+ stats.lookupReferencesResidue.push(...residue);
114
+ }
115
+ if (edges.some((e) => e.kind === "derivation")) {
116
+ stats.recordsErased += await coll.dispatchArrayDerivationsOnDelete(ref.id, true);
117
+ }
118
+ if (envelope && edges.some((e) => e.kind === "rollup")) {
119
+ const priorRecord = await coll._decodeEnvelope(envelope, ref.id);
120
+ if (priorRecord) {
121
+ for (const r of await coll.dispatchRollupsOnDelete(ref.id, priorRecord)) {
122
+ if (r.outcome === "written") stats.aggregatesRecomputed += 1;
123
+ else if (r.outcome === "skipped-frozen") stats.residueFrozen.push(`${r.into}:${r.parentId}`);
124
+ }
125
+ }
126
+ }
127
+ }
128
+ async function applyLookupRefsFanout(vault, backing, key, compareKeys) {
129
+ let cascaded = 0;
130
+ let nullified = 0;
131
+ const residue = [];
132
+ for (const { referencing, onDelete, keyField } of vault.graph.referencingEdgesOf(backing)) {
133
+ if (onDelete === "restrict") continue;
134
+ const compareKey = keyField === "id" ? key : compareKeys.get(keyField);
135
+ if (compareKey === void 0) {
136
+ residue.push(`${backing}:${key}:${referencing.collection}.${referencing.field}`);
137
+ continue;
138
+ }
139
+ const coll = vault._getCollection(referencing.collection);
140
+ if (!coll) continue;
141
+ const matches = (await coll.list()).filter((rec) => matchesReferencingValue(rec, referencing.field, compareKey));
142
+ for (const rec of matches) {
143
+ const id = rec["id"];
144
+ if (id === void 0) continue;
145
+ if (onDelete === "cascade") {
146
+ await coll.delete(id);
147
+ cascaded++;
148
+ } else if (onDelete === "nullify") {
149
+ await coll.put(id, { ...rec, [referencing.field]: null });
150
+ nullified++;
151
+ }
152
+ }
153
+ }
154
+ return { cascaded, nullified, residue };
155
+ }
156
+ function selfWriteFieldEqual(a, b) {
157
+ if (a === b) return true;
158
+ if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
159
+ try {
160
+ return JSON.stringify(a) === JSON.stringify(b);
161
+ } catch {
162
+ return false;
163
+ }
164
+ }
165
+
166
+ export {
167
+ touchFor,
168
+ resolveRollupDeleteIntents,
169
+ findRollupSpecForIntent,
170
+ runGraphDispatchWave,
171
+ putDerivedOutput,
172
+ ledgerAuditHook,
173
+ forgetDerivedFanout,
174
+ selfWriteFieldEqual
175
+ };
176
+ //# sourceMappingURL=chunk-MGOXJIBV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/kernel/via/dispatch.ts"],"sourcesContent":["// kernel/via/dispatch.ts — the batched, origin-aware sync/cutover/restore dispatch wave\n// (Via port phase C, #638 Task 4, fixes #621).\n//\n// `Collection._onRecordMutated`'s `sync-apply`/`cutover`/`restore` cases feed touched\n// (collection, id) pairs into a per-session `GraphBatch` (owned by `Vault._collectGraphTouch`)\n// instead of dispatching inline — the `local-write` path keeps its own byte-identical inline\n// dispatch (`wave` stays `undefined` there, per the spec's behavior lock). At batch-flush time\n// `runGraphDispatchWave` decrypts each touched record (id threaded into the decrypt, matching\n// the phase-B at-rest contract) and re-runs the SAME `dispatchDerivations`/\n// `dispatchMaterializedViews` the local-write path uses, sharing one `WaveContext` so N touched\n// records feeding the SAME rollup/MV target recompute exactly once (per-target dedup).\n// See design-history/2026-07-11-via-phase-c-design.md §3.\n\nimport type { ViaGraph } from './graph.js'\nimport type { Collection } from '../collection.js'\nimport type { EncryptedEnvelope } from '../types.js'\nimport { PeriodClosedError } from '../errors.js'\nimport { matchesReferencingValue } from '../../port/with/lookup-strategy.js'\n\n/** One deleted child's resolved rollup PARENT intent (#640) — ids + a field name only. A\n * resolved `parentId` is an id, same class as any touched record id — never a stored value. */\nexport interface RollupDeleteIntent {\n readonly into: string\n readonly parentId: string\n readonly field: string\n}\n\n/** One collection's batched touches this session (#640 widens the #638 `Set<string>` shape):\n * `puts` — the original semantics, unchanged; `deletes` — a deleted record's id mapped to its\n * resolved rollup-parent intents, captured PRE-invalidation by `Collection._onRecordMutated`'s\n * sync-apply delete case (the FK is only readable there — see `kernel/collection.ts\n * #_rollupDeleteIntents`). */\nexport interface GraphTouch {\n readonly puts: Set<string>\n readonly deletes: Map<string, readonly RollupDeleteIntent[]>\n}\n\n/** Per-session touched set — collection → its `GraphTouch`. Metadata only — ids (collection\n * names, record ids INCLUDING resolved rollup parent ids) and field names; NEVER record payload\n * or key material. A resolved parentId is an id, same class as the touched ids — not a stored\n * value. */\nexport type GraphBatch = Map<string, GraphTouch>\n\n/** Get-or-create `batch`'s `GraphTouch` entry for `collection` (#640) — shared by\n * `Vault._collectGraphTouch`/`_collectGraphDelete` so each stays a one-line call. */\nexport function touchFor(batch: GraphBatch, collection: string): GraphTouch {\n let t = batch.get(collection)\n if (!t) {\n t = { puts: new Set(), deletes: new Map() }\n batch.set(collection, t)\n }\n return t\n}\n\n/** #640 — sync, I/O-free: `deleted`'s resolved rollup PARENT intents (the FK is only readable\n * NOW, before the record is gone) — a pure function so `Collection._rollupDeleteIntents` stays\n * a one-line delegator under the kernel-surface ceiling. `registry` is `this.derivationSource\n * ?.registry()`; `collectionName` is `this.name` (the CHILD/`rollup.from` side). Generic over\n * the registry's own spec shape so this file never imports a `with-formula` type (port-layering\n * — via/dispatch.ts must not gain a with-* import). */\nexport function resolveRollupDeleteIntents<S extends { source: string; rollup?: { from: string; key: string; field: string } }>(\n registry: { strategiesForSource(name: string): ReadonlyArray<{ spec: S }> } | undefined,\n collectionName: string,\n deleted: Record<string, unknown>,\n): RollupDeleteIntent[] {\n if (!registry) return []\n const intents: RollupDeleteIntent[] = []\n for (const { spec } of registry.strategiesForSource(collectionName)) {\n if (!spec.rollup || spec.rollup.from !== collectionName) continue\n const kv = deleted[spec.rollup.key]\n if (typeof kv === 'string' || typeof kv === 'number') intents.push({ into: spec.source, parentId: String(kv), field: spec.rollup.field })\n }\n return intents\n}\n\n/** #640 — resolve a `RollupDeleteIntent` back to its registry `spec`. The wave's per-intent\n * driver needs `spec.rollup.compute`, which `GraphBatch`'s metadata-only pin forbids batching —\n * so it's re-resolved here, post-boundary, instead of carried across it. `undefined` if the\n * intent's originating strategy was unregistered between collect time and wave time (residual\n * gap, freshness-only). */\nexport function findRollupSpecForIntent<S extends { source: string; rollup?: { from: string; field: string } }>(\n registry: { strategiesForSource(name: string): ReadonlyArray<{ spec: S }> } | undefined,\n collectionName: string,\n intent: RollupDeleteIntent,\n): S | undefined {\n return registry?.strategiesForSource(collectionName).find((s) => s.spec.source === intent.into && s.spec.rollup?.field === intent.field && s.spec.rollup?.from === collectionName)?.spec\n}\n\n/** One dedup ledger for a single wave — a target is recomputed at most once (mark-on-check). */\nexport class WaveContext {\n private readonly _seen = new Set<string>()\n seen(targetKey: string): boolean {\n if (this._seen.has(targetKey)) return true\n this._seen.add(targetKey)\n return false\n }\n}\n\n/** The slice of `Vault` the wave needs: the shared graph (the #553 zero-cost skip) and cached-\n * collection lookup — never constructs, since a touched collection is, by construction,\n * already open (it just fired `_onRecordMutated`). */\nexport interface VaultLike {\n readonly graph: ViaGraph\n _getCollection(name: string): Collection<Record<string, unknown>> | undefined\n /** #640 (#644 item 3) — structured wave-error surfacing, additive to the existing console.warn. */\n _emit(event: string, payload: unknown): void\n}\n\n/**\n * Run ONE dispatch wave for a completed batch: for each touched (collection, id), decrypt the\n * applied envelope (id threaded), then run the SAME `dispatchDerivations` +\n * `dispatchMaterializedViews` the local-write path uses — with a shared `WaveContext` so N\n * touched records feeding the SAME target recompute once. #553: a collection with no graph\n * out-edges (e.g. money-only) is skipped before any decrypt/dispatch async work.\n */\nexport async function runGraphDispatchWave(vault: VaultLike, batch: GraphBatch): Promise<void> {\n const wave = new WaveContext()\n for (const [collectionName, touch] of batch) {\n if (vault.graph.dependentsOf(collectionName).length === 0) continue\n const coll = vault._getCollection(collectionName)\n if (!coll) continue\n for (const id of touch.puts) {\n try {\n // Decrypt is INSIDE the per-id isolation boundary (whole-branch review Important\n // finding, #638): an undecryptable synced envelope (`TamperedError`) must not\n // escape `_getStoredRecordForDispatch` and abort the wave — that would propagate\n // through `_flushGraphBatch` and reject the surrounding `SyncEngine.pull`/`push`\n // AFTER records were already applied + meta persisted, starving every other\n // touched target in the same batch.\n const stored = await coll._getStoredRecordForDispatch(id)\n if (!stored) continue\n await coll.dispatchDerivations(id, stored.record, stored.version, wave)\n await coll.dispatchMaterializedViews(id, stored.record, wave)\n } catch (err) {\n // #638 Task 5 review mandate: one touched record's recompute must not abort the\n // whole wave (starving co-batched healthy targets) or the pull/push it's nested\n // inside. `PeriodClosedError` never reaches here — `putDerivedOutput` already\n // intercepts it at every output-write call site and turns it into a skip+event.\n // Anything else (a genuine decrypt failure, a derive()/executor bug, a schema\n // violation on the output, ...) is surfaced — not silently swallowed — via the\n // SAME console.warn channel `dispatchDerivations`/the MV executor already use for\n // their own non-strict-mode output failures, then isolated to just this one id.\n // #644 item 3: ADDITIVELY (never in place of the warn) emit a structured event too,\n // so a listener doesn't have to scrape console output to react to a wave failure.\n console.warn(`[via-dispatch] wave recompute failed for ${collectionName}/${id}:`, err)\n vault._emit('derivation:wave-error', { collection: collectionName, id, error: err })\n }\n }\n // #640 — delete-kind touches: the deleted child's rollup-parent intents, resolved (sync,\n // pre-invalidation) at collect time by `Collection._rollupDeleteIntents`. Same per-id\n // isolation as the puts loop above. #658: the wave now ALSO heals MV rows and array-shape\n // derivation output rows for the deleted child — parity with the local-delete boundary\n // (`Collection._doDelete`'s `!internal` block, which already calls all three). Still never\n // routed through `dispatchDerivations` (the mutation-choke-point.test.ts:85-99 pin — sync-\n // applied deletes never RE-DERIVE); `dispatchMaterializedViewsOnDelete`/\n // `dispatchArrayDerivationsOnDelete` only need `id` (MV refresh recomputes+diffs; array-\n // derivation reads its per-source sidecar) and never call `derive()`, so the pin holds.\n for (const [id, intents] of touch.deletes) {\n try {\n await coll._recomputeDeletedRollups(intents, wave)\n // #658: record-shape same-id outputs are left untouched (eraseRecordShapeToo defaults\n // false), matching local delete — the user deletes those directly if wanted.\n // #658: TODO dedup MV refresh per wave — `dispatchMaterializedViewsOnDelete` doesn't\n // accept a `WaveContext` (unlike the put-path `dispatchMaterializedViews`), so N deleted\n // children of one eager MV in a batch each trigger a full refresh pass. Threading `wave`\n // through would touch collection.ts's zero-slack kernel-surface ceiling for a perf-only\n // win; correctness doesn't depend on it (idempotent per source-id) — left as a follow-up.\n await coll.dispatchMaterializedViewsOnDelete(id)\n await coll.dispatchArrayDerivationsOnDelete(id)\n } catch (err) {\n console.warn(`[via-dispatch] wave delete-recompute failed for ${collectionName}/${id}:`, err)\n vault._emit('derivation:wave-error', { collection: collectionName, id, error: err })\n }\n }\n }\n}\n\n/** A record's post-freeze source mutation, for the `'derivation:skipped-frozen'` event\n * and the optional audit-trail entry. See {@link putDerivedOutput}. */\nexport interface DerivationSkippedFrozen {\n readonly source: { readonly collection: string; readonly id: string }\n readonly target: { readonly collection: string; readonly id: string }\n readonly period: string\n readonly endDate: string\n}\n\n/** The minimal put-capable shape `putDerivedOutput` needs. Any `Collection<T>` structurally\n * satisfies this — its real `options` type is a superset of what's declared here. */\nexport interface CollectionLike {\n put(id: string, value: unknown, options?: { readonly source?: string }): Promise<void>\n}\n\nexport interface PutDerivedOutputCtx {\n readonly emit: (ev: string, p: unknown) => void\n readonly source: { readonly collection: string; readonly id: string }\n readonly audit?: ((e: DerivationSkippedFrozen) => Promise<void>) | undefined\n}\n\n/**\n * Attempt a dispatch-driven output write. On `PeriodClosedError` (the closed-period\n * `beforePut` gate — `freezePeriod`/`archivePeriod` add no separate gate, per seam map\n * Part 7): SKIP (no `_ts` stamped — the gate throws before any write happens), emit\n * `'derivation:skipped-frozen'` on the event bus (ALWAYS), and append an audit-trail entry\n * when the with-history ledger is active. Returns `'written' | 'skipped-frozen'`. Any OTHER\n * error propagates unchanged — this helper narrows exactly one error class. The SOURCE\n * write is never wrapped through this helper — only derived-output writes are (§7).\n */\nexport async function putDerivedOutput(\n outColl: CollectionLike, id: string, value: unknown,\n ctx: PutDerivedOutputCtx,\n options?: { readonly source?: string },\n): Promise<'written' | 'skipped-frozen'> {\n try {\n await outColl.put(id, value, options)\n return 'written'\n } catch (err) {\n if (!(err instanceof PeriodClosedError)) throw err\n // Reach-around for the target collection's private `name` — the SAME pattern\n // `with-formula/materialized-views/executor.ts`'s `listOutputIds` already uses to\n // read a `Collection`'s private `adapter`/`vault`/`name` fields from outside the class.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const targetCollection = (outColl as any).name as string\n const event: DerivationSkippedFrozen = {\n source: ctx.source,\n target: { collection: targetCollection, id },\n period: err.periodName,\n endDate: err.endDate,\n }\n ctx.emit('derivation:skipped-frozen', event)\n if (ctx.audit) await ctx.audit(event)\n return 'skipped-frozen'\n }\n}\n\n/** Structural (no static import — kernel spine may not statically reach a with-* service,\n * the S4 gate recipe `check-architecture.mjs`'s port-layering check enforces) shape of the\n * with-history `LedgerStore.append` seam this helper needs. */\ninterface AuditLedgerLike {\n append(input: {\n readonly op: 'lifecycle'\n readonly collection: string\n readonly id: string\n readonly version: number\n readonly actor: string\n readonly payloadHash: string\n readonly reason?: string\n }): Promise<unknown>\n}\n\n/** @internal — the optional with-history ledger audit hook for `putDerivedOutput`, present\n * only when the ledger is active (mirrors every other kernel event's `if (this.ledger)`\n * gate). Encoded as a `'lifecycle'` entry — the existing \"non-data audit event\" op (the\n * same convention `forget`'s JSON-summary-in-`reason` entry uses; `ledger/entry.ts:88-104`). */\nexport function ledgerAuditHook(\n ledger: AuditLedgerLike | undefined, actor: string,\n): ((e: DerivationSkippedFrozen) => Promise<void>) | undefined {\n if (!ledger) return undefined\n return async (e) => {\n await ledger.append({\n op: 'lifecycle', collection: e.target.collection, id: e.target.id, version: 0, actor, payloadHash: '',\n reason: JSON.stringify({ event: 'derivation-skipped-frozen', ...e }),\n })\n }\n}\n\n/** `recomputeRollup`/`dispatchRollupsOnDelete`'s per-target write outcome (#638 Task 6):\n * `'written'`/`'skipped-frozen'` mirror {@link putDerivedOutput}'s result; `'noop'` covers\n * no-parent/no-change/deduped-by-wave — nothing to report either way. */\nexport type RollupOutcome = 'written' | 'skipped-frozen' | 'noop'\n\n/** Mutable accumulator `forgetDerivedFanout` writes into, one per `Vault.forget()` call — keeps\n * the per-ref loop's call site to a single line under vault.ts's tight kernel-surface ceiling.\n * Maps 1:1 onto `ForgetResult`'s additive fields (`with-audit/forget/strategy.ts`). */\nexport interface ForgetFanoutStats {\n recordsErased: number\n aggregatesRecomputed: number\n readonly residueFrozen: string[]\n /** #650 Task 5 (#648) — referencing records tombstoned via a `'ref'` edge's `cascade` policy. */\n lookupReferencesCascaded: number\n /** #650 Task 5 (#648) — referencing fields cleared via a `'ref'` edge's `nullify` policy. */\n lookupReferencesNullified: number\n /** #650 Task 5 review (Important fix) — `'ref'` edges whose compare-key could NOT be resolved,\n * even from the LIVE pre-shred row (the backing row itself is unreadable) — propagation was\n * skipped for these, reported here so the skip is never silent. `backing:key:collection.field`\n * entries, one per un-propagated edge. */\n readonly lookupReferencesResidue: string[]\n /** #776/#782/#785 — `outputCollection:id` MV-output rows that survived erasure invalidation\n * (eager tombstone leg AND lazy/manual `invalidateMVAtRest`) despite belonging to the\n * forgotten subject, because the ownership stamp could not be decoded (undecodable under\n * the default DEK — e.g. elevated above tier 0 on a tiered output collection). Ownership\n * UNCONFIRMED. Never erased, but surfaced here rather than silently skipped. */\n readonly derivedResidueUndecodable: string[]\n /** #782/#785 — `outputCollection:id` MV-output rows that decoded and stamp-matched but whose\n * `_internalDelete` declined (the #718 tier-elevation gate). Ownership CONFIRMED, erasure\n * declined — a real silent survival, surfaced here rather than silently skipped. */\n readonly derivedResidueDeclined: string[]\n}\n\n/**\n * #622 — after `_writeTombstone(ref.id, actor)` erases the forgotten subject's own record, fan\n * out to its derived residue via the graph (spec §5): record-grain artifacts (MV rows,\n * array-shape derivation rows, same-id record-shape derivation copies) are ERASED through the\n * SAME `!internal` housekeeping-bypass machinery the ordinary delete path uses (no user\n * `onDelete` re-fires — the shred-is-not-a-domain-delete property `_writeTombstone` protects);\n * aggregate-grain rollups are RECOMPUTED without the forgotten contribution in open periods, or\n * skip+audit (via `putDerivedOutput`, already wired into the rollup/MV output paths) in frozen\n * ones. Mutates `stats` in place. `envelope` is `ref`'s PRE-tombstone envelope, decoded only if\n * a rollup edge is actually present — the #553 zero-cost-skip discipline: no decrypt for the\n * common case of a forgotten record with no aggregate-grain consumer, and no work at all when\n * `ref.collection` has no graph out-edges. `lookupCompareKeys` is the `'ref'` edges' compare-key\n * map, resolved from the LIVE row BEFORE any shred (`VaultLinks.checkLookupRefsRestrict`'s return\n * value — `Vault.forget()`'s pre-shred restrict check doubles as the live-resolve pass) — see\n * {@link applyLookupRefsFanout}.\n */\nexport async function forgetDerivedFanout(\n vault: VaultLike,\n ref: { readonly collection: string; readonly id: string },\n envelope: EncryptedEnvelope | null,\n stats: ForgetFanoutStats,\n lookupCompareKeys: ReadonlyMap<string, string | undefined>,\n): Promise<void> {\n const coll = vault._getCollection(ref.collection)\n\n // #761 item 4 — drive the MV arm off `dispatchMaterializedViewsOnDelete` UNCONDITIONALLY,\n // ABOVE the `edges` check below: `registry.edges()` drops the same-collection\n // partition-disjoint MV edge (registry.ts's `sources.length === 0 → continue`), so a\n // same-collection MV's source collection never appears in `vault.graph`'s derived-artifact\n // edges even though the MV IS registered — forget() would otherwise never reach it.\n // `dispatchMaterializedViewsOnDelete` self-guards O(1) via `mvsForSource` (a no-op when\n // this collection isn't an MV source), so this is safe to call unconditionally. Safe to\n // route through the eager executor's tombstone leg ONLY because that leg is now\n // stamp-scoped (#762) — before that fix this would have opened a NEW forget-time\n // data-loss path into the unscoped tombstone diff.\n if (coll) {\n const mv = await coll.dispatchMaterializedViewsOnDelete(ref.id)\n stats.recordsErased += mv.deleted\n stats.derivedResidueUndecodable.push(...mv.residueUndecodable)\n stats.derivedResidueDeclined.push(...mv.residueDeclined)\n }\n\n const edges = vault.graph.derivedArtifactsOf(ref.collection)\n if (edges.length === 0 || !coll) return\n\n // #650 Task 5 (#648) — 'ref' cascade/nullify propagate ADDITIVELY, here, AFTER the shred\n // (restrict already refused BEFORE any shred — the caller's pre-tombstone check, spec §4).\n // The I/O shell (loop shape, collection accessor) is duplicated, not imported, from\n // `VaultLinks.applyLookupRefsPropagation`/`checkLookupRefsRestrict` (with-shape/links/\n // vault-facade.ts) — the kernel spine may not statically import a with-* service\n // (port-layering, the #638 Task 5 via/dispatch.ts precedent); `vault._getCollection` is\n // cached-only while `VaultLinks`' accessor constructs. The pure match predicate itself is\n // shared through the port seam (#651 Task 3 — `matchesReferencingValue`, `port/with/\n // lookup-strategy.ts`), so only the shell, not the coercion logic, stays duplicated. A\n // referencing edge whose dimension uses a non-default `key` (matrix tier only) needs the\n // backing row's OWN value at that field, not its PUT-id — the row is ALREADY tombstoned by\n // now, so it's read from `lookupCompareKeys`, resolved by the caller from the LIVE row\n // BEFORE the shred (#650 Task 5 review, Important fix — eliminates the post-shred\n // envelope-decode dependency this used to have, which silently skipped propagation whenever\n // that decode failed).\n if (edges.some((e) => e.kind === 'ref')) {\n const { cascaded, nullified, residue } = await applyLookupRefsFanout(vault, ref.collection, ref.id, lookupCompareKeys)\n stats.lookupReferencesCascaded += cascaded\n stats.lookupReferencesNullified += nullified\n stats.lookupReferencesResidue.push(...residue)\n }\n\n if (edges.some((e) => e.kind === 'derivation')) {\n // #622 review Finding 1: count REAL erasures (dispatchArrayDerivationsOnDelete's own\n // `_internalDelete`-backed tally), not the derivation EDGE count — an edge exists whenever\n // this collection is ANY trigger (source/sources[]/triggerBy), but the same-id record-shape\n // guard only erases for `spec.source === this.name`, and `derive()` may never have produced\n // an output row (optional-skip) in the first place. Both cases must contribute 0, not +1.\n stats.recordsErased += await coll.dispatchArrayDerivationsOnDelete(ref.id, true)\n }\n\n if (envelope && edges.some((e) => e.kind === 'rollup')) {\n const priorRecord = await coll._decodeEnvelope(envelope, ref.id)\n if (priorRecord) {\n for (const r of await coll.dispatchRollupsOnDelete(ref.id, priorRecord)) {\n if (r.outcome === 'written') stats.aggregatesRecomputed += 1\n else if (r.outcome === 'skipped-frozen') stats.residueFrozen.push(`${r.into}:${r.parentId}`)\n }\n }\n }\n}\n\n/**\n * Apply `cascade`/`nullify` propagation for `backing`'s non-restrict `'ref'` edges — the\n * forget-path counterpart of `VaultLinks.applyLookupRefsPropagation` (I/O shell duplicated, not\n * imported, match predicate shared via the port — see {@link forgetDerivedFanout}'s call site\n * comment). `restrict` edges are skipped here: the\n * forget loop's `checkLookupRefsRestrict`-equivalent call already refused (or the reference no\n * longer existed) BEFORE `_writeTombstone` ran, so by the time this runs only cascade/nullify\n * remain to propagate. `compareKeys` is the non-`'id'`-`keyField` compare-value map, resolved by\n * the caller from the LIVE row BEFORE the shred (#650 Task 5 review, Important fix) — the live row\n * is already shredded by the time THIS function runs, so it can no longer resolve one itself. A\n * `keyField` absent from (or `undefined` in) the map means that live resolve failed too — the edge\n * is reported as residue instead of silently skipped (never a bare `continue` with no trace).\n */\nasync function applyLookupRefsFanout(\n vault: VaultLike,\n backing: string,\n key: string,\n compareKeys: ReadonlyMap<string, string | undefined>,\n): Promise<{ cascaded: number; nullified: number; residue: string[] }> {\n let cascaded = 0\n let nullified = 0\n const residue: string[] = []\n for (const { referencing, onDelete, keyField } of vault.graph.referencingEdgesOf(backing)) {\n if (onDelete === 'restrict') continue\n const compareKey = keyField === 'id' ? key : compareKeys.get(keyField)\n if (compareKey === undefined) {\n residue.push(`${backing}:${key}:${referencing.collection}.${referencing.field}`)\n continue\n }\n const coll = vault._getCollection(referencing.collection)\n if (!coll) continue\n const matches = (await coll.list()).filter((rec) => matchesReferencingValue(rec, referencing.field, compareKey))\n for (const rec of matches) {\n const id = rec['id'] as string | undefined\n if (id === undefined) continue\n if (onDelete === 'cascade') {\n await coll.delete(id)\n cascaded++\n } else if (onDelete === 'nullify') {\n await coll.put(id, { ...rec, [referencing.field]: null })\n nullified++\n }\n }\n }\n return { cascaded, nullified, residue }\n}\n\n/**\n * Value-equality for a single self-write reverse-denorm field. Scalars\n * compare by identity; objects by canonical JSON (denorm values should be\n * deterministically shaped). Used as the cycle guard — when every denorm\n * field already matches, no write is issued and the self-write recursion ends.\n */\nexport function selfWriteFieldEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false\n try {\n return JSON.stringify(a) === JSON.stringify(b)\n } catch {\n return false\n }\n}\n"],"mappings":"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","names":[]}
@@ -812,7 +812,7 @@ async function applySatelliteLivenessFilter(vault, dumpJson) {
812
812
  if (!backup.collections || typeof backup.collections !== "object") return dumpJson;
813
813
  if (typeof vault._introspectState !== "function") return dumpJson;
814
814
  const { adapter, name, getDEK } = vault._introspectState();
815
- const { liveBaseIdSetsForBundle } = await import("./dead-filter-M3NZUWBZ.js");
815
+ const { liveBaseIdSetsForBundle } = await import("./dead-filter-OMMVBAKZ.js");
816
816
  const satLive = await liveBaseIdSetsForBundle(adapter, name, Object.keys(backup.collections), getDEK);
817
817
  if (satLive.size === 0) return dumpJson;
818
818
  const next = {};
@@ -1059,4 +1059,4 @@ export {
1059
1059
  readPodRedirect,
1060
1060
  readPod
1061
1061
  };
1062
- //# sourceMappingURL=chunk-PKJ43CMD.js.map
1062
+ //# sourceMappingURL=chunk-NLOLF6LD.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  walkClosure
3
- } from "./chunk-DJ3LNNZQ.js";
3
+ } from "./chunk-3LK2XZCW.js";
4
4
  import {
5
5
  resolveManagedSecret
6
6
  } from "./chunk-JUBWUJY5.js";
@@ -8,7 +8,7 @@ import {
8
8
  parseExtractedPartitionBody,
9
9
  readPod,
10
10
  readPodHeader
11
- } from "./chunk-PKJ43CMD.js";
11
+ } from "./chunk-NLOLF6LD.js";
12
12
  import {
13
13
  createOwnerKeyring,
14
14
  mintRosterTag,
@@ -41,7 +41,7 @@ import {
41
41
  function withCargo() {
42
42
  return {
43
43
  async extractPartition(vault, opts) {
44
- const { extractPartitionCore } = await import("./extract-partition-RYJZIKQZ.js");
44
+ const { extractPartitionCore } = await import("./extract-partition-AZGSEMIF.js");
45
45
  return extractPartitionCore(vault, opts);
46
46
  }
47
47
  };
@@ -252,7 +252,7 @@ async function createOwnerOnAdoptedPartition(store, vaultName, opts) {
252
252
  }
253
253
  }
254
254
  if (isManaged(opts)) {
255
- const { createNoydb } = await import("./noydb-JJXPICIC.js");
255
+ const { createNoydb } = await import("./noydb-Y53CQHZK.js");
256
256
  const db = await createNoydb({
257
257
  store,
258
258
  user: userId,
@@ -307,4 +307,4 @@ export {
307
307
  withCargo,
308
308
  describeExtraction
309
309
  };
310
- //# sourceMappingURL=chunk-7NTBVFGD.js.map
310
+ //# sourceMappingURL=chunk-NOSRK3ZZ.js.map
@@ -294,31 +294,31 @@ function buildBlobFieldEntries(filenamesByField) {
294
294
  function withSearch() {
295
295
  return {
296
296
  async search(ctx, field, query, opts) {
297
- const { search } = await import("./collection-facade-MGGHQ3HU.js");
297
+ const { search } = await import("./collection-facade-VOWW732S.js");
298
298
  return search(ctx, field, query, opts);
299
299
  },
300
300
  async retrieve(ctx, query, opts) {
301
- const { retrieve } = await import("./collection-facade-MGGHQ3HU.js");
301
+ const { retrieve } = await import("./collection-facade-VOWW732S.js");
302
302
  return retrieve(ctx, query, opts);
303
303
  },
304
304
  async similarTo(ctx, vector, opts) {
305
- const { similarTo } = await import("./collection-facade-MGGHQ3HU.js");
305
+ const { similarTo } = await import("./collection-facade-VOWW732S.js");
306
306
  return similarTo(ctx, vector, opts);
307
307
  },
308
308
  async warmIndex(ctx) {
309
- const { warmIndex } = await import("./collection-facade-MGGHQ3HU.js");
309
+ const { warmIndex } = await import("./collection-facade-VOWW732S.js");
310
310
  return warmIndex(ctx);
311
311
  },
312
312
  async flushIndex(ctx) {
313
- const { flushIndex } = await import("./collection-facade-MGGHQ3HU.js");
313
+ const { flushIndex } = await import("./collection-facade-VOWW732S.js");
314
314
  return flushIndex(ctx);
315
315
  },
316
316
  async embedOnWrite(ctx, id, record, version) {
317
- const { embedOnWrite } = await import("./collection-facade-MGGHQ3HU.js");
317
+ const { embedOnWrite } = await import("./collection-facade-VOWW732S.js");
318
318
  return embedOnWrite(ctx, id, record, version);
319
319
  },
320
320
  async rebuildEmbeddings(ctx) {
321
- const { rebuildEmbeddings } = await import("./collection-facade-MGGHQ3HU.js");
321
+ const { rebuildEmbeddings } = await import("./collection-facade-VOWW732S.js");
322
322
  return rebuildEmbeddings(ctx);
323
323
  }
324
324
  };
@@ -364,4 +364,4 @@ export {
364
364
  buildBlobFieldEntries,
365
365
  withSearch
366
366
  };
367
- //# sourceMappingURL=chunk-HN4FNA4X.js.map
367
+ //# sourceMappingURL=chunk-OMB2ZKIT.js.map
@@ -16,7 +16,7 @@ function withCustody() {
16
16
  return host._revokeCustodianImpl(revoke, vault, options, factors);
17
17
  },
18
18
  async liberate(vault, opts) {
19
- const { liberateVault } = await import("./liberate-4APIV7FR.js");
19
+ const { liberateVault } = await import("./liberate-AAZSP2BT.js");
20
20
  return liberateVault(vault, opts);
21
21
  }
22
22
  };
@@ -74,4 +74,4 @@ export {
74
74
  withCustody,
75
75
  CustodyApi
76
76
  };
77
- //# sourceMappingURL=chunk-Y4MJQ2UG.js.map
77
+ //# sourceMappingURL=chunk-QQ2YMQZU.js.map
@@ -37,7 +37,7 @@ async function loadZodToJsonSchemaConverter() {
37
37
  }
38
38
  async function loadZodV4Converter() {
39
39
  try {
40
- const mod = await import("./zod-HAJM7LMS.js");
40
+ const mod = await import("zod");
41
41
  if (typeof mod.toJSONSchema !== "function") {
42
42
  throw new Error("zod.toJSONSchema not available (zod v4 required)");
43
43
  }
@@ -79,4 +79,4 @@ export {
79
79
  isZod4Schema,
80
80
  derivePersistedSchema
81
81
  };
82
- //# sourceMappingURL=chunk-URHRIAV6.js.map
82
+ //# sourceMappingURL=chunk-REDOOY2Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-shape/persisted-schemas/derive.ts"],"sourcesContent":["/**\n * Derive a {@link PersistedSchemaEnvelope} from a Standard Schema v1\n * validator. Supports zod 3 (via the optional `zod-to-json-schema` peer-dep)\n * and zod 4 (via its native `z.toJSONSchema()`); both are loaded lazily so\n * hub never statically imports zod. Other validator families write a stub\n * envelope flagging the kind.\n *\n * @see design-history/2026-05-22-schema-dump-design.md\n *\n * @module\n */\n\nimport { canonicalize } from './canonicalize.js'\nimport { sha256Hex } from '../../kernel/enclave/index.js'\nimport type { PersistedSchemaEnvelope, PersistedSchemaKind } from './types.js'\n\n/**\n * Heuristic Zod detection — uses the Standard Schema v1 `~standard.vendor`\n * property (present in Zod v3.23+ and Zod v4+). Falls back to the\n * `_def.typeName` heuristic for older Zod v3 builds that predate Standard\n * Schema support.\n */\nexport function isZodSchema(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false\n // Standard Schema v1 vendor tag — most reliable across Zod majors\n const std = (value as { '~standard'?: { vendor?: unknown } })['~standard']\n if (std && typeof std === 'object' && (std as { vendor?: unknown }).vendor === 'zod') return true\n // Zod v3 fallback: _def.typeName starts with 'Zod' (e.g. 'ZodObject')\n const def = (value as { _def?: { typeName?: unknown } })._def\n if (!def || typeof def !== 'object') return false\n return typeof (def as { typeName?: unknown }).typeName === 'string'\n && ((def as { typeName: string }).typeName).startsWith('Zod')\n}\n\nfunction detectKind(validator: unknown): PersistedSchemaKind {\n if (isZodSchema(validator)) return 'Zod'\n return 'Unknown'\n}\n\n/**\n * Returns `true` when the validator was created with the Zod v4 native API\n * (i.e. `import { z } from 'zod'` where zod resolves to v4). Zod v4 native\n * schemas carry a `_zod` property whereas Zod v3 schemas (and the v3-compat\n * shim shipped inside zod@4 at `zod/v3`) use `_def.typeName`.\n *\n * Kept duck-typed so hub never statically imports zod.\n */\nexport function isZod4Schema(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false\n return '_zod' in value\n}\n\n/**\n * Lazy-require `zod-to-json-schema`. Returns the converter, or throws a\n * clear error if the peer-dep isn't installed.\n */\nasync function loadZodToJsonSchemaConverter(): Promise<(s: unknown) => object> {\n try {\n const mod = (await import('zod-to-json-schema')) as { zodToJsonSchema?: (s: unknown) => object }\n if (!mod.zodToJsonSchema) {\n throw new Error('zod-to-json-schema export missing')\n }\n return mod.zodToJsonSchema\n } catch (err) {\n throw new Error(\n 'persistJsonSchema requires the optional peer-dep `zod-to-json-schema`. '\n + 'Install it: `pnpm add zod-to-json-schema` (or npm/yarn equivalent). '\n + `Original error: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\n/**\n * Lazy-require Zod v4's built-in `toJSONSchema`. Used when the validator is a\n * Zod v4 native schema and `zod-to-json-schema` is absent or cannot handle it.\n *\n * Dynamic so hub never loads zod unless a caller actually persists a Zod v4\n * schema. ⚠️ Dynamic is NOT what keeps zod out of the tarball — that is\n * `peerDependencies` (#1227). tsup externalises DECLARED dependencies and\n * BUNDLES everything else, static or dynamic alike, so while zod was a\n * devDependency only, this line silently vendored 548 KB of zod@4.4.3 into\n * `dist/` at a build-frozen version. The comment here previously claimed the\n * dynamic import was what prevented that; it never did.\n *\n * Declaring it an OPTIONAL peer is also the correctness fix, not just a size\n * one: `toJSONSchema` reads a schema's internals, and the schema is built by\n * the CONSUMER's zod. A vendored copy meant hub inspected one zod's objects\n * with another zod's version of that reader. Now there is one zod — theirs.\n */\nasync function loadZodV4Converter(): Promise<(s: unknown) => object> {\n try {\n const mod = (await import('zod')) as { toJSONSchema?: (s: unknown) => object }\n if (typeof mod.toJSONSchema !== 'function') {\n throw new Error('zod.toJSONSchema not available (zod v4 required)')\n }\n return mod.toJSONSchema\n } catch (err) {\n throw new Error(\n 'persistJsonSchema with a Zod v4 schema requires zod@4 with toJSONSchema. '\n + `Original error: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n\nexport async function derivePersistedSchema(\n validator: unknown,\n): Promise<PersistedSchemaEnvelope> {\n const kind = detectKind(validator)\n const derivedAt = new Date().toISOString()\n\n if (kind === 'Zod') {\n let jsonSchema: object\n\n if (isZod4Schema(validator)) {\n // Zod v4 native schemas: use zod's built-in toJSONSchema (available in\n // zod@4) because zod-to-json-schema@3.x parses _def.typeName which is\n // absent in native v4 schemas.\n const convert = await loadZodV4Converter()\n jsonSchema = convert(validator)\n } else {\n // Zod v3 schemas (or v3-compat shim): use the optional peer-dep\n // zod-to-json-schema for backward compatibility.\n const convert = await loadZodToJsonSchemaConverter()\n jsonSchema = convert(validator)\n }\n\n const canonical = canonicalize(jsonSchema)\n const hash = await sha256Hex(new TextEncoder().encode(canonical))\n return { _noydb_schema: 1, kind, jsonSchema, hash, derivedAt }\n }\n\n return {\n _noydb_schema: 1,\n kind,\n jsonSchema: null,\n hash: null,\n reason: `derivation not yet supported for kind=${kind} (v0 supports Zod only)`,\n derivedAt,\n }\n}\n"],"mappings":";;;;;;;;AAsBO,SAAS,YAAY,OAAyB;AACnD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AAExD,QAAM,MAAO,MAAiD,WAAW;AACzE,MAAI,OAAO,OAAO,QAAQ,YAAa,IAA6B,WAAW,MAAO,QAAO;AAE7F,QAAM,MAAO,MAA4C;AACzD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,SAAO,OAAQ,IAA+B,aAAa,YACpD,IAA6B,SAAU,WAAW,KAAK;AAChE;AAEA,SAAS,WAAW,WAAyC;AAC3D,MAAI,YAAY,SAAS,EAAG,QAAO;AACnC,SAAO;AACT;AAUO,SAAS,aAAa,OAAyB;AACpD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,SAAO,UAAU;AACnB;AAMA,eAAe,+BAAgE;AAC7E,MAAI;AACF,UAAM,MAAO,MAAM,OAAO,oBAAoB;AAC9C,QAAI,CAAC,IAAI,iBAAiB;AACxB,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AACA,WAAO,IAAI;AAAA,EACb,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,kKAEqB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACvE;AAAA,EACF;AACF;AAmBA,eAAe,qBAAsD;AACnE,MAAI;AACF,UAAM,MAAO,MAAM,OAAO,KAAK;AAC/B,QAAI,OAAO,IAAI,iBAAiB,YAAY;AAC1C,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AACA,WAAO,IAAI;AAAA,EACb,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,4FACqB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACvE;AAAA,EACF;AACF;AAEA,eAAsB,sBACpB,WACkC;AAClC,QAAM,OAAO,WAAW,SAAS;AACjC,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AAEzC,MAAI,SAAS,OAAO;AAClB,QAAI;AAEJ,QAAI,aAAa,SAAS,GAAG;AAI3B,YAAM,UAAU,MAAM,mBAAmB;AACzC,mBAAa,QAAQ,SAAS;AAAA,IAChC,OAAO;AAGL,YAAM,UAAU,MAAM,6BAA6B;AACnD,mBAAa,QAAQ,SAAS;AAAA,IAChC;AAEA,UAAM,YAAY,aAAa,UAAU;AACzC,UAAM,OAAO,MAAM,UAAU,IAAI,YAAY,EAAE,OAAO,SAAS,CAAC;AAChE,WAAO,EAAE,eAAe,GAAG,MAAM,YAAY,MAAM,UAAU;AAAA,EAC/D;AAEA,SAAO;AAAA,IACL,eAAe;AAAA,IACf;AAAA,IACA,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,QAAQ,yCAAyC,IAAI;AAAA,IACrD;AAAA,EACF;AACF;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  freezeSnapshotOnly
3
- } from "./chunk-QHW35ZON.js";
3
+ } from "./chunk-TGPST6FK.js";
4
4
  import {
5
5
  resolveManagedSecret
6
6
  } from "./chunk-JUBWUJY5.js";
@@ -137,4 +137,4 @@ export {
137
137
  isDeedVault,
138
138
  liberateVault
139
139
  };
140
- //# sourceMappingURL=chunk-YCXICMOY.js.map
140
+ //# sourceMappingURL=chunk-RJ7NN2UR.js.map