@noy-db/hub 0.6.0-pre.2 → 0.6.0-pre.21

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 (499) hide show
  1. package/CHANGELOG.md +5234 -0
  2. package/codemods/0.4.0-pre.json +699 -0
  3. package/codemods/0.6.0-pre.json +211 -0
  4. package/dist/api-EYNT6KGS.js +20 -0
  5. package/dist/attestation/index.js +19 -19
  6. package/dist/{backup-YYBE5RPC.js → backup-FSC36VWC.js} +36 -18
  7. package/dist/backup-FSC36VWC.js.map +1 -0
  8. package/dist/blobs/index.js +13 -13
  9. package/dist/broker/index.js +12 -12
  10. package/dist/cargo/index.js +33 -33
  11. package/dist/{chunk-35EBKWQU.js → chunk-2LFOMUEA.js} +4 -4
  12. package/dist/{chunk-OF3L2SD6.js → chunk-32B7VCKU.js} +2 -2
  13. package/dist/{chunk-MSWYQLMQ.js → chunk-3B6S6RSI.js} +2 -2
  14. package/dist/{chunk-PJNTR2NV.js → chunk-3ENNPCIW.js} +7 -10
  15. package/dist/chunk-3ENNPCIW.js.map +1 -0
  16. package/dist/{chunk-7F4DBNSP.js → chunk-3O72ZD2E.js} +3 -3
  17. package/dist/{chunk-FNSMZQS4.js → chunk-3PMFMUFX.js} +2 -2
  18. package/dist/{chunk-WQDTCJ6W.js → chunk-45YHJ3GV.js} +11 -3
  19. package/dist/{chunk-WQDTCJ6W.js.map → chunk-45YHJ3GV.js.map} +1 -1
  20. package/dist/{chunk-NIVEDYJZ.js → chunk-4A34KWOU.js} +31 -49
  21. package/dist/chunk-4A34KWOU.js.map +1 -0
  22. package/dist/{chunk-ZJADGNYF.js → chunk-4AOERLTV.js} +1 -1
  23. package/dist/chunk-4AOERLTV.js.map +1 -0
  24. package/dist/{chunk-EDXAQ6JI.js → chunk-4N5Q23KV.js} +87 -34
  25. package/dist/chunk-4N5Q23KV.js.map +1 -0
  26. package/dist/{chunk-VV4NSB7P.js → chunk-4OQZNXQG.js} +15 -24
  27. package/dist/chunk-4OQZNXQG.js.map +1 -0
  28. package/dist/{chunk-FAXZQ5UF.js → chunk-4RAS6HN6.js} +2 -2
  29. package/dist/{chunk-C2OOEM57.js → chunk-4YL2LMXK.js} +92 -35
  30. package/dist/chunk-4YL2LMXK.js.map +1 -0
  31. package/dist/{chunk-EP7VPR7Y.js → chunk-5Q7AJ7DN.js} +11 -8
  32. package/dist/chunk-5Q7AJ7DN.js.map +1 -0
  33. package/dist/{chunk-VMCCGUQB.js → chunk-6JPGVL7X.js} +2 -2
  34. package/dist/{chunk-PRDRGL73.js → chunk-6KTVJ5ZZ.js} +17 -14
  35. package/dist/chunk-6KTVJ5ZZ.js.map +1 -0
  36. package/dist/{chunk-4MC5JRRX.js → chunk-6WO62AJW.js} +14 -15
  37. package/dist/chunk-6WO62AJW.js.map +1 -0
  38. package/dist/{chunk-WLYIROCJ.js → chunk-6YD5SYQS.js} +2 -2
  39. package/dist/{chunk-YAWK6AWF.js → chunk-7KAE7CQ2.js} +2 -2
  40. package/dist/{chunk-MWKDOHEF.js → chunk-7LNE2N2V.js} +17 -10
  41. package/dist/chunk-7LNE2N2V.js.map +1 -0
  42. package/dist/{chunk-5PR33AC4.js → chunk-7PJ3BKHS.js} +20 -17
  43. package/dist/chunk-7PJ3BKHS.js.map +1 -0
  44. package/dist/{chunk-JXIDGFKS.js → chunk-A4TOGAUW.js} +4 -2
  45. package/dist/chunk-A4TOGAUW.js.map +1 -0
  46. package/dist/{chunk-VCETL77O.js → chunk-AU3AUJIZ.js} +27 -7
  47. package/dist/chunk-AU3AUJIZ.js.map +1 -0
  48. package/dist/{chunk-NKH7GSIH.js → chunk-AUPSEJ7O.js} +4 -4
  49. package/dist/{chunk-CCZJJ4I2.js → chunk-B6IYATME.js} +4 -4
  50. package/dist/{chunk-WYZ5MYUA.js → chunk-BAHOV5I5.js} +44 -42
  51. package/dist/chunk-BAHOV5I5.js.map +1 -0
  52. package/dist/{chunk-G5APARUV.js → chunk-BEZDLIDG.js} +13 -15
  53. package/dist/chunk-BEZDLIDG.js.map +1 -0
  54. package/dist/{chunk-ERB6NDDW.js → chunk-CAGI4LLJ.js} +427 -72
  55. package/dist/chunk-CAGI4LLJ.js.map +1 -0
  56. package/dist/{chunk-4UVDI4XE.js → chunk-CLAHBPOI.js} +4 -4
  57. package/dist/{chunk-HNGSV2YC.js → chunk-CWM4GQD2.js} +39 -10
  58. package/dist/chunk-CWM4GQD2.js.map +1 -0
  59. package/dist/{chunk-HVQGCKY4.js → chunk-D3R7Q4QK.js} +2 -2
  60. package/dist/{chunk-I4F6PKUK.js → chunk-DIZ6HM7Q.js} +16 -17
  61. package/dist/chunk-DIZ6HM7Q.js.map +1 -0
  62. package/dist/{chunk-ZNACADFG.js → chunk-DVRZYAGT.js} +4 -4
  63. package/dist/chunk-DVRZYAGT.js.map +1 -0
  64. package/dist/{chunk-P7OG2ETF.js → chunk-DXKLLKME.js} +3 -3
  65. package/dist/{chunk-TDJPS3T2.js → chunk-E5SC6ENM.js} +24 -19
  66. package/dist/chunk-E5SC6ENM.js.map +1 -0
  67. package/dist/{chunk-26JMGSSC.js → chunk-EES37S2V.js} +4 -4
  68. package/dist/chunk-EES37S2V.js.map +1 -0
  69. package/dist/{chunk-JIUVJ4M7.js → chunk-EKEIEJL6.js} +2 -2
  70. package/dist/{chunk-GASWQ3XG.js → chunk-ESKIAXO2.js} +15 -16
  71. package/dist/chunk-ESKIAXO2.js.map +1 -0
  72. package/dist/{chunk-TOAIYQJR.js → chunk-FFSQFH6D.js} +7 -10
  73. package/dist/chunk-FFSQFH6D.js.map +1 -0
  74. package/dist/{chunk-DG27IUVI.js → chunk-FS7NTGCJ.js} +2 -2
  75. package/dist/{chunk-V5KYPBUR.js → chunk-FW7NGYLT.js} +11 -14
  76. package/dist/chunk-FW7NGYLT.js.map +1 -0
  77. package/dist/{chunk-52QYGZHH.js → chunk-GBBDFE6C.js} +2 -2
  78. package/dist/chunk-GBBDFE6C.js.map +1 -0
  79. package/dist/{chunk-HXGUIPQB.js → chunk-GQXFOAYZ.js} +3 -3
  80. package/dist/{chunk-BTVYJWSN.js → chunk-GTHVPVAR.js} +10 -10
  81. package/dist/{chunk-DMQCLZ4Z.js → chunk-GWO2IDAB.js} +2 -2
  82. package/dist/{chunk-6HFM7EIT.js → chunk-H3G4I3PN.js} +2 -2
  83. package/dist/chunk-HK67PX4J.js +27 -0
  84. package/dist/chunk-HK67PX4J.js.map +1 -0
  85. package/dist/{chunk-E7OBCBXS.js → chunk-HQSPF5IY.js} +22 -18
  86. package/dist/chunk-HQSPF5IY.js.map +1 -0
  87. package/dist/{chunk-VJFMXYKP.js → chunk-HR5R5JOM.js} +2 -2
  88. package/dist/{chunk-FDX7OWWI.js → chunk-HYYVT2KZ.js} +2 -2
  89. package/dist/{chunk-JRCYZEHU.js → chunk-I6QKB7MT.js} +2 -2
  90. package/dist/{chunk-MZ5ZFO2D.js → chunk-ICDHUUMQ.js} +84 -112
  91. package/dist/chunk-ICDHUUMQ.js.map +1 -0
  92. package/dist/{chunk-W5W6KHN2.js → chunk-IGNJOJEP.js} +1 -1
  93. package/dist/chunk-IGNJOJEP.js.map +1 -0
  94. package/dist/{chunk-KUDZVJKQ.js → chunk-IJ46VCWD.js} +2 -2
  95. package/dist/{chunk-4HDYQL3J.js → chunk-INMHEMXX.js} +2 -2
  96. package/dist/{chunk-66W5OOCX.js → chunk-JLYQQNY4.js} +52 -65
  97. package/dist/chunk-JLYQQNY4.js.map +1 -0
  98. package/dist/{chunk-UHXCLPGC.js → chunk-JNMTN5D5.js} +2 -2
  99. package/dist/chunk-JNMTN5D5.js.map +1 -0
  100. package/dist/{chunk-I4ZAO7F7.js → chunk-JWMBG5S3.js} +7 -11
  101. package/dist/chunk-JWMBG5S3.js.map +1 -0
  102. package/dist/chunk-KRVS7YWY.js +123 -0
  103. package/dist/chunk-KRVS7YWY.js.map +1 -0
  104. package/dist/{chunk-AR2JDFAE.js → chunk-KV6JM2IA.js} +2 -2
  105. package/dist/{chunk-PVU7LW5L.js → chunk-KX57HODD.js} +6 -6
  106. package/dist/{chunk-PY7CI6L5.js → chunk-LGFTOU27.js} +553 -275
  107. package/dist/chunk-LGFTOU27.js.map +1 -0
  108. package/dist/chunk-LN7P6MZB.js +190 -0
  109. package/dist/chunk-LN7P6MZB.js.map +1 -0
  110. package/dist/{chunk-UIBGXRDD.js → chunk-LX46B56Q.js} +25 -31
  111. package/dist/chunk-LX46B56Q.js.map +1 -0
  112. package/dist/{chunk-CEJWMBXO.js → chunk-LXECF2ZR.js} +2 -2
  113. package/dist/{chunk-7VHY5E3X.js → chunk-MSLPDFK2.js} +7 -7
  114. package/dist/{chunk-I6SDX7WX.js → chunk-MZTUTEHP.js} +16 -22
  115. package/dist/chunk-MZTUTEHP.js.map +1 -0
  116. package/dist/{chunk-VRA4Q7DA.js → chunk-NCCEDL2K.js} +55 -48
  117. package/dist/chunk-NCCEDL2K.js.map +1 -0
  118. package/dist/{chunk-HNHCQ556.js → chunk-NR2SUJ3H.js} +2 -2
  119. package/dist/{chunk-AHLJSVRL.js → chunk-NW4TY6VC.js} +5 -7
  120. package/dist/chunk-NW4TY6VC.js.map +1 -0
  121. package/dist/{chunk-RKKORO4M.js → chunk-O742QBZZ.js} +10 -10
  122. package/dist/chunk-O742QBZZ.js.map +1 -0
  123. package/dist/{chunk-VN45YLWM.js → chunk-OHOKBMTM.js} +343 -132
  124. package/dist/chunk-OHOKBMTM.js.map +1 -0
  125. package/dist/{chunk-W3TVWF6N.js → chunk-OIBEJN5H.js} +2 -2
  126. package/dist/{chunk-QD724WDJ.js → chunk-OMYCSTXL.js} +4 -4
  127. package/dist/{chunk-Z45YRGFO.js → chunk-ONVD6GBY.js} +2 -2
  128. package/dist/{chunk-TAGFGKHP.js → chunk-OTHHO3KI.js} +7 -10
  129. package/dist/chunk-OTHHO3KI.js.map +1 -0
  130. package/dist/{chunk-3737A6EA.js → chunk-P2MR5UNE.js} +75 -18
  131. package/dist/chunk-P2MR5UNE.js.map +1 -0
  132. package/dist/chunk-PENPHA45.js +66 -0
  133. package/dist/chunk-PENPHA45.js.map +1 -0
  134. package/dist/{chunk-3FSX6LDG.js → chunk-PPCOKVFA.js} +8 -3
  135. package/dist/{chunk-3FSX6LDG.js.map → chunk-PPCOKVFA.js.map} +1 -1
  136. package/dist/{chunk-VOQW5LAM.js → chunk-QSIEL6TL.js} +102 -71
  137. package/dist/chunk-QSIEL6TL.js.map +1 -0
  138. package/dist/chunk-RFO4UXKQ.js +201 -0
  139. package/dist/chunk-RFO4UXKQ.js.map +1 -0
  140. package/dist/{chunk-KCQX65FU.js → chunk-RKCRPPNB.js} +3 -3
  141. package/dist/{chunk-7TSYBD3E.js → chunk-RSXQNFGO.js} +2 -2
  142. package/dist/{chunk-SZ2K3MHL.js → chunk-S56VYR4Q.js} +8 -11
  143. package/dist/{chunk-SZ2K3MHL.js.map → chunk-S56VYR4Q.js.map} +1 -1
  144. package/dist/{chunk-7NXHMW6Y.js → chunk-SYS46BL7.js} +2 -2
  145. package/dist/{chunk-55LKVALY.js → chunk-T7IR4N2S.js} +1 -3
  146. package/dist/chunk-T7IR4N2S.js.map +1 -0
  147. package/dist/{chunk-A67356IR.js → chunk-T7JGWTES.js} +33 -8
  148. package/dist/chunk-T7JGWTES.js.map +1 -0
  149. package/dist/{chunk-WHJKIZS7.js → chunk-TDSN2O2V.js} +27 -149
  150. package/dist/chunk-TDSN2O2V.js.map +1 -0
  151. package/dist/{chunk-747LRSOO.js → chunk-TMBSPLUG.js} +2 -2
  152. package/dist/{chunk-LWSJG3LI.js → chunk-TQPASLY4.js} +7 -3
  153. package/dist/chunk-TQPASLY4.js.map +1 -0
  154. package/dist/{chunk-WKPJXWKR.js → chunk-TX5MKAHA.js} +3 -3
  155. package/dist/{chunk-HFQ6LGIO.js → chunk-U27Y4DJZ.js} +12 -10
  156. package/dist/{chunk-HFQ6LGIO.js.map → chunk-U27Y4DJZ.js.map} +1 -1
  157. package/dist/{chunk-E6427JU2.js → chunk-VVSOPRUE.js} +18 -2
  158. package/dist/chunk-VVSOPRUE.js.map +1 -0
  159. package/dist/{chunk-7G6VDWXB.js → chunk-WVE6YKMX.js} +3 -3
  160. package/dist/chunk-WVE6YKMX.js.map +1 -0
  161. package/dist/{chunk-GLKDWWNO.js → chunk-YALD3SST.js} +16 -14
  162. package/dist/chunk-YALD3SST.js.map +1 -0
  163. package/dist/{chunk-NJDQ4YSV.js → chunk-YKXZHUOW.js} +4 -4
  164. package/dist/{chunk-VW6ZZGCG.js → chunk-ZLHHE5XZ.js} +3 -3
  165. package/dist/{chunk-FUHKW252.js → chunk-ZZXLB6AS.js} +2 -2
  166. package/dist/classified/index.js +5 -5
  167. package/dist/{classified-marker-FTOX7Q5L.js → classified-marker-NA6HUV33.js} +3 -3
  168. package/dist/collection-facade-VBM35WAH.js +45 -0
  169. package/dist/{computed-6G5M3UPH.js → computed-GSTLWSY5.js} +3 -3
  170. package/dist/consent/index.js +10 -10
  171. package/dist/cover/index.js +10 -3
  172. package/dist/custody/index.js +11 -11
  173. package/dist/{dead-filter-ENRIOQIE.js → dead-filter-UALP5DJX.js} +2 -2
  174. package/dist/debug/index.js +1 -1
  175. package/dist/{delegation-SSEPLXXO.js → delegation-JLNXM7KH.js} +24 -23
  176. package/dist/delegation-JLNXM7KH.js.map +1 -0
  177. package/dist/derivations/index.js +12 -12
  178. package/dist/derive-YUO3TQYN.js +22 -0
  179. package/dist/directory/index.js +10 -10
  180. package/dist/{dispatch-RE74SH6L.js → dispatch-4ZHLSFZQ.js} +5 -5
  181. package/dist/{dispatch-BDVGYC6X.js → dispatch-77CB75KB.js} +18 -18
  182. package/dist/{enclave-52VUXNRL.js → enclave-O4NOROCJ.js} +27 -9
  183. package/dist/executor-CZDXBXKE.js +29 -0
  184. package/dist/executor-DVJAWN3B.js +9 -0
  185. package/dist/executor-VRLG4TQJ.js +9 -0
  186. package/dist/export-accessible-UXSMG7AB.js +23 -0
  187. package/dist/extract-partition-TVY7AMKH.js +38 -0
  188. package/dist/{fanout-sidecar-DTBIDOWF.js → fanout-sidecar-NLQ7QAYW.js} +18 -27
  189. package/dist/fanout-sidecar-NLQ7QAYW.js.map +1 -0
  190. package/dist/find-WSARMJBW.js +11 -0
  191. package/dist/forget/index.js +9 -9
  192. package/dist/guards/index.js +7 -7
  193. package/dist/history/index.js +23 -20
  194. package/dist/history/index.js.map +1 -1
  195. package/dist/i18n/index.js +14 -14
  196. package/dist/index.d.ts +33 -15
  197. package/dist/index.js +187 -191
  198. package/dist/index.js.map +1 -1
  199. package/dist/indexing/index.js +2 -2
  200. package/dist/introspection/index.js +11 -11
  201. package/dist/issue-3JNJNPT3.js +19 -0
  202. package/dist/kernel/constants.d.ts +18 -0
  203. package/dist/kernel/enclave/crypto.d.ts +24 -4
  204. package/dist/kernel/enclave/index.d.ts +9 -2
  205. package/dist/kernel/enclave/record-aad.d.ts +131 -0
  206. package/dist/kernel/enclave/record-envelope.d.ts +76 -0
  207. package/dist/kernel/enclave/record-keys/envelope-body.d.ts +34 -2
  208. package/dist/kernel/enclave/record-keys/lifecycle.d.ts +22 -5
  209. package/dist/kernel/enclave/record-keys/record-codec.d.ts +47 -14
  210. package/dist/kernel/enclave/record-keys/rekey-blob.d.ts +88 -0
  211. package/dist/kernel/enclave/record-keys/rekey.d.ts +68 -0
  212. package/dist/kernel/enclave/record-keys/tombstone.d.ts +3 -2
  213. package/dist/kernel/errors.d.ts +71 -12
  214. package/dist/kernel/merge-authority.d.ts +25 -0
  215. package/dist/kernel/noydb.d.ts +27 -4
  216. package/dist/kernel/query/builder.d.ts +15 -2
  217. package/dist/kernel/query/join.d.ts +27 -0
  218. package/dist/kernel/sync-policy.d.ts +0 -2
  219. package/dist/kernel/tier-visibility.d.ts +15 -0
  220. package/dist/kernel/types.d.ts +145 -12
  221. package/dist/kernel/vault.d.ts +22 -18
  222. package/dist/kernel/via/index.d.ts +11 -2
  223. package/dist/{ledger-4WJL66BW.js → ledger-MVPNG5OS.js} +11 -11
  224. package/dist/liberate-4LOJOLDT.js +24 -0
  225. package/dist/{link-set-N4ZPV6E6.js → link-set-3POMQR3I.js} +11 -11
  226. package/dist/{managed-secret-OTHS64IF.js → managed-secret-IARIJJ7Q.js} +11 -3
  227. package/dist/materialized-views/index.js +22 -21
  228. package/dist/migrate-cek-XM2ETTZW.js +19 -0
  229. package/dist/migrate-cek-XM2ETTZW.js.map +1 -0
  230. package/dist/money/index.js +12 -8
  231. package/dist/noydb-IRILUDED.js +84 -0
  232. package/dist/overlay-views/index.js +4 -4
  233. package/dist/periods/index.js +22 -12
  234. package/dist/pod/index.js +75 -86
  235. package/dist/pod-handle-MELERCKU.js +41 -0
  236. package/dist/pod-handle-MELERCKU.js.map +1 -0
  237. package/dist/policy/index.js +11 -4
  238. package/dist/port/to/index.d.ts +4 -4
  239. package/dist/port/to/locator.d.ts +54 -8
  240. package/dist/port/with/i18n-strategy.d.ts +4 -1
  241. package/dist/port/with/lookup-strategy.d.ts +4 -1
  242. package/dist/port/with/merge-authority.d.ts +52 -0
  243. package/dist/port/with/service-bus.d.ts +0 -4
  244. package/dist/port/with/strategies.d.ts +2 -0
  245. package/dist/port/with/team-strategy.d.ts +5 -1
  246. package/dist/portability/index.js +8 -8
  247. package/dist/{post-register-ZAG2CAIY.js → post-register-Z5T7FY45.js} +6 -6
  248. package/dist/query/index.js +3 -3
  249. package/dist/reduce/index.js +4 -4
  250. package/dist/register-UZ37QPDB.js +32 -0
  251. package/dist/registry-ETJNA7PO.js +9 -0
  252. package/dist/registry-F7U6KSLX.js +9 -0
  253. package/dist/registry-L4ELKX6C.js +18 -0
  254. package/dist/registry-SDT5LIGO.js +19 -0
  255. package/dist/request-withdrawal-PVFEYX57.js +31 -0
  256. package/dist/{reveal-CMOJY4VO.js → reveal-DQ2E3QAZ.js} +6 -6
  257. package/dist/revoke-YXY6SHUH.js +24 -0
  258. package/dist/satellites/index.js +1 -1
  259. package/dist/schema-update/index.js +2 -2
  260. package/dist/sealed-record/index.js +20 -13
  261. package/dist/sealed-record/index.js.map +1 -1
  262. package/dist/search/index.js +4 -4
  263. package/dist/{seed-UK4QRIUN.js → seed-DZ4ORU5N.js} +19 -22
  264. package/dist/seed-DZ4ORU5N.js.map +1 -0
  265. package/dist/sequence/index.js +9 -9
  266. package/dist/session/index.js +10 -10
  267. package/dist/shadow/index.js +2 -2
  268. package/dist/share-link/index.js +1 -1
  269. package/dist/signer-Q7XQBMPA.js +25 -0
  270. package/dist/snapshots/index.js +10 -10
  271. package/dist/snapshots/index.js.map +1 -1
  272. package/dist/{stale-ZWXSOWJI.js → stale-NI7SAXAZ.js} +10 -10
  273. package/dist/storage-3VNGSHXS.js +23 -0
  274. package/dist/{store-coordination-provider-PB6QZ57W.js → store-coordination-provider-TYKNR3OU.js} +18 -12
  275. package/dist/store-coordination-provider-TYKNR3OU.js.map +1 -0
  276. package/dist/sync/index.js +11 -11
  277. package/dist/team/index.js +15 -14
  278. package/dist/tiers/index.js +11 -10
  279. package/dist/to/index.js +17 -9
  280. package/dist/to/index.js.map +1 -1
  281. package/dist/transactions/index.js +4 -4
  282. package/dist/{ulid-PTAIMDG4.js → ulid-VQW6ERQN.js} +2 -2
  283. package/dist/util/index.js +1 -1
  284. package/dist/vault-head/index.js +115 -0
  285. package/dist/vault-head/index.js.map +1 -0
  286. package/dist/{verify-RO5RTZOJ.js → verify-6J2YLKSO.js} +5 -5
  287. package/dist/via/lookup/handle.d.ts +4 -1
  288. package/dist/via/money/exact.d.ts +44 -0
  289. package/dist/via/money/index.d.ts +2 -0
  290. package/dist/via/money/normalize.d.ts +20 -0
  291. package/dist/walk-WRA7RAPY.js +21 -0
  292. package/dist/with-audit/guards/immutable-guard.d.ts +14 -0
  293. package/dist/with-audit/periods/active.d.ts +28 -1
  294. package/dist/with-audit/periods/index.d.ts +4 -2
  295. package/dist/with-audit/periods/periods.d.ts +161 -6
  296. package/dist/with-audit/periods/strategy.d.ts +14 -5
  297. package/dist/with-audit/periods/vault-facade.d.ts +89 -10
  298. package/dist/with-audit/periods/window.d.ts +10 -0
  299. package/dist/with-audit/sealed-record/index.d.ts +13 -0
  300. package/dist/with-audit/tiers/index.d.ts +17 -2
  301. package/dist/with-cargo/adopt-partition.d.ts +1 -1
  302. package/dist/with-cargo/extract-partition.d.ts +1 -1
  303. package/dist/with-cargo/index.d.ts +2 -2
  304. package/dist/with-commit/history/history.d.ts +26 -0
  305. package/dist/with-commit/history/index.d.ts +2 -1
  306. package/dist/with-commit/history/ledger/store.d.ts +7 -1
  307. package/dist/with-commit/history/strategy.d.ts +11 -1
  308. package/dist/with-commit/numbering/index.d.ts +0 -5
  309. package/dist/with-commit/vault-head/active.d.ts +2 -0
  310. package/dist/with-commit/vault-head/head.d.ts +28 -0
  311. package/dist/with-commit/vault-head/index.d.ts +10 -0
  312. package/dist/with-commit/vault-head/strategy.d.ts +127 -0
  313. package/dist/with-commit/vault-head/verify.d.ts +90 -0
  314. package/dist/with-formula/materialized-views/types.d.ts +52 -0
  315. package/dist/with-party/directory/cover/storage.d.ts +0 -8
  316. package/dist/with-party/directory/storage.d.ts +0 -11
  317. package/dist/with-party/directory/visibility.d.ts +0 -21
  318. package/dist/with-party/policy/storage.d.ts +0 -10
  319. package/dist/with-party/team/index.d.ts +1 -1
  320. package/dist/with-party/team/keyring.d.ts +149 -3
  321. package/dist/with-party/team/noydb-facade.d.ts +5 -1
  322. package/dist/with-party/team/roster-tag.d.ts +101 -0
  323. package/dist/with-party/team/tiers.d.ts +2 -1
  324. package/dist/with-pod/format.d.ts +10 -12
  325. package/dist/with-pod/index.d.ts +10 -10
  326. package/dist/with-pod/open.d.ts +2 -2
  327. package/dist/with-pod/pod-handle.d.ts +19 -0
  328. package/dist/with-pod/pod-store.d.ts +0 -8
  329. package/dist/with-pod/{bundle.d.ts → pod.d.ts} +26 -19
  330. package/dist/with-pod/ulid.d.ts +1 -1
  331. package/dist/with-shape/blobs/blob-set.d.ts +2 -2
  332. package/dist/with-shape/introspection/index.d.ts +1 -0
  333. package/dist/with-sync/engine.d.ts +54 -5
  334. package/dist/with-sync/strategy.d.ts +3 -0
  335. package/dist/withdraw-accessible-4JTDFRFZ.js +28 -0
  336. package/package.json +13 -5
  337. package/dist/api-WVY3H3YH.js +0 -20
  338. package/dist/backup-YYBE5RPC.js.map +0 -1
  339. package/dist/bundle-handle-JEUWU2SJ.js +0 -36
  340. package/dist/bundle-handle-JEUWU2SJ.js.map +0 -1
  341. package/dist/chunk-26JMGSSC.js.map +0 -1
  342. package/dist/chunk-3737A6EA.js.map +0 -1
  343. package/dist/chunk-4MC5JRRX.js.map +0 -1
  344. package/dist/chunk-52QYGZHH.js.map +0 -1
  345. package/dist/chunk-55LKVALY.js.map +0 -1
  346. package/dist/chunk-5PR33AC4.js.map +0 -1
  347. package/dist/chunk-66W5OOCX.js.map +0 -1
  348. package/dist/chunk-7G6VDWXB.js.map +0 -1
  349. package/dist/chunk-A67356IR.js.map +0 -1
  350. package/dist/chunk-AFHQATY4.js +0 -47
  351. package/dist/chunk-AFHQATY4.js.map +0 -1
  352. package/dist/chunk-AHLJSVRL.js.map +0 -1
  353. package/dist/chunk-C2OOEM57.js.map +0 -1
  354. package/dist/chunk-DIMKRG2E.js +0 -131
  355. package/dist/chunk-DIMKRG2E.js.map +0 -1
  356. package/dist/chunk-E6427JU2.js.map +0 -1
  357. package/dist/chunk-E7OBCBXS.js.map +0 -1
  358. package/dist/chunk-EDXAQ6JI.js.map +0 -1
  359. package/dist/chunk-EP7VPR7Y.js.map +0 -1
  360. package/dist/chunk-ERB6NDDW.js.map +0 -1
  361. package/dist/chunk-G5APARUV.js.map +0 -1
  362. package/dist/chunk-GASWQ3XG.js.map +0 -1
  363. package/dist/chunk-GLKDWWNO.js.map +0 -1
  364. package/dist/chunk-HNGSV2YC.js.map +0 -1
  365. package/dist/chunk-I4F6PKUK.js.map +0 -1
  366. package/dist/chunk-I4ZAO7F7.js.map +0 -1
  367. package/dist/chunk-I6SDX7WX.js.map +0 -1
  368. package/dist/chunk-JXIDGFKS.js.map +0 -1
  369. package/dist/chunk-LWSJG3LI.js.map +0 -1
  370. package/dist/chunk-MWKDOHEF.js.map +0 -1
  371. package/dist/chunk-MZ5ZFO2D.js.map +0 -1
  372. package/dist/chunk-NIVEDYJZ.js.map +0 -1
  373. package/dist/chunk-NWCGDAV6.js +0 -45
  374. package/dist/chunk-NWCGDAV6.js.map +0 -1
  375. package/dist/chunk-PJNTR2NV.js.map +0 -1
  376. package/dist/chunk-PRDRGL73.js.map +0 -1
  377. package/dist/chunk-PY7CI6L5.js.map +0 -1
  378. package/dist/chunk-RKKORO4M.js.map +0 -1
  379. package/dist/chunk-TAGFGKHP.js.map +0 -1
  380. package/dist/chunk-TDJPS3T2.js.map +0 -1
  381. package/dist/chunk-TOAIYQJR.js.map +0 -1
  382. package/dist/chunk-UHXCLPGC.js.map +0 -1
  383. package/dist/chunk-UIBGXRDD.js.map +0 -1
  384. package/dist/chunk-V5KYPBUR.js.map +0 -1
  385. package/dist/chunk-VCETL77O.js.map +0 -1
  386. package/dist/chunk-VN45YLWM.js.map +0 -1
  387. package/dist/chunk-VOQW5LAM.js.map +0 -1
  388. package/dist/chunk-VRA4Q7DA.js.map +0 -1
  389. package/dist/chunk-VV4NSB7P.js.map +0 -1
  390. package/dist/chunk-W5W6KHN2.js.map +0 -1
  391. package/dist/chunk-WHJKIZS7.js.map +0 -1
  392. package/dist/chunk-WYZ5MYUA.js.map +0 -1
  393. package/dist/chunk-ZJADGNYF.js.map +0 -1
  394. package/dist/chunk-ZNACADFG.js.map +0 -1
  395. package/dist/collection-facade-QN5ULWYC.js +0 -45
  396. package/dist/delegation-SSEPLXXO.js.map +0 -1
  397. package/dist/derive-6V3PIZF7.js +0 -22
  398. package/dist/executor-CYJQLKKI.js +0 -9
  399. package/dist/executor-FHHLXO4Y.js +0 -28
  400. package/dist/executor-S4QIKEHW.js +0 -9
  401. package/dist/export-accessible-PE5AYKZR.js +0 -23
  402. package/dist/extract-partition-X2L26O6A.js +0 -38
  403. package/dist/fanout-sidecar-DTBIDOWF.js.map +0 -1
  404. package/dist/find-RBBYKXI7.js +0 -11
  405. package/dist/issue-RNU55YOF.js +0 -19
  406. package/dist/liberate-PX4CAT5V.js +0 -24
  407. package/dist/noydb-YPSEGK32.js +0 -83
  408. package/dist/register-TMD6YRCX.js +0 -32
  409. package/dist/registry-CZVINVQH.js +0 -9
  410. package/dist/registry-FVLDWQKT.js +0 -18
  411. package/dist/registry-IKUR2MXD.js +0 -9
  412. package/dist/registry-JET4HBLY.js +0 -19
  413. package/dist/request-withdrawal-RH55LNZL.js +0 -31
  414. package/dist/revoke-R5TBYVA6.js +0 -24
  415. package/dist/seed-UK4QRIUN.js.map +0 -1
  416. package/dist/signer-AGALOXOO.js +0 -25
  417. package/dist/storage-N7LB275C.js +0 -23
  418. package/dist/store-coordination-provider-PB6QZ57W.js.map +0 -1
  419. package/dist/walk-OCUHCYJ6.js +0 -21
  420. package/dist/with-pod/bundle-handle.d.ts +0 -9
  421. package/dist/withdraw-accessible-QQNWM5BO.js +0 -28
  422. /package/dist/{api-WVY3H3YH.js.map → api-EYNT6KGS.js.map} +0 -0
  423. /package/dist/{chunk-35EBKWQU.js.map → chunk-2LFOMUEA.js.map} +0 -0
  424. /package/dist/{chunk-OF3L2SD6.js.map → chunk-32B7VCKU.js.map} +0 -0
  425. /package/dist/{chunk-MSWYQLMQ.js.map → chunk-3B6S6RSI.js.map} +0 -0
  426. /package/dist/{chunk-7F4DBNSP.js.map → chunk-3O72ZD2E.js.map} +0 -0
  427. /package/dist/{chunk-FNSMZQS4.js.map → chunk-3PMFMUFX.js.map} +0 -0
  428. /package/dist/{chunk-FAXZQ5UF.js.map → chunk-4RAS6HN6.js.map} +0 -0
  429. /package/dist/{chunk-VMCCGUQB.js.map → chunk-6JPGVL7X.js.map} +0 -0
  430. /package/dist/{chunk-WLYIROCJ.js.map → chunk-6YD5SYQS.js.map} +0 -0
  431. /package/dist/{chunk-YAWK6AWF.js.map → chunk-7KAE7CQ2.js.map} +0 -0
  432. /package/dist/{chunk-NKH7GSIH.js.map → chunk-AUPSEJ7O.js.map} +0 -0
  433. /package/dist/{chunk-CCZJJ4I2.js.map → chunk-B6IYATME.js.map} +0 -0
  434. /package/dist/{chunk-4UVDI4XE.js.map → chunk-CLAHBPOI.js.map} +0 -0
  435. /package/dist/{chunk-HVQGCKY4.js.map → chunk-D3R7Q4QK.js.map} +0 -0
  436. /package/dist/{chunk-P7OG2ETF.js.map → chunk-DXKLLKME.js.map} +0 -0
  437. /package/dist/{chunk-JIUVJ4M7.js.map → chunk-EKEIEJL6.js.map} +0 -0
  438. /package/dist/{chunk-DG27IUVI.js.map → chunk-FS7NTGCJ.js.map} +0 -0
  439. /package/dist/{chunk-HXGUIPQB.js.map → chunk-GQXFOAYZ.js.map} +0 -0
  440. /package/dist/{chunk-BTVYJWSN.js.map → chunk-GTHVPVAR.js.map} +0 -0
  441. /package/dist/{chunk-DMQCLZ4Z.js.map → chunk-GWO2IDAB.js.map} +0 -0
  442. /package/dist/{chunk-6HFM7EIT.js.map → chunk-H3G4I3PN.js.map} +0 -0
  443. /package/dist/{chunk-VJFMXYKP.js.map → chunk-HR5R5JOM.js.map} +0 -0
  444. /package/dist/{chunk-FDX7OWWI.js.map → chunk-HYYVT2KZ.js.map} +0 -0
  445. /package/dist/{chunk-JRCYZEHU.js.map → chunk-I6QKB7MT.js.map} +0 -0
  446. /package/dist/{chunk-KUDZVJKQ.js.map → chunk-IJ46VCWD.js.map} +0 -0
  447. /package/dist/{chunk-4HDYQL3J.js.map → chunk-INMHEMXX.js.map} +0 -0
  448. /package/dist/{chunk-AR2JDFAE.js.map → chunk-KV6JM2IA.js.map} +0 -0
  449. /package/dist/{chunk-PVU7LW5L.js.map → chunk-KX57HODD.js.map} +0 -0
  450. /package/dist/{chunk-CEJWMBXO.js.map → chunk-LXECF2ZR.js.map} +0 -0
  451. /package/dist/{chunk-7VHY5E3X.js.map → chunk-MSLPDFK2.js.map} +0 -0
  452. /package/dist/{chunk-HNHCQ556.js.map → chunk-NR2SUJ3H.js.map} +0 -0
  453. /package/dist/{chunk-W3TVWF6N.js.map → chunk-OIBEJN5H.js.map} +0 -0
  454. /package/dist/{chunk-QD724WDJ.js.map → chunk-OMYCSTXL.js.map} +0 -0
  455. /package/dist/{chunk-Z45YRGFO.js.map → chunk-ONVD6GBY.js.map} +0 -0
  456. /package/dist/{chunk-KCQX65FU.js.map → chunk-RKCRPPNB.js.map} +0 -0
  457. /package/dist/{chunk-7TSYBD3E.js.map → chunk-RSXQNFGO.js.map} +0 -0
  458. /package/dist/{chunk-7NXHMW6Y.js.map → chunk-SYS46BL7.js.map} +0 -0
  459. /package/dist/{chunk-747LRSOO.js.map → chunk-TMBSPLUG.js.map} +0 -0
  460. /package/dist/{chunk-WKPJXWKR.js.map → chunk-TX5MKAHA.js.map} +0 -0
  461. /package/dist/{chunk-NJDQ4YSV.js.map → chunk-YKXZHUOW.js.map} +0 -0
  462. /package/dist/{chunk-VW6ZZGCG.js.map → chunk-ZLHHE5XZ.js.map} +0 -0
  463. /package/dist/{chunk-FUHKW252.js.map → chunk-ZZXLB6AS.js.map} +0 -0
  464. /package/dist/{classified-marker-FTOX7Q5L.js.map → classified-marker-NA6HUV33.js.map} +0 -0
  465. /package/dist/{collection-facade-QN5ULWYC.js.map → collection-facade-VBM35WAH.js.map} +0 -0
  466. /package/dist/{computed-6G5M3UPH.js.map → computed-GSTLWSY5.js.map} +0 -0
  467. /package/dist/{dead-filter-ENRIOQIE.js.map → dead-filter-UALP5DJX.js.map} +0 -0
  468. /package/dist/{derive-6V3PIZF7.js.map → derive-YUO3TQYN.js.map} +0 -0
  469. /package/dist/{dispatch-RE74SH6L.js.map → dispatch-4ZHLSFZQ.js.map} +0 -0
  470. /package/dist/{dispatch-BDVGYC6X.js.map → dispatch-77CB75KB.js.map} +0 -0
  471. /package/dist/{enclave-52VUXNRL.js.map → enclave-O4NOROCJ.js.map} +0 -0
  472. /package/dist/{executor-CYJQLKKI.js.map → executor-CZDXBXKE.js.map} +0 -0
  473. /package/dist/{executor-FHHLXO4Y.js.map → executor-DVJAWN3B.js.map} +0 -0
  474. /package/dist/{executor-S4QIKEHW.js.map → executor-VRLG4TQJ.js.map} +0 -0
  475. /package/dist/{export-accessible-PE5AYKZR.js.map → export-accessible-UXSMG7AB.js.map} +0 -0
  476. /package/dist/{extract-partition-X2L26O6A.js.map → extract-partition-TVY7AMKH.js.map} +0 -0
  477. /package/dist/{find-RBBYKXI7.js.map → find-WSARMJBW.js.map} +0 -0
  478. /package/dist/{issue-RNU55YOF.js.map → issue-3JNJNPT3.js.map} +0 -0
  479. /package/dist/{ledger-4WJL66BW.js.map → ledger-MVPNG5OS.js.map} +0 -0
  480. /package/dist/{liberate-PX4CAT5V.js.map → liberate-4LOJOLDT.js.map} +0 -0
  481. /package/dist/{link-set-N4ZPV6E6.js.map → link-set-3POMQR3I.js.map} +0 -0
  482. /package/dist/{managed-secret-OTHS64IF.js.map → managed-secret-IARIJJ7Q.js.map} +0 -0
  483. /package/dist/{noydb-YPSEGK32.js.map → noydb-IRILUDED.js.map} +0 -0
  484. /package/dist/{post-register-ZAG2CAIY.js.map → post-register-Z5T7FY45.js.map} +0 -0
  485. /package/dist/{register-TMD6YRCX.js.map → register-UZ37QPDB.js.map} +0 -0
  486. /package/dist/{registry-CZVINVQH.js.map → registry-ETJNA7PO.js.map} +0 -0
  487. /package/dist/{registry-FVLDWQKT.js.map → registry-F7U6KSLX.js.map} +0 -0
  488. /package/dist/{registry-IKUR2MXD.js.map → registry-L4ELKX6C.js.map} +0 -0
  489. /package/dist/{registry-JET4HBLY.js.map → registry-SDT5LIGO.js.map} +0 -0
  490. /package/dist/{request-withdrawal-RH55LNZL.js.map → request-withdrawal-PVFEYX57.js.map} +0 -0
  491. /package/dist/{reveal-CMOJY4VO.js.map → reveal-DQ2E3QAZ.js.map} +0 -0
  492. /package/dist/{revoke-R5TBYVA6.js.map → revoke-YXY6SHUH.js.map} +0 -0
  493. /package/dist/{signer-AGALOXOO.js.map → signer-Q7XQBMPA.js.map} +0 -0
  494. /package/dist/{stale-ZWXSOWJI.js.map → stale-NI7SAXAZ.js.map} +0 -0
  495. /package/dist/{storage-N7LB275C.js.map → storage-3VNGSHXS.js.map} +0 -0
  496. /package/dist/{ulid-PTAIMDG4.js.map → ulid-VQW6ERQN.js.map} +0 -0
  497. /package/dist/{verify-RO5RTZOJ.js.map → verify-6J2YLKSO.js.map} +0 -0
  498. /package/dist/{walk-OCUHCYJ6.js.map → walk-WRA7RAPY.js.map} +0 -0
  499. /package/dist/{withdraw-accessible-QQNWM5BO.js.map → withdraw-accessible-4JTDFRFZ.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/via/money/iso4217.ts","../src/via/money/fixed-point.ts","../src/via/money/paths.ts","../src/via/money/normalize.ts","../src/via/money/where.ts","../src/via/money/money-reducer.ts","../src/via/money/binding.ts","../src/via/money/descriptor.ts","../src/via/money/arith.ts","../src/via/money/branded.ts"],"sourcesContent":["/**\n * ISO 4217 minor-unit (scale) lookup.\n *\n * Maps a currency code to the number of decimal places its minor unit\n * uses — `EUR` → 2 (cents), `JPY` → 0 (no minor unit), `BHD` → 3 (fils).\n * Only listed codes are \"known\"; an unlisted code returns `null` so the\n * descriptor layer can demand an explicit `scale` / `scaleOverrides`\n * rather than silently assuming 2. This avoids the classic bug where a\n * 0-decimal currency (JPY) is stored as if it had cents.\n *\n * Coverage is the common ISO-4217 set; exotic or non-listed codes must\n * declare scale explicitly. This table is the single source of truth for\n * default scale resolution — see {@link scaleForCurrency}.\n */\nconst MINOR_UNITS: Readonly<Record<string, number>> = {\n // 2-decimal majors\n EUR: 2, USD: 2, GBP: 2, CHF: 2, CAD: 2, AUD: 2, NZD: 2, SGD: 2,\n HKD: 2, CNY: 2, INR: 2, BRL: 2, MXN: 2, ZAR: 2, RUB: 2, TRY: 2,\n PLN: 2, SEK: 2, NOK: 2, DKK: 2, CZK: 2, HUF: 2, RON: 2, ILS: 2,\n THB: 2, PHP: 2, MYR: 2, IDR: 2, AED: 2, SAR: 2, QAR: 2, EGP: 2,\n // 0-decimal\n JPY: 0, KRW: 0, ISK: 0, CLP: 0, VND: 0, XOF: 0, XAF: 0, PYG: 0,\n // 3-decimal\n BHD: 3, KWD: 3, OMR: 3, TND: 3, JOD: 3, IQD: 3, LYD: 3,\n}\n\n/**\n * Return the ISO-4217 minor-unit scale for a currency code, or `null`\n * when the code is not in the known set (caller must supply an explicit\n * scale).\n */\nexport function scaleForCurrency(code: string): number | null {\n const v = MINOR_UNITS[code]\n return v === undefined ? null : v\n}\n","/**\n * Pure fixed-point decimal core for the money descriptor.\n *\n * All conversion goes decimal-string ⇄ scaled `BigInt`. There is no\n * floating-point arithmetic anywhere: a value like `123.45` becomes\n * `12345n` purely by string manipulation, never by `value * 100` (which\n * would reintroduce the very drift money() exists to eliminate). BigInt\n * has no magnitude ceiling, so values past `Number.MAX_SAFE_INTEGER`\n * stay exact end-to-end.\n *\n * This module knows nothing about currencies, descriptors, or storage —\n * it is the isolated, exhaustively-tested arithmetic kernel.\n */\n\nexport type RoundingMode =\n | 'half-up'\n | 'half-even'\n | 'half-down'\n | 'up'\n | 'down'\n | 'ceil'\n | 'floor'\n\nexport type ParseResult =\n | { ok: true; value: bigint }\n | { ok: false; reason: 'precision' | 'nonfinite' }\n\n/**\n * Expand exponent notation (`1e-7`, `1.5e3`) into a plain decimal\n * string. Returns the input unchanged when it carries no exponent.\n */\nfunction expandExponent(s: string): string {\n const m = /^([+-]?)(\\d+)(?:\\.(\\d+))?[eE]([+-]?\\d+)$/.exec(s)\n if (!m) return s\n const sign = m[1] === '-' ? '-' : ''\n const intp = m[2]!\n const frac = m[3] ?? ''\n const exp = Number(m[4]!)\n const digits = intp + frac\n const pointPos = intp.length + exp\n let body: string\n if (pointPos <= 0) {\n body = '0.' + '0'.repeat(-pointPos) + digits\n } else if (pointPos >= digits.length) {\n body = digits + '0'.repeat(pointPos - digits.length)\n } else {\n body = digits.slice(0, pointPos) + '.' + digits.slice(pointPos)\n }\n return sign + body\n}\n\n/**\n * Normalize an input to a canonical decimal string, or `null` if it is\n * non-finite / not a valid decimal.\n */\nfunction toCanonicalDecimalString(input: number | string): string | null {\n let s: string\n if (typeof input === 'number') {\n if (!Number.isFinite(input)) return null\n s = String(input)\n } else {\n s = input.trim()\n }\n s = expandExponent(s)\n if (s.startsWith('+')) s = s.slice(1)\n // optional sign, then digits with at most one dot, at least one digit\n if (!/^-?(\\d+(\\.\\d*)?|\\.\\d+)$/.test(s)) return null\n return s\n}\n\n/**\n * Decide whether the truncated magnitude should be incremented by one\n * minor unit, given the discarded fractional tail. Only called when the\n * tail is known to contain a non-zero digit.\n */\nfunction shouldRoundUp(\n negative: boolean,\n lastKeptDigit: number,\n firstDiscarded: number,\n hasMoreNonZeroAfterFirst: boolean,\n mode: RoundingMode,\n): boolean {\n switch (mode) {\n case 'up':\n return true\n case 'down':\n return false\n case 'ceil':\n return !negative\n case 'floor':\n return negative\n case 'half-up':\n return firstDiscarded >= 5\n case 'half-down':\n return firstDiscarded > 5 || (firstDiscarded === 5 && hasMoreNonZeroAfterFirst)\n case 'half-even':\n if (firstDiscarded > 5) return true\n if (firstDiscarded < 5) return false\n // exactly 5 leading the tail\n return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1\n }\n}\n\n/**\n * Parse a decimal (`number | string`) into a scaled `BigInt` at the\n * given scale. When the input carries more fractional precision than\n * `scale`:\n * - `rounding` omitted ⇒ `{ ok: false, reason: 'precision' }`\n * - `rounding` set ⇒ the tail is rounded per the mode.\n * Non-finite / unparseable input ⇒ `{ ok: false, reason: 'nonfinite' }`.\n */\nexport function parseToScaledInt(\n input: number | string,\n scale: number,\n rounding?: RoundingMode,\n): ParseResult {\n const canonical = toCanonicalDecimalString(input)\n if (canonical === null) return { ok: false, reason: 'nonfinite' }\n\n const negative = canonical.startsWith('-')\n const unsigned = negative ? canonical.slice(1) : canonical\n const dot = unsigned.indexOf('.')\n const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot)\n const fracPart = dot === -1 ? '' : unsigned.slice(dot + 1)\n\n const intDigits = intPart === '' ? '0' : intPart\n\n if (fracPart.length <= scale) {\n const keep = fracPart.padEnd(scale, '0')\n const magnitude = BigInt(intDigits + keep)\n return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude }\n }\n\n // More precision than scale — inspect the discarded tail.\n const keep = fracPart.slice(0, scale)\n const tail = fracPart.slice(scale)\n const magnitudeDigits = intDigits + keep\n let magnitude = BigInt(magnitudeDigits)\n\n if (/^0+$/.test(tail)) {\n // tail is all zeros — exact, no rounding required.\n return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude }\n }\n\n if (rounding === undefined) return { ok: false, reason: 'precision' }\n\n const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1])\n const firstDiscarded = Number(tail[0])\n const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1))\n if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {\n magnitude += 1n\n }\n return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude }\n}\n\n/**\n * Number of fractional digits in the canonical decimal rendering of\n * `input` (`'123.45'` → 2, `100` → 0, `'1e-7'` → 7), or `null` when the\n * input is non-finite / not a decimal. Used to infer the working scale\n * of arithmetic helpers from their canonical-string inputs.\n */\nexport function decimalScaleOf(input: number | string): number | null {\n const s = toCanonicalDecimalString(input)\n if (s === null) return null\n const dot = s.indexOf('.')\n return dot === -1 ? 0 : s.length - dot - 1\n}\n\n/**\n * Re-scale a scaled `BigInt` from `fromScale` to `toScale`. Scaling up\n * is exact (append zeros); scaling down rounds the discarded tail per\n * `rounding`. The rounding decision reuses the same kernel as\n * {@link parseToScaledInt}, so `rescaleScaledInt(parse('1.005', 3), 3, 2)`\n * and `parse('1.005', 2, mode)` agree for every mode.\n */\nexport function rescaleScaledInt(\n value: bigint,\n fromScale: number,\n toScale: number,\n rounding: RoundingMode = 'half-up',\n): bigint {\n if (toScale >= fromScale) return value * 10n ** BigInt(toScale - fromScale)\n const drop = fromScale - toScale\n const negative = value < 0n\n const absStr = (negative ? -value : value).toString().padStart(drop + 1, '0')\n const keptStr = absStr.slice(0, absStr.length - drop)\n const tail = absStr.slice(absStr.length - drop)\n let magnitude = BigInt(keptStr)\n if (!/^0+$/.test(tail)) {\n const lastKeptDigit = Number(keptStr[keptStr.length - 1])\n const firstDiscarded = Number(tail[0])\n const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1))\n if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {\n magnitude += 1n\n }\n }\n return negative && magnitude !== 0n ? -magnitude : magnitude\n}\n\n/**\n * Render a scaled `BigInt` back to its canonical decimal string at the\n * given scale. `(12345n, 2)` → `'123.45'`; `(5n, 0)` → `'5'`;\n * `(-1n, 2)` → `'-0.01'`. Exact for any magnitude.\n */\nexport function formatScaledInt(value: bigint, scale: number): string {\n const negative = value < 0n\n const abs = (negative ? -value : value).toString()\n if (scale === 0) return (negative ? '-' : '') + abs\n const padded = abs.padStart(scale + 1, '0')\n const cut = padded.length - scale\n const intPart = padded.slice(0, cut)\n const fracPart = padded.slice(cut)\n return (negative ? '-' : '') + intPart + '.' + fracPart\n}\n","/**\n * Nested-path support for `moneyFields` declarations.\n *\n * A descriptor map key is a PATH, not just a top-level field name:\n *\n * - `'total'` — top-level scalar (the original form)\n * - `'billing.monthlyServiceFee'` — nested object member\n * - `'lineItems[].amount'` — member of every element of an array\n * - `'summary.*'` — every value of a record/map object\n * - `'income[].taxWithheld'`, `'byMonth.*.amount'` — segments compose\n *\n * Paths are parsed ONCE, at collection registration, and a syntactically\n * invalid declaration throws there — loudly, at setup time. (The\n * historical failure mode this kills: a declared-but-unreachable path\n * that was silently ignored, leaving the field un-quantized — a latent\n * 100× bug.) A path that doesn't match a given record at write time is\n * NOT an error — optional fields and empty arrays are legitimate — but\n * a path segment that hits a value of the WRONG SHAPE (`[]` on a\n * non-array, `.*` on a non-object) throws, because that means the\n * declaration and the data model disagree.\n */\n\nimport { ValidationError } from '../../kernel/errors.js'\n\nexport type MoneyPathSegment =\n | { readonly kind: 'key'; readonly key: string; readonly array: boolean }\n | { readonly kind: 'wildcard'; readonly array: boolean }\n\nconst SEGMENT_RE = /^(\\*|[^.[\\]*]+)(\\[\\])?$/\n\nconst parseCache = new Map<string, readonly MoneyPathSegment[]>()\n\n/**\n * Parse a moneyFields path into segments. Throws `ValidationError` on\n * bad syntax. Results are memoized — the same declared paths are walked\n * on every write/read.\n */\nexport function parseMoneyPath(path: string): readonly MoneyPathSegment[] {\n const cached = parseCache.get(path)\n if (cached) return cached\n\n if (typeof path !== 'string' || path.length === 0) {\n throw new ValidationError('moneyFields: path must be a non-empty string')\n }\n const segments: MoneyPathSegment[] = []\n for (const part of path.split('.')) {\n const m = SEGMENT_RE.exec(part)\n if (!m) {\n throw new ValidationError(\n `moneyFields: invalid path \"${path}\" — segment \"${part}\" must be a key, \"key[]\", \"*\", or \"*[]\"`,\n )\n }\n const array = m[2] === '[]'\n segments.push(\n m[1] === '*' ? { kind: 'wildcard', array } : { kind: 'key', key: m[1]!, array },\n )\n }\n parseCache.set(path, segments)\n return segments\n}\n\n/** True when the path is a plain top-level field name (the fast path). */\nexport function isSimpleMoneyPath(path: string): boolean {\n return !path.includes('.') && !path.includes('[') && !path.includes('*')\n}\n\n/**\n * Validate every declared path's syntax. Call at collection\n * registration so typos throw at setup, not silently no-op per write.\n */\nexport function validateMoneyFieldPaths(moneyFields: Record<string, unknown>): void {\n for (const path of Object.keys(moneyFields)) parseMoneyPath(path)\n}\n\n/**\n * The leaf visitor: receives the (already-cloned) container holding the\n * money value and the key/index of that value. Mutating the container\n * is safe — every container on a matched path is a fresh clone.\n */\nexport type MoneyLeafVisitor = (\n container: Record<string, unknown> | unknown[],\n key: string | number,\n) => void\n\n/**\n * Walk `node` along `segments`, copy-on-write-cloning every container on\n * a matched path, and call `visit` at each leaf position. Returns the\n * (possibly new) node; unmatched paths return the input untouched.\n *\n * Shape mismatches (`[]` on a non-array, `*` on a non-object) THROW on\n * the write path — the declaration and the data model disagree, and\n * writing through would store an un-quantized amount. On the READ path\n * pass `lenient: true` instead: stored data predating a declaration\n * change must stay readable, so a mismatched node is returned untouched\n * (mirrors the defensive `continue` the flat decoder always had).\n */\nexport function transformAtMoneyPath(\n node: unknown,\n path: string,\n segments: readonly MoneyPathSegment[],\n index: number,\n visit: MoneyLeafVisitor,\n lenient: boolean,\n): unknown {\n if (node === null || node === undefined) return node\n const seg = segments[index]!\n const last = index === segments.length - 1\n\n if (seg.kind === 'key') {\n if (typeof node !== 'object' || Array.isArray(node)) {\n if (lenient) return node\n throw new ValidationError(\n `moneyFields: path \"${path}\" expected an object at segment \"${seg.key}\", got ${Array.isArray(node) ? 'an array' : typeof node}`,\n )\n }\n const obj = node as Record<string, unknown>\n if (!(seg.key in obj) || obj[seg.key] === null || obj[seg.key] === undefined) return node\n\n if (seg.array) {\n const arr = obj[seg.key]\n if (!Array.isArray(arr)) {\n if (lenient) return node\n throw new ValidationError(\n `moneyFields: path \"${path}\" declares \"${seg.key}[]\" but the value is not an array`,\n )\n }\n const cloned = [...arr]\n if (last) {\n for (let i = 0; i < cloned.length; i++) visit(cloned, i)\n } else {\n for (let i = 0; i < cloned.length; i++) {\n cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient)\n }\n }\n return { ...obj, [seg.key]: cloned }\n }\n\n const clone = { ...obj }\n if (last) {\n visit(clone, seg.key)\n } else {\n clone[seg.key] = transformAtMoneyPath(clone[seg.key], path, segments, index + 1, visit, lenient)\n }\n return clone\n }\n\n // wildcard\n if (seg.array) {\n if (!Array.isArray(node)) {\n if (lenient) return node\n throw new ValidationError(`moneyFields: path \"${path}\" declares \"*[]\" but the value is not an array`)\n }\n const cloned = [...node]\n if (last) {\n for (let i = 0; i < cloned.length; i++) visit(cloned, i)\n } else {\n for (let i = 0; i < cloned.length; i++) {\n cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient)\n }\n }\n return cloned\n }\n if (typeof node !== 'object' || Array.isArray(node)) {\n if (lenient) return node\n throw new ValidationError(\n `moneyFields: path \"${path}\" applies \"*\" to a non-object (${Array.isArray(node) ? 'array — use \"*[]\"' : typeof node})`,\n )\n }\n const obj = node as Record<string, unknown>\n const clone: Record<string, unknown> = { ...obj }\n for (const key of Object.keys(obj)) {\n const v = clone[key]\n if (v === null || v === undefined) continue\n if (last) visit(clone, key)\n else clone[key] = transformAtMoneyPath(v, path, segments, index + 1, visit, lenient)\n }\n return clone\n}\n","/**\n * Write-side and read-side normalization for money fields.\n *\n * - **Write** ({@link quantizeMoneyFields}): user input (`number | string`,\n * or `{ amount, currency }` in multi mode) → the canonical *stored*\n * form — a scaled-integer **digit string** (`'12345'`), or\n * `{ amount: '12345', currency: 'EUR' }` in multi mode. A JSON number\n * would truncate past 2^53, so the integer is always stored as a string.\n * - **Read** ({@link decodeMoneyFields}): stored form → an exact decimal\n * string (`'123.45'`) plus, when formatting is requested, the virtual\n * `<field>Formatted` (locale currency string) and `<field>Number`\n * (convenience JS number, explicitly lossy past 2^53).\n *\n * Both return a shallow clone; neither mutates the input record.\n */\n\nimport { parseToScaledInt, formatScaledInt } from './fixed-point.js'\nimport { MoneyPrecisionError, type MoneyDescriptor } from './descriptor.js'\nimport { isSimpleMoneyPath, parseMoneyPath, transformAtMoneyPath } from './paths.js'\n\ninterface MoneyValueObject {\n amount: unknown\n currency: unknown\n}\n\nfunction isMoneyValueObject(v: unknown): v is MoneyValueObject {\n return typeof v === 'object' && v !== null && 'currency' in v\n}\n\n/** Parse one decimal input to a stored digit string at `scale`. */\nfunction quantizeAmount(\n field: string,\n input: number | string,\n scale: number,\n rounding: MoneyDescriptor['rounding'],\n): string {\n const r = parseToScaledInt(input, scale, rounding)\n if (!r.ok) {\n if (r.reason === 'precision') throw new MoneyPrecisionError(field, input, scale)\n throw new TypeError(`money: field \"${field}\" value ${JSON.stringify(input)} is not a finite decimal`)\n }\n return r.value.toString()\n}\n\n/**\n * Canonicalize a STORED-form record's money fields for an internal\n * callback boundary. Gate handlers (guard `check` /\n * `frozenFields` / `onDelete`, period guard, amendment invariants) and\n * derivation `derive(source, ctx)` callbacks are user-facing: they must\n * see the same decoded canonical decimal that `get()` returns — the\n * scaled-int storage form never escapes. Decoded with `'raw'` (no\n * `<field>Formatted`/`<field>Number` virtuals): these boundaries carry\n * no locale, and fabricating one would re-create a two-read-paths\n * skew inside comparisons.\n */\nexport function canonicalizeStoredMoney(\n record: unknown,\n moneyFields: Record<string, MoneyDescriptor> | undefined,\n): unknown {\n if (record === null || record === undefined) return record\n if (!moneyFields || Object.keys(moneyFields).length === 0) return record\n return decodeMoneyFields(record as Record<string, unknown>, moneyFields, 'raw')\n}\n\n/**\n * Canonicalize an INCOMING record's money fields at the top of the\n * write pipeline. Raw user input (pre-quantize) may hold a\n * number (`10000`), a major-unit string (`'10000.00'`), or a spread of\n * an already-decoded read. Quantize→decode folds all three to the\n * canonical decimal string, so gate handlers, computed-field\n * callbacks, and schema validation all see the `get()` shape — and\n * freeze-style guards comparing `incoming[f]` vs `existing[f]` see\n * equal values for an unchanged field. Best-effort: input that fails\n * to quantize passes through unchanged — the write path quantizes\n * again after validation and surfaces the real\n * `MoneyPrecisionError`/`TypeError`.\n */\nexport function canonicalizeIncomingMoney(\n record: unknown,\n moneyFields: Record<string, MoneyDescriptor> | undefined,\n): unknown {\n if (!moneyFields || Object.keys(moneyFields).length === 0) return record\n try {\n return decodeMoneyFields(\n quantizeMoneyFields(record as Record<string, unknown>, moneyFields),\n moneyFields,\n 'raw',\n )\n } catch {\n return record\n }\n}\n\n/** Quantize ONE field value (any nesting level) to its stored form. */\nfunction quantizeValue(field: string, raw: unknown, desc: MoneyDescriptor): unknown {\n if (desc.mode === 'fixed') {\n const currency = desc.fixedCurrency!\n return quantizeAmount(field, raw as number | string, desc.scaleFor(currency), desc.rounding)\n }\n // multi mode\n let amount: number | string\n let currency: string\n if (isMoneyValueObject(raw)) {\n currency = String(raw.currency)\n amount = raw.amount as number | string\n } else {\n const sole = desc.soleCurrency()\n if (sole === undefined) {\n throw new TypeError(\n `money: field \"${field}\" is multi-currency — write { amount, currency }, not a bare amount`,\n )\n }\n currency = sole\n amount = raw as number | string\n }\n const scale = desc.scaleFor(currency) // throws MoneyCurrencyError if disallowed\n return { amount: quantizeAmount(field, amount, scale, desc.rounding), currency }\n}\n\n/**\n * Convert money fields in `record` from user input to their canonical\n * stored form. Returns a shallow clone (deep along declared nested\n * paths). A nested path whose declared shape disagrees with the\n * data throws: writing through would store an un-quantized amount.\n */\nexport function quantizeMoneyFields<T extends Record<string, unknown>>(\n record: T,\n moneyFields: Record<string, MoneyDescriptor>,\n): T {\n let out: Record<string, unknown> = { ...record }\n for (const [path, desc] of Object.entries(moneyFields)) {\n if (isSimpleMoneyPath(path)) {\n const raw = out[path]\n if (raw === null || raw === undefined) continue\n out[path] = quantizeValue(path, raw, desc)\n continue\n }\n out = transformAtMoneyPath(out, path, parseMoneyPath(path), 0, (container, key) => {\n const raw = (container as Record<string | number, unknown>)[key]\n if (raw === null || raw === undefined) return\n ;(container as Record<string | number, unknown>)[key] = quantizeValue(path, raw, desc)\n }, /* lenient */ false) as Record<string, unknown>\n }\n return out as T\n}\n\nfunction formatCurrency(decimal: string, currency: string, scale: number, locale: string): string {\n const fmt = new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n minimumFractionDigits: scale,\n maximumFractionDigits: scale,\n })\n // V8's Intl.format accepts a decimal STRING and formats it at full\n // precision (exact past 2^53). The TS lib types only declare\n // number|bigint, so cast — a number arg here would re-introduce the\n // float drift this whole feature exists to eliminate.\n return (fmt.format as unknown as (value: string) => string)(decimal)\n}\n\n/**\n * The stored scaled-integer value of a money field as a `BigInt`, for\n * exact numeric comparison (e.g. `orderBy` / sorting), or `null` when the\n * stored value is missing/malformed. Mirrors the stored form `decodeValue`\n * reads: a bare scaled-int (fixed mode) or `{ amount, currency }`\n * (multi-currency). Comparison is in scaled space and is exact within a\n * single currency/scale (the `where` / `sum` BigInt model); across\n * currencies of different scales the raw scaled comparison is best-effort.\n */\nexport function moneyScaledValue(stored: unknown, desc: MoneyDescriptor): bigint | null {\n let raw: unknown\n if (desc.mode === 'fixed') {\n raw = stored\n } else {\n if (!isMoneyValueObject(stored)) return null\n raw = stored.amount\n }\n if (typeof raw !== 'string' && typeof raw !== 'number') return null\n try {\n return BigInt(String(raw))\n } catch {\n return null\n }\n}\n\n/**\n * The `ViaBinding.canonicalizeIndexKey` implementation for money (#672) —\n * bucket an eager index's money field entries by the BigInt-normalized\n * scaled-int string, not the raw stored bytes, so a pre-declaration /\n * non-canonical value (e.g. `'0100'`) lands under the SAME key a canonical\n * write produces (`'100'`). Only FIXED-mode declared money fields\n * participate — multi-mode stores `{ amount, currency }`, which has no\n * single bucketable scalar (mirrors `moneyIndexProbe`'s fixed-only gate,\n * `via/money/where.ts`). `undefined` when `field` isn't a declared\n * fixed-mode money field, or the stored value doesn't parse — in both\n * cases the caller falls back to the raw stringified bucket, which is\n * exactly what the scan (`evaluateMoneyClause`) also treats as a\n * non-match, preserving fast-path/scan parity.\n */\nexport function canonicalizeMoneyIndexKey(\n field: string,\n rawValue: unknown,\n moneyFields: Record<string, MoneyDescriptor>,\n): string | undefined {\n const desc = moneyFields[field]\n if (!desc || desc.mode !== 'fixed') return undefined\n return moneyScaledValue(rawValue, desc)?.toString()\n}\n\n/**\n * Decode ONE stored field value to its read shape, or `null` when the\n * stored value is malformed (defensive — never brick a read).\n */\nfunction decodeValue(\n stored: unknown,\n desc: MoneyDescriptor,\n): { decoded: unknown; decimal: string; currency: string; scale: number } | null {\n let currency: string\n let scaledIntString: string\n if (desc.mode === 'fixed') {\n if (typeof stored !== 'string' && typeof stored !== 'number') return null\n currency = desc.fixedCurrency!\n scaledIntString = String(stored)\n } else {\n if (!isMoneyValueObject(stored)) return null // defensive: malformed stored value\n const amount = stored.amount\n if (typeof stored.currency !== 'string' || (typeof amount !== 'string' && typeof amount !== 'number')) return null\n currency = stored.currency\n scaledIntString = String(amount)\n }\n const scale = desc.scaleFor(currency)\n let decimal: string\n try {\n decimal = formatScaledInt(BigInt(scaledIntString), scale)\n } catch {\n return null // defensive: non-integer stored value\n }\n return {\n decoded: desc.mode === 'fixed' ? decimal : { amount: decimal, currency },\n decimal,\n currency,\n scale,\n }\n}\n\n/**\n * money's `presentLate` hook (#669) — for each field in `virtualMoney` (money ∩\n * virtual-mode computed on the SAME field), quantize the computed fn's fresh\n * MAJOR-UNITS output to the descriptor's scale (with its declared rounding)\n * and present it EXACTLY like a stored money field: the exact decimal string\n * (via {@link formatScaledInt}), plus `<field>Formatted`/`<field>Number` when\n * `locale !== 'raw'`. NEVER the scaled-int decode {@link decodeMoneyFields}\n * runs for a genuinely-stored value — that would misread a virtual field's\n * raw major-unit `21` as 21 SCALED units (`'0.21'`, the #665 corruption this\n * must not reintroduce). Runs AFTER computed's `present()` (the pipeline's\n * `presentLate` fold point, `kernel/via/pipeline.ts`), so `record[field]`\n * already holds the fn's fresh output by the time this runs.\n *\n * Absent/null value → left untouched (nothing to dress). Unparseable value\n * (fails `parseToScaledInt` — e.g. excess precision with no declared\n * rounding) → left RAW, no throw: read-time dressing must never brick a\n * read. Fixed-mode fields only — a virtual field's computed output has no\n * natural `{ amount, currency }` shape to parse for multi-currency mode.\n */\nexport function presentVirtualMoneyFields<T extends Record<string, unknown>>(\n record: T,\n moneyFields: Record<string, MoneyDescriptor>,\n virtualMoney: ReadonlySet<string>,\n locale: string | undefined,\n): T {\n let out: Record<string, unknown> = record\n const format = locale !== 'raw'\n const fmtLocale = typeof locale === 'string' && locale !== 'raw' ? locale : 'en-US'\n for (const field of virtualMoney) {\n const desc = moneyFields[field]\n if (!desc || desc.mode !== 'fixed') continue\n const raw = out[field]\n if (raw === null || raw === undefined) continue\n if (typeof raw !== 'number' && typeof raw !== 'string') continue\n const currency = desc.fixedCurrency!\n const scale = desc.scaleFor(currency)\n const r = parseToScaledInt(raw, scale, desc.rounding)\n if (!r.ok) continue // unparseable / excess precision with no rounding declared — leave raw, no throw\n if (out === record) out = { ...record }\n const decimal = formatScaledInt(r.value, scale)\n out[field] = decimal\n if (format) {\n out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale)\n out[`${field}Number`] = Number(decimal)\n }\n }\n return out as T\n}\n\n/**\n * Convert money fields in `record` from stored form to the read shape:\n * an exact decimal string, plus `<field>Formatted` / `<field>Number`\n * virtuals when `locale !== 'raw'`. Returns a shallow clone (deep along\n * declared nested paths; virtuals land as siblings inside the\n * nested container, e.g. each `lineItems[]` element gains\n * `amountFormatted`, except for values held directly in arrays where a\n * scalar has no sibling slot). The decode walk is LENIENT: stored data\n * predating a declaration change must stay readable.\n */\nexport function decodeMoneyFields<T extends Record<string, unknown>>(\n record: T,\n moneyFields: Record<string, MoneyDescriptor>,\n locale: string | undefined,\n): T {\n let out: Record<string, unknown> = { ...record }\n const format = locale !== 'raw'\n const fmtLocale = typeof locale === 'string' && locale !== 'raw' ? locale : 'en-US'\n\n for (const [path, desc] of Object.entries(moneyFields)) {\n if (isSimpleMoneyPath(path)) {\n const stored = out[path]\n if (stored === null || stored === undefined) continue\n const r = decodeValue(stored, desc)\n if (r === null) continue\n out[path] = r.decoded\n if (format) {\n out[`${path}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale)\n out[`${path}Number`] = Number(r.decimal)\n }\n continue\n }\n out = transformAtMoneyPath(out, path, parseMoneyPath(path), 0, (container, key) => {\n const stored = (container as Record<string | number, unknown>)[key]\n if (stored === null || stored === undefined) return\n const r = decodeValue(stored, desc)\n if (r === null) return\n ;(container as Record<string | number, unknown>)[key] = r.decoded\n if (format && typeof key === 'string' && !Array.isArray(container)) {\n container[`${key}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale)\n container[`${key}Number`] = Number(r.decimal)\n }\n }, /* lenient */ true) as Record<string, unknown>\n }\n return out as T\n}\n","/**\n * Money-aware `where()` comparison.\n *\n * Query clauses evaluate against RAW stored records — money decode\n * happens on output only — so a money field's stored form is a\n * scaled-integer digit string (`'1000000'`) while the caller naturally\n * writes the operand in major units (`10000`, `'10000.00'`). Without a\n * rewrite the comparison is silently wrong by the scale factor, and a\n * string-vs-number comparison is excluded by `isComparable` anyway.\n *\n * Two halves:\n *\n * - {@link moneyFieldClause} runs at QUERY BUILD time: it quantizes the\n * caller's major-unit operand into stored scaled-int space via the\n * same `parseToScaledInt` path as writes, so a malformed operand\n * throws at `.where()` — not silently filters everything out.\n * - {@link evaluateMoneyClause} runs per record and compares\n * `BigInt`-exact in scaled space (exact past 2^53, like the rest of\n * the money service).\n *\n * Currency semantics (multi mode): an operand carries one currency —\n * explicit via `{ amount, currency }`, or the descriptor's sole allowed\n * currency for a bare amount. A record in a DIFFERENT currency has no\n * defined order against the operand: it matches `!=` and nothing else.\n */\n\nimport { parseToScaledInt } from './fixed-point.js'\nimport { MoneyUnsupportedError, type MoneyDescriptor } from './descriptor.js'\nimport type { Operator } from '../../kernel/query/predicate.js'\n\n/** One quantized operand value: scaled digit string + its currency. */\ninterface MoneyOperandEntry {\n readonly scaled: string\n readonly currency: string\n}\n\n/**\n * The opaque `via` clause payload for a `where()` over a declared money\n * field (see `ViaBinding.buildClause` in kernel/via/index.ts). `entries` holds\n * one element for comparison ops, two for `between` (lo, hi — same\n * currency), N for `in`.\n */\nexport interface MoneyWhereOperand {\n readonly mode: 'fixed' | 'multi'\n readonly entries: ReadonlyArray<MoneyOperandEntry>\n}\n\ninterface MoneyValueObject {\n amount: unknown\n currency: unknown\n}\n\nfunction isMoneyValueObject(v: unknown): v is MoneyValueObject {\n return typeof v === 'object' && v !== null && 'currency' in v\n}\n\n/** Quantize ONE operand value to scaled space, resolving its currency. */\nfunction parseOperand(field: string, raw: unknown, desc: MoneyDescriptor): MoneyOperandEntry {\n let amount: unknown\n let currency: string\n if (desc.mode === 'fixed') {\n currency = desc.fixedCurrency!\n amount = raw\n } else if (isMoneyValueObject(raw)) {\n currency = String(raw.currency)\n amount = raw.amount\n } else {\n const sole = desc.soleCurrency()\n if (sole === undefined) {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): field is multi-currency — compare against { amount, currency }, not a bare amount`,\n )\n }\n currency = sole\n amount = raw\n }\n if (typeof amount !== 'number' && typeof amount !== 'string') {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): operand ${JSON.stringify(raw)} is not a money amount`,\n )\n }\n const r = parseToScaledInt(amount, desc.scaleFor(currency), desc.rounding)\n if (!r.ok) {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): operand ${JSON.stringify(amount)} is not a finite decimal`,\n )\n }\n return { scaled: r.value.toString(), currency }\n}\n\n/**\n * Build the `via` clause payload ({@link MoneyWhereOperand}) for a\n * `where()` over a declared money field — the `ViaBinding.buildClause`\n * implementation for money. The operand is quantized into stored\n * scaled-int space NOW — build time — so typos throw at the call site.\n */\nexport function moneyFieldClause(\n field: string,\n op: Operator,\n value: unknown,\n desc: MoneyDescriptor,\n): MoneyWhereOperand {\n switch (op) {\n case '==': case '!=': case '<': case '<=': case '>': case '>=': {\n const e = parseOperand(field, value, desc)\n return { mode: desc.mode, entries: [e] }\n }\n case 'between': {\n if (!Array.isArray(value) || value.length !== 2) {\n throw new MoneyUnsupportedError(`where(\"${field}\"): 'between' needs a [lo, hi] tuple`)\n }\n const lo = parseOperand(field, value[0], desc)\n const hi = parseOperand(field, value[1], desc)\n if (lo.currency !== hi.currency) {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): 'between' bounds mix currencies (${lo.currency} vs ${hi.currency})`,\n )\n }\n return { mode: desc.mode, entries: [lo, hi] }\n }\n case 'in': {\n if (!Array.isArray(value)) {\n throw new MoneyUnsupportedError(`where(\"${field}\"): 'in' needs an array of amounts`)\n }\n return { mode: desc.mode, entries: value.map(v => parseOperand(field, v, desc)) }\n }\n default:\n // contains / startsWith — string ops have no meaning in scaled space.\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): operator '${op}' is not supported on a money field`,\n )\n }\n}\n\n/**\n * The `via` index-probe operand for a `where()` over a declared money\n * field (see `ViaBinding.indexProbe` in kernel/via/index.ts) — the STORED-form\n * value `CollectionIndexes.lookupEqual`/`lookupIn` (`with-lookup/indexing/\n * eager-indexes.ts`) can bucket directly.\n *\n * Only FIXED-mode `==`/`in` are sound:\n * - fixed-mode stores a bare scaled-int digit string (no currency\n * component), and `entries[i].scaled` is built by {@link parseOperand}\n * via the exact same `parseToScaledInt(...).value.toString()` path\n * `quantizeMoneyFields` (`via-money/normalize.ts#quantizeAmount`) uses\n * to produce the STORED value — so the two strings are byte-identical\n * by construction, and `stringifyKey` (which passes a string through\n * unchanged) buckets them the same way.\n * - multi-mode stores `{ amount, currency }` — the index would stringify\n * that object to its no-match `'\\0OBJECT\\0'` sentinel — so multi-mode\n * always returns `undefined` (no sound probe).\n * - every op besides `==`/`in` (range comparisons, `between`) has no\n * single stored-form value the hash index can serve, so they return\n * `undefined` too — the caller falls back to a scan.\n *\n * MIXED-ERA DATA (#672, fixed — including the #672 review's C1 finding): a\n * record whose stored value predates the field's `money()` declaration, or\n * otherwise bypassed `quantizeMoneyFields`, may hold a NON-canonical scaled\n * string (e.g. `'0100'` instead of `'100'`). `CollectionIndexes` no longer\n * buckets it under that raw string verbatim at ANY bucket-mutation site —\n * `build`, `upsert`, and `remove` all consult the same registered\n * canonicalizer (`ViaPipeline.canonicalizeIndexKey`; money's implementation:\n * `canonicalizeMoneyIndexKey`, `via/money/normalize.ts`), which re-parses the\n * stored value the same way the scan's `evaluateMoneyClause`/`readStored`\n * does (`BigInt(actual).toString()`). So the legacy record lands in the SAME\n * bucket a canonical write produces, an `==`/`in` probe for `'100'` finds it,\n * and — because add and remove now agree on that bucket — a later `put()`\n * (update) or `delete()` of that same legacy record cleans up the correct\n * bucket instead of stranding the id under a bucket the removal could never\n * reach. The two-string byte-identity argument above still explains WHY a\n * canonical write's probe hits directly (no canonicalization needed for the\n * common case); this note covers the pre-declaration tail.\n *\n * Boundary — CLOSED for the index layer (#677): LAZY mode's\n * `PersistedCollectionIndex` (`with-lookup/indexing/persisted-indexes.ts`)\n * now consults the SAME `ViaPipeline.canonicalizeIndexKey` at every\n * bucket-mutation site (`ingest`/`upsert`/`remove`) as `CollectionIndexes`\n * does, and its query path (`with-lookup/indexing/lazy-builder.ts`'s\n * `resolveCandidateIds()`) canonicalizes the `==`/`in` probe value before\n * calling `lookupEqual`/`lookupIn` — mirroring `candidateRecords()` above\n * resolving `indexValue` before its own `lookupEqual`/`lookupIn` call. A\n * lazy-mode mixed-era record's bucket and a canonicalized probe now agree,\n * the same as eager mode.\n *\n * Residual boundary CLOSED by #684: `LazyQuery.where()` now builds\n * `clause.via` exactly like `Query.where()`/`ScanBuilder.where()` do, and\n * `LazyQuery`'s post-filter (`lazy-builder.ts`'s `toArray()`) runs it\n * against the RAW record (a dedicated raw-fetch seam on `Collection`, split\n * out of `get()`) instead of the DECODED one — `clause.via.evaluate` (this\n * file's {@link evaluateMoneyClause}) now sees the same stored-form\n * operand/actual-value space eager's `filterRecords` does, at any `scale`.\n * Only survivors are decoded (`present()`) on the way out. Money RANGE\n * clauses additionally route around `PersistedCollectionIndex.lookupRange`\n * (raw/typed, wrong space for scaled-int money) — `resolveCandidateIds()`\n * enumerates the field's indexed ids instead and lets this now-via-aware\n * post-filter apply the correct BigInt-exact comparison. `lazyQuery().\n * where()` on a money field now agrees with `scan().where()` /\n * `query().where()` at every `scale` — for `.where()` results specifically.\n * `orderBy` ordering is a separate, still-open lazy-vs-eager divergence,\n * tracked in #695.\n */\n// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\nexport function moneyIndexProbe(op: Operator, payload: MoneyWhereOperand): unknown | undefined {\n if (payload.mode !== 'fixed') return undefined\n if (op === '==') return payload.entries[0]?.scaled\n if (op === 'in') return payload.entries.map(e => e.scaled)\n return undefined\n}\n\n/** Read a raw STORED money value into scaled space, or null if absent/malformed. */\nfunction readStored(actual: unknown, operand: MoneyWhereOperand): MoneyOperandEntry | null {\n let amount: unknown\n let currency: string\n if (operand.mode === 'fixed') {\n if (typeof actual !== 'string' && typeof actual !== 'number') return null\n amount = actual\n currency = operand.entries[0]?.currency ?? ''\n } else {\n if (!isMoneyValueObject(actual)) return null\n if (typeof actual.currency !== 'string') return null\n amount = actual.amount\n currency = actual.currency\n }\n if (typeof amount !== 'string' && typeof amount !== 'number') return null\n try {\n return { scaled: BigInt(amount).toString(), currency }\n } catch {\n return null\n }\n}\n\n/**\n * Per-record evaluation of a money clause: BigInt-exact comparison in\n * scaled-integer space. `actual` is the RAW stored field value.\n *\n * Missing/malformed stored values and cross-currency comparisons match\n * `!=` only — consistent with the generic clause semantics where an\n * absent field is \"not equal\" and has no defined order.\n */\nexport function evaluateMoneyClause(\n actual: unknown,\n op: Operator,\n operand: MoneyWhereOperand,\n): boolean {\n const stored = readStored(actual, operand)\n if (stored === null) return op === '!='\n const a = BigInt(stored.scaled)\n\n if (op === 'in') {\n return operand.entries.some(\n e => e.currency === stored.currency && BigInt(e.scaled) === a,\n )\n }\n if (op === 'between') {\n const [lo, hi] = operand.entries\n if (!lo || !hi || lo.currency !== stored.currency) return false\n return a >= BigInt(lo.scaled) && a <= BigInt(hi.scaled)\n }\n\n const e = operand.entries[0]\n if (!e) return false\n if (e.currency !== stored.currency) return op === '!='\n const b = BigInt(e.scaled)\n switch (op) {\n case '==': return a === b\n case '!=': return a !== b\n case '<': return a < b\n case '<=': return a <= b\n case '>': return a > b\n case '>=': return a >= b\n default: return false\n }\n}\n","/**\n * Money-aware aggregation: exact `sum` / `min` / `max` over money\n * fields.\n *\n * The generic reducers (`aggregate/reducers.ts`) read a field via\n * `readNumber`, which coerces a string (the stored scaled-integer form\n * of money, e.g. `'12345'`) to `0` — so an un-wrapped money `sum` would\n * silently return zero. {@link wrapMoneyReducers} rewrites any `sum` /\n * `min` / `max` over a declared money field into a reducer that\n * accumulates per-currency `BigInt` totals of the stored integers, so\n * the result is exact for any magnitude (past `Number.MAX_SAFE_INTEGER`\n * included).\n *\n * Results are currency-aware and never silently mix currencies:\n * - **fixed** mode → a single exact decimal string.\n * - **multi** mode → an exact `Record<currency, string>` map.\n * - **`sum` with `convertTo` + `fx`** → a single exact string, with\n * each currency converted via BigInt arithmetic (no float).\n *\n * `remove()` is implemented for every money reducer so they participate\n * in incremental live-aggregation and materialized-view maintenance\n * exactly like the generic reducers they replace.\n */\n\nimport { readPath } from '../../kernel/query/predicate.js'\nimport { formatScaledInt, parseToScaledInt } from './fixed-point.js'\nimport { scaleForCurrency } from './iso4217.js'\nimport { MoneyUnsupportedError } from './descriptor.js'\nimport type { MoneyDescriptor } from './descriptor.js'\nimport type { Reducer } from '../../with-lookup/reduce/reducers.js'\nimport type { ReduceSpec } from '../../with-lookup/reduce/reduction.js'\n\nexport type FxRates = Record<string, number | string>\n\ninterface ReadMoney {\n currency: string\n value: bigint\n}\n\n/**\n * Coerce an arbitrary money-field value into its scaled `BigInt` at the\n * given `scale`. Handles the two shapes a money field can arrive in by\n * the time it reaches a reducer:\n *\n * - **stored form** — a bare scaled-integer string (`'12345'`) or a\n * `bigint`. No decimal point, so `BigInt(v)` is the fast path.\n * - **decoded form** — a canonical decimal string (`'123.45'`), which\n * is what `query().toArray()` / `decodeMoneyFields` produce (UNION\n * arms map over decoded rows). `BigInt('123.45')` throws, so these\n * route through `parseToScaledInt(v, scale)`.\n *\n * A `number` is treated as a decimal magnitude (parsed at `scale`).\n * Anything unparseable → `null`.\n */\nfunction toScaledIntFromAny(v: unknown, scale: number): bigint | null {\n if (typeof v === 'bigint') return v\n if (typeof v === 'number') {\n const r = parseToScaledInt(v, scale)\n return r.ok ? r.value : null\n }\n if (typeof v === 'string') {\n if (!v.includes('.')) {\n // Stored scaled-integer form (e.g. '12345') — no decimal point.\n try {\n return BigInt(v)\n } catch {\n return null\n }\n }\n // Decoded canonical-decimal form (e.g. '123.45').\n const r = parseToScaledInt(v, scale)\n return r.ok ? r.value : null\n }\n return null\n}\n\n/** Read the raw stored money value (scaled integer) from a record. */\nfunction readMoney(record: unknown, field: string, desc: MoneyDescriptor): ReadMoney | null {\n const raw = readPath(record, field)\n if (raw === null || raw === undefined) return null\n if (desc.mode === 'fixed') {\n const cur = desc.fixedCurrency!\n const value = toScaledIntFromAny(raw, desc.scaleFor(cur))\n return value === null ? null : { currency: cur, value }\n }\n // multi mode: stored as { amount, currency }\n if (typeof raw !== 'object') return null\n const o = raw as { amount?: unknown; currency?: unknown }\n if (typeof o.currency !== 'string') return null\n const scale = desc.allows(o.currency) ? desc.scaleFor(o.currency) : 0\n const value = toScaledIntFromAny(o.amount, scale)\n return value === null ? null : { currency: o.currency, value }\n}\n\n/** Resolve the scale to use for a target currency (may be outside the allow-list). */\nfunction targetScaleFor(desc: MoneyDescriptor, currency: string): number {\n if (desc.allows(currency)) return desc.scaleFor(currency)\n const s = scaleForCurrency(currency)\n if (s === null) {\n throw new Error(`money: cannot determine scale for conversion target \"${currency}\"`)\n }\n return s\n}\n\n/** Parse a rate (number | string) into a scaled BigInt + its scale. */\nfunction parseRate(rate: number | string): { int: bigint; scale: number } {\n const s = String(rate).trim()\n const neg = s.startsWith('-')\n const body = neg ? s.slice(1) : s\n const dot = body.indexOf('.')\n const intPart = dot === -1 ? body : body.slice(0, dot)\n const fracPart = dot === -1 ? '' : body.slice(dot + 1)\n const int = BigInt((intPart === '' ? '0' : intPart) + fracPart)\n return { int: neg ? -int : int, scale: fracPart.length }\n}\n\n/** BigInt division of `n / d` with half-even (banker's) rounding. */\nfunction divRoundHalfEven(n: bigint, d: bigint): bigint {\n const q = n / d\n const r = n % d\n const twiceR = (r < 0n ? -r : r) * 2n\n if (twiceR < d) return q\n if (twiceR > d) return q + (n < 0n ? -1n : 1n)\n return q % 2n === 0n ? q : q + (n < 0n ? -1n : 1n)\n}\n\n/** Convert a scaled integer from `srcScale` to `targetScale` applying `rate`. */\nfunction convertScaled(value: bigint, srcScale: number, rate: number | string, targetScale: number): bigint {\n const { int: rateInt, scale: rateScale } = parseRate(rate)\n const product = value * rateInt\n const curScale = srcScale + rateScale\n if (curScale === targetScale) return product\n if (curScale < targetScale) return product * 10n ** BigInt(targetScale - curScale)\n return divRoundHalfEven(product, 10n ** BigInt(curScale - targetScale))\n}\n\nfunction finalizeSum(\n state: Map<string, bigint>,\n desc: MoneyDescriptor,\n convertTo: string | undefined,\n fx: FxRates | undefined,\n): string | Record<string, string> {\n if (convertTo !== undefined) {\n if (fx === undefined) {\n throw new Error(`money: sum convertTo \"${convertTo}\" requires an fx rate map`)\n }\n const targetScale = targetScaleFor(desc, convertTo)\n let total = 0n\n for (const [cur, v] of state) {\n if (cur === convertTo) {\n total += convertScaled(v, desc.scaleFor(cur), 1, targetScale)\n continue\n }\n const rate = fx[`${cur}->${convertTo}`]\n if (rate === undefined) {\n throw new Error(`money: no fx rate for \"${cur}->${convertTo}\"`)\n }\n total += convertScaled(v, desc.scaleFor(cur), rate, targetScale)\n }\n return formatScaledInt(total, targetScale)\n }\n\n if (desc.mode === 'fixed') {\n const cur = desc.fixedCurrency!\n return formatScaledInt(state.get(cur) ?? 0n, desc.scaleFor(cur))\n }\n\n const out: Record<string, string> = {}\n for (const [cur, v] of state) out[cur] = formatScaledInt(v, desc.scaleFor(cur))\n return out\n}\n\nfunction moneySumReducer(\n field: string,\n desc: MoneyDescriptor,\n convertTo: string | undefined,\n fx: FxRates | undefined,\n): Reducer<unknown, Map<string, bigint>> {\n return {\n op: 'sum',\n field,\n init: () => new Map<string, bigint>(),\n step: (state, record) => {\n const m = readMoney(record, field, desc)\n if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) + m.value)\n return state\n },\n remove: (state, record) => {\n const m = readMoney(record, field, desc)\n if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) - m.value)\n return state\n },\n finalize: (state) => finalizeSum(state, desc, convertTo, fx),\n }\n}\n\nfunction extremum(values: readonly bigint[], op: 'min' | 'max'): bigint {\n let out = values[0]!\n for (let i = 1; i < values.length; i++) {\n const v = values[i]!\n if (op === 'min' ? v < out : v > out) out = v\n }\n return out\n}\n\nfunction moneyMinMaxReducer(\n op: 'min' | 'max',\n field: string,\n desc: MoneyDescriptor,\n): Reducer<unknown, Map<string, bigint[]>> {\n return {\n op,\n field,\n init: () => new Map<string, bigint[]>(),\n step: (state, record) => {\n const m = readMoney(record, field, desc)\n if (m) {\n const arr = state.get(m.currency)\n if (arr) arr.push(m.value)\n else state.set(m.currency, [m.value])\n }\n return state\n },\n remove: (state, record) => {\n const m = readMoney(record, field, desc)\n if (m) {\n const arr = state.get(m.currency)\n if (arr) {\n const idx = arr.indexOf(m.value)\n if (idx >= 0) arr.splice(idx, 1)\n }\n }\n return state\n },\n finalize: (state) => {\n if (desc.mode === 'fixed') {\n const cur = desc.fixedCurrency!\n const arr = state.get(cur)\n if (!arr || arr.length === 0) return null\n return formatScaledInt(extremum(arr, op), desc.scaleFor(cur))\n }\n const out: Record<string, string> = {}\n for (const [cur, arr] of state) {\n if (arr.length > 0) out[cur] = formatScaledInt(extremum(arr, op), desc.scaleFor(cur))\n }\n return out\n },\n }\n}\n\n/**\n * Rewrite any `sum` / `min` / `max` reducer over a declared money field\n * into its exact BigInt money-aware equivalent. Other reducers (and\n * reducers over non-money fields) pass through unchanged. Returns a new\n * spec; the input is not mutated.\n */\nexport function wrapMoneyReducers(\n spec: ReduceSpec,\n moneyFields: Record<string, MoneyDescriptor>,\n): ReduceSpec {\n let changed = false\n const out: Record<string, Reducer<unknown, unknown>> = {}\n for (const [key, reducer] of Object.entries(spec)) {\n const field = reducer.field\n const desc = field ? moneyFields[field] : undefined\n if (desc && reducer.op === 'avg') {\n throw new MoneyUnsupportedError(\n field!,\n `avg() is not supported on money field \"${field}\" in v1 — use sum() and count() and divide at the boundary.`,\n )\n }\n if (desc && (reducer.op === 'sum' || reducer.op === 'min' || reducer.op === 'max')) {\n changed = true\n out[key] =\n reducer.op === 'sum'\n ? moneySumReducer(field!, desc, reducer.convertTo, reducer.fx as FxRates | undefined)\n : (moneyMinMaxReducer(reducer.op, field!, desc) as Reducer<unknown, unknown>)\n } else {\n out[key] = reducer\n }\n }\n return changed ? out : spec\n}\n","/**\n * The money `ViaBinding` — wires the money engine (normalize/paths/where/\n * money-reducer) into the kernel's generic Via port. `money()`\n * (descriptor.ts) calls {@link linkMoneyVia} at declaration time — the\n * same #553 static-link pattern the retired `linkMoneyEngine()` /\n * `kernel/money-runtime.ts` seam used, now the only one (Task 6 cut the\n * query DSL over to this binding and deleted the legacy seam).\n */\nimport type { ViaBinding, ViaReadCtx } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport type { MoneyDescriptor } from './descriptor.js'\nimport { quantizeMoneyFields, decodeMoneyFields, canonicalizeStoredMoney, canonicalizeIncomingMoney, moneyScaledValue, canonicalizeMoneyIndexKey, presentVirtualMoneyFields } from './normalize.js'\nimport { validateMoneyFieldPaths } from './paths.js'\nimport { moneyFieldClause, evaluateMoneyClause, moneyIndexProbe, type MoneyWhereOperand } from './where.js'\nimport { wrapMoneyReducers } from './money-reducer.js'\nimport type { Operator } from '../../kernel/query/predicate.js'\nimport type { ReduceSpec } from '../../with-lookup/reduce/reduction.js'\n\n/** #669 — the money binder's config bag. `virtualMoneyFields` is the money∩virtual-mode-\n * computed field-name intersection (`kernel/collection-config.ts#resolveVirtualMoneyFields`);\n * absent/empty means no field on this collection composes money with a virtual computed\n * field on itself — the pre-#669 behavior (no `presentLate`, ordinary `present()` for\n * every declared money field). */\nexport interface MoneyBindingConfig {\n readonly moneyFields: Record<string, MoneyDescriptor>\n readonly virtualMoneyFields?: ReadonlySet<string>\n}\n\nexport function moneyBinding(moneyFields: Record<string, MoneyDescriptor>, virtualMoneyFields?: ReadonlySet<string>): ViaBinding {\n validateMoneyFieldPaths(moneyFields) // declaration-time (replaces sites 1 & 2)\n const hasVirtualMoney = virtualMoneyFields !== undefined && virtualMoneyFields.size > 0\n // #669 — a field that is BOTH money AND virtual-mode computed has no value yet when\n // money's ORDINARY present() runs (money is first in `_presentOrder`, computed second —\n // the #665 invariant); decodeMoneyFields already no-ops on an absent value, but the skip\n // is made explicit here so the intent reads at the call site: dressing this field happens\n // in `presentLate` below, once computed's present() has actually produced a value.\n const ordinaryPresentFields = hasVirtualMoney\n ? Object.fromEntries(Object.entries(moneyFields).filter(([f]) => !virtualMoneyFields.has(f)))\n : moneyFields\n return {\n brand: 'money',\n posture: { encryptedAtRest: 'envelope', queryable: 'ordered', exportable: true, forgettable: true },\n covers: (field) => field in moneyFields,\n ingest: (r) => canonicalizeIncomingMoney(r, moneyFields) as Record<string, unknown>,\n canonicalizeStored: (r) => canonicalizeStoredMoney(r, moneyFields) as Record<string, unknown>,\n encodeWrite: (r) => quantizeMoneyFields(r, moneyFields),\n present: (r, ctx) => decodeMoneyFields(r, ordinaryPresentFields, typeof ctx.locale === 'string' ? ctx.locale : undefined),\n ...(hasVirtualMoney\n ? {\n presentLate: (r: Record<string, unknown>, ctx: ViaReadCtx) =>\n presentVirtualMoneyFields(r, moneyFields, virtualMoneyFields, typeof ctx.locale === 'string' ? ctx.locale : undefined),\n }\n : {}),\n buildClause: (field, op, value) => {\n const desc = moneyFields[field]\n if (!desc) return undefined\n return moneyFieldClause(field, op as Operator, value, desc) // the MoneyWhereOperand payload\n },\n evaluateClause: (actual, op, payload) => evaluateMoneyClause(actual, op as Operator, payload as MoneyWhereOperand),\n indexProbe: (op, payload) => moneyIndexProbe(op as Operator, payload as MoneyWhereOperand),\n canonicalizeIndexKey: (field, rawValue) => canonicalizeMoneyIndexKey(field, rawValue, moneyFields),\n decodeResults: (r) => decodeMoneyFields(r as Record<string, unknown>, moneyFields, 'raw'),\n compareForOrder: (field, a, b) => {\n const desc = moneyFields[field]\n if (!desc) return undefined\n const av = moneyScaledValue(a, desc); const bv = moneyScaledValue(b, desc)\n if (av === null) return bv === null ? 0 : 1\n if (bv === null) return -1\n return av < bv ? -1 : av > bv ? 1 : 0\n },\n wrapReducers: (spec) => wrapMoneyReducers(spec as ReduceSpec, moneyFields),\n }\n}\n\nexport function linkMoneyVia(): void {\n installViaBinder('money', (cfg) => {\n const c = cfg as MoneyBindingConfig\n return moneyBinding(c.moneyFields, c.virtualMoneyFields)\n })\n}\n","/**\n * The `money()` field descriptor — a branded schema-layer descriptor, a\n * sibling of `i18nText()` / `dictKey()`. It owns currency, scale, and\n * rounding policy for a field; the pure arithmetic lives in\n * {@link ./fixed-point} and default scale resolution in\n * {@link ./iso4217}.\n *\n * Two modes:\n * - **fixed** `money({ currency: 'EUR' })` — one currency for the\n * field; the value stores a bare scaled-integer string.\n * - **multi** `money({ currencies: 'any' | [...] })` — currency travels\n * per record; the value stores `{ amount, currency }`.\n *\n * `currency` and `currencies` are mutually exclusive.\n */\n\nimport type { RoundingMode } from './fixed-point.js'\nimport { scaleForCurrency } from './iso4217.js'\nimport { NoydbError } from '../../kernel/errors.js'\nimport { linkMoneyVia } from './binding.js'\n\nexport interface MoneyOptionsFixed {\n currency: string\n /** Override the ISO-4217 default scale (required for unlisted codes). */\n scale?: number\n rounding?: RoundingMode\n}\n\nexport interface MoneyOptionsMulti {\n currencies: 'any' | readonly string[]\n /** Per-currency scale overrides (required for unlisted codes). */\n scaleOverrides?: Record<string, number>\n rounding?: RoundingMode\n}\n\nexport type MoneyOptions = MoneyOptionsFixed | MoneyOptionsMulti\n\nexport interface MoneyDescriptor {\n readonly _noydbMoney: true\n /** Via port brand marker — lets a `MoneyDescriptor` satisfy the kernel's opaque `ViaDescriptor`. */\n readonly _viaBrand: 'money'\n readonly mode: 'fixed' | 'multi'\n readonly options: MoneyOptions\n readonly rounding: RoundingMode | undefined\n /** The currency for fixed mode; `undefined` in multi mode. */\n readonly fixedCurrency: string | undefined\n /** Resolve the scale for a currency, throwing if not allowed / unknown. */\n scaleFor(currency: string): number\n /** Whether this descriptor permits the given currency. */\n allows(currency: string): boolean\n /**\n * The single currency this descriptor implies, if any — fixed mode, or\n * multi mode with exactly one allow-listed currency. Lets a multi field\n * accept a bare amount unambiguously. `undefined` otherwise.\n */\n soleCurrency(): string | undefined\n}\n\n/** Raised when a written value carries more precision than `scale` allows. */\nexport class MoneyPrecisionError extends NoydbError {\n constructor(\n public readonly field: string,\n public readonly value: unknown,\n public readonly scale: number,\n ) {\n super(\n 'MONEY_PRECISION',\n `money: value ${JSON.stringify(value)} for field \"${field}\" exceeds scale ${scale} ` +\n `and no rounding mode is configured`,\n )\n this.name = 'MoneyPrecisionError'\n }\n}\n\n/** Raised when a currency is disallowed or has no resolvable scale. */\nexport class MoneyCurrencyError extends NoydbError {\n constructor(\n public readonly currency: string,\n public readonly reason: 'not-allowed' | 'unknown-scale',\n public readonly field?: string,\n ) {\n super(\n 'MONEY_CURRENCY',\n reason === 'not-allowed'\n ? `money: currency \"${currency}\" is not allowed${field ? ` for field \"${field}\"` : ''}`\n : `money: no scale known for currency \"${currency}\"${field ? ` (field \"${field}\")` : ''} — ` +\n `pass an explicit scale / scaleOverrides`,\n )\n this.name = 'MoneyCurrencyError'\n }\n}\n\n/** Raised when an aggregate operation is not supported on a money field. */\nexport class MoneyUnsupportedError extends NoydbError {\n constructor(\n public readonly field: string,\n message?: string,\n ) {\n super(\n 'MONEY_UNSUPPORTED',\n message ??\n `money: operation is not supported on field \"${field}\" — use sum() and count() and divide at the boundary`,\n )\n this.name = 'MoneyUnsupportedError'\n }\n}\n\nfunction isMultiOptions(o: MoneyOptions): o is MoneyOptionsMulti {\n return 'currencies' in o\n}\n\n/** Create a {@link MoneyDescriptor}. */\nexport function money(options: MoneyOptions): MoneyDescriptor {\n // The declaration is the Via binding's opt-in unit (#553): constructing\n // a descriptor statically links normalize/paths/where/money-reducer into\n // the kernel's Via port, so every later kernel consultation (including\n // the SYNC query DSL) finds the binding already installed.\n linkMoneyVia()\n const hasFixed = 'currency' in options\n const hasMulti = 'currencies' in options\n if (hasFixed && hasMulti) {\n throw new TypeError('money: `currency` and `currencies` are mutually exclusive')\n }\n if (!hasFixed && !hasMulti) {\n throw new TypeError('money: one of `currency` or `currencies` is required')\n }\n\n const rounding = options.rounding\n\n if (isMultiOptions(options)) {\n const overrides = options.scaleOverrides ?? {}\n const allowList = options.currencies\n const allows = (c: string): boolean =>\n allowList === 'any' ? true : allowList.includes(c)\n const scaleFor = (c: string): number => {\n if (!allows(c)) throw new MoneyCurrencyError(c, 'not-allowed')\n const s = overrides[c] ?? scaleForCurrency(c)\n if (s === null || s === undefined) throw new MoneyCurrencyError(c, 'unknown-scale')\n return s\n }\n // Eagerly validate the allow-list resolves (catch typos at construction).\n if (allowList !== 'any') for (const c of allowList) scaleFor(c)\n const soleCurrency = (): string | undefined =>\n allowList !== 'any' && allowList.length === 1 ? allowList[0] : undefined\n return {\n _noydbMoney: true,\n _viaBrand: 'money',\n mode: 'multi',\n options,\n rounding,\n fixedCurrency: undefined,\n scaleFor,\n allows,\n soleCurrency,\n }\n }\n\n // fixed\n const currency = options.currency\n const resolvedScale = options.scale ?? scaleForCurrency(currency)\n if (resolvedScale === null || resolvedScale === undefined) {\n throw new MoneyCurrencyError(currency, 'unknown-scale')\n }\n return {\n _noydbMoney: true,\n _viaBrand: 'money',\n mode: 'fixed',\n options,\n rounding,\n fixedCurrency: currency,\n scaleFor(c: string): number {\n if (c !== currency) throw new MoneyCurrencyError(c, 'not-allowed')\n return resolvedScale\n },\n allows: (c: string): boolean => c === currency,\n soleCurrency: (): string | undefined => currency,\n }\n}\n\n/** Runtime predicate for detecting a {@link MoneyDescriptor}. */\nexport function isMoneyDescriptor(x: unknown): x is MoneyDescriptor {\n return (\n typeof x === 'object' &&\n x !== null &&\n (x as { _noydbMoney?: unknown })._noydbMoney === true\n )\n}\n","/**\n * Exact money arithmetic helpers — `mulRate` and `allocate`.\n *\n * Both operate on the canonical decoded string form that `get()`\n * returns (`'10000.00'`), entirely in scaled-`BigInt` space — no\n * floating-point step anywhere, exact past 2^53. They are pure\n * functions with no vault or descriptor dependency: the working scale\n * is inferred from the amount's fractional digits (a canonical money\n * string always carries exactly the field's scale) or pinned with\n * `opts.scale`.\n *\n * Why these two: app-side invariants of the form\n * `Σ parts === whole` (receipt totals, net-zero WHT pairs, proration)\n * carry ±0.01 tolerances ONLY because the math is major-unit floats +\n * round2. `mulRate` (VAT-style rate application with explicit\n * rounding) and `allocate` (largest-remainder split — parts sum to the\n * input EXACTLY, by construction) make those tolerances zero.\n */\n\nimport {\n parseToScaledInt,\n formatScaledInt,\n rescaleScaledInt,\n decimalScaleOf,\n type RoundingMode,\n} from './fixed-point.js'\nimport { MoneyUnsupportedError } from './descriptor.js'\n\nexport interface MulRateOptions {\n /**\n * Output scale (fraction digits). Defaults to the amount's own\n * fractional digits — for a canonical money string that IS the\n * field's scale.\n */\n readonly scale?: number\n /** Rounding for the final re-scale. Default `'half-up'`. */\n readonly rounding?: RoundingMode\n}\n\nexport interface AllocateOptions {\n /** Working scale. Defaults to the amount's own fractional digits. */\n readonly scale?: number\n}\n\n/** Parse `amount` at `scale` or throw the standard money TypeError. */\nfunction parseAmount(label: string, amount: number | string, scale: number, rounding?: RoundingMode): bigint {\n const r = parseToScaledInt(amount, scale, rounding)\n if (!r.ok) {\n throw new MoneyUnsupportedError(\n r.reason === 'precision'\n ? `${label}: amount ${JSON.stringify(amount)} has more precision than scale ${scale} and no rounding mode is configured`\n : `${label}: amount ${JSON.stringify(amount)} is not a finite decimal`,\n )\n }\n return r.value\n}\n\nfunction resolveScale(label: string, amount: number | string, explicit: number | undefined): number {\n if (explicit !== undefined) {\n if (!Number.isInteger(explicit) || explicit < 0) {\n throw new MoneyUnsupportedError(`${label}: scale must be a non-negative integer`)\n }\n return explicit\n }\n const inferred = decimalScaleOf(amount)\n if (inferred === null) {\n throw new MoneyUnsupportedError(`${label}: amount ${JSON.stringify(amount)} is not a finite decimal`)\n }\n return inferred\n}\n\n/**\n * Multiply a money amount by a decimal rate, exactly.\n *\n * The rate is parsed at its OWN full precision (`0.07` → `7` at scale\n * 2), the product computed in `BigInt` at `amountScale + rateScale`,\n * then re-scaled back to the output scale with the requested rounding —\n * one rounding step, at the very end.\n *\n * ```ts\n * mulRate('10000.00', 0.07) // '700.00' (VAT)\n * mulRate('33.33', '0.07') // '2.33' (half-up)\n * mulRate('33.33', 0.07, { rounding: 'floor' }) // '2.33'\n * mulRate(100, 0.07, { scale: 2 }) // '7.00'\n * ```\n */\nexport function mulRate(\n amount: number | string,\n rate: number | string,\n opts: MulRateOptions = {},\n): string {\n const scale = resolveScale('mulRate', amount, opts.scale)\n const rounding = opts.rounding ?? 'half-up'\n const a = parseAmount('mulRate', amount, scale, rounding)\n\n const rateScale = decimalScaleOf(rate)\n if (rateScale === null) {\n throw new MoneyUnsupportedError(`mulRate: rate ${JSON.stringify(rate)} is not a finite decimal`)\n }\n const r = parseToScaledInt(rate, rateScale)\n if (!r.ok) {\n throw new MoneyUnsupportedError(`mulRate: rate ${JSON.stringify(rate)} is not a finite decimal`)\n }\n\n const product = a * r.value // scaled at scale + rateScale\n return formatScaledInt(rescaleScaledInt(product, scale + rateScale, scale, rounding), scale)\n}\n\n/**\n * Split a money amount across weighted buckets with ZERO drift: the\n * returned parts sum to the input exactly, by construction\n * (largest-remainder method).\n *\n * Each bucket gets `floor(amount × weightᵢ / Σweights)`; the leftover\n * minor units (always fewer than the bucket count) go one each to the\n * buckets with the largest truncated remainders — ties broken by\n * position, earlier bucket first. Weights are parsed as exact decimals\n * (no float division anywhere); they must be non-negative with a\n * positive sum.\n *\n * ```ts\n * allocate('100.00', [1, 1, 1]) // ['33.34', '33.33', '33.33']\n * allocate('0.05', [3, 7]) // ['0.02', '0.03']\n * allocate('-100.00', [1, 1, 1]) // ['-33.34', '-33.33', '-33.33']\n * ```\n */\nexport function allocate(\n amount: number | string,\n weights: ReadonlyArray<number | string>,\n opts: AllocateOptions = {},\n): string[] {\n if (weights.length === 0) {\n throw new MoneyUnsupportedError('allocate: weights must not be empty')\n }\n const scale = resolveScale('allocate', amount, opts.scale)\n const a = parseAmount('allocate', amount, scale)\n\n // Parse every weight as an exact decimal at a common scale, so the\n // proportion arithmetic below is pure BigInt.\n let weightScale = 0\n for (const w of weights) {\n const s = decimalScaleOf(w)\n if (s === null) {\n throw new MoneyUnsupportedError(`allocate: weight ${JSON.stringify(w)} is not a finite decimal`)\n }\n if (s > weightScale) weightScale = s\n }\n const scaledWeights = weights.map(w => {\n const r = parseToScaledInt(w, weightScale)\n if (!r.ok || r.value < 0n) {\n throw new MoneyUnsupportedError(`allocate: weight ${JSON.stringify(w)} must be a non-negative decimal`)\n }\n return r.value\n })\n const sumW = scaledWeights.reduce((acc, w) => acc + w, 0n)\n if (sumW === 0n) {\n throw new MoneyUnsupportedError('allocate: weights must not all be zero')\n }\n\n // Largest-remainder over the MAGNITUDE; a negative amount negates the\n // parts at the end, so Σparts === amount holds for both signs.\n const negative = a < 0n\n const mag = negative ? -a : a\n\n const base: bigint[] = []\n const remainders: Array<{ index: number; rem: bigint }> = []\n let distributed = 0n\n for (let i = 0; i < scaledWeights.length; i++) {\n const product = mag * scaledWeights[i]!\n const share = product / sumW\n base.push(share)\n distributed += share\n remainders.push({ index: i, rem: product % sumW })\n }\n\n // Leftover minor units: strictly fewer than the bucket count.\n let leftover = mag - distributed\n remainders.sort((x, y) => (y.rem > x.rem ? 1 : y.rem < x.rem ? -1 : x.index - y.index))\n for (const { index } of remainders) {\n if (leftover === 0n) break\n base[index] = base[index]! + 1n\n leftover -= 1n\n }\n\n return base.map(p => formatScaledInt(negative && p !== 0n ? -p : p, scale))\n}\n","/**\n * Branded money output type.\n *\n * The decoded read shape of a money field is an exact decimal STRING\n * (`'10000.00'`) — but a hand-written schema types it `string | number`,\n * which is honest about the input side and unhelpful on the output\n * side: every read site hand-coerces, and a missed coercion passes\n * typecheck silently (then surfaces as a runtime template warning, or\n * worse, as `'10000.00' * 100` arithmetic).\n *\n * {@link MoneyString} is the output-side brand: a `string` that has\n * been through noy-db's decode (or {@link asMoney}'s guard). Branding\n * is type-level only — zero runtime cost on the read path.\n *\n * ## Wiring the brand through a validator (the input/output asymmetry)\n *\n * Keep the permissive `string | number` on the INPUT side so write\n * sites are untouched, and brand the OUTPUT side. With Zod:\n *\n * ```ts\n * import type { MoneyString } from '@noy-db/hub'\n *\n * const moneyValue = z.union([z.number(), z.string()]) as unknown as\n * z.ZodType<MoneyString, z.ZodTypeDef, string | number>\n *\n * const Invoice = z.object({ id: z.string(), total: moneyValue })\n * type InvoiceOut = z.output<typeof Invoice> // total: MoneyString\n * type InvoiceIn = z.input<typeof Invoice> // total: string | number\n * ```\n *\n * The cast is sound for fields declared in `moneyFields`: reads pass\n * through `decodeMoneyFields`, which always produces the canonical\n * decimal string.\n */\n\nimport { decimalScaleOf } from './fixed-point.js'\nimport { MoneyUnsupportedError } from './descriptor.js'\n\ndeclare const MONEY_BRAND: unique symbol\n\n/**\n * An exact decimal string produced by noy-db's money decode\n * (`'10000.00'`). The brand exists only at the type level — at runtime\n * a `MoneyString` is a plain string.\n */\nexport type MoneyString = string & { readonly [MONEY_BRAND]: true }\n\n/**\n * Runtime-guarded cast to {@link MoneyString}. Accepts a decimal string\n * or a number, returns the canonical decimal string branded — throws\n * `MoneyUnsupportedError` on anything non-finite / non-decimal. Use at\n * trust boundaries (deserialized JSON, route params); values read\n * through `get()`/`list()` are already canonical.\n */\nexport function asMoney(value: string | number): MoneyString {\n if (!isMoneyLike(value)) {\n throw new MoneyUnsupportedError(`asMoney: ${JSON.stringify(value)} is not a finite decimal`)\n }\n return String(value).trim() as MoneyString\n}\n\n/** Type guard: is `value` a decimal string acceptable as money? */\nexport function isMoneyString(value: unknown): value is MoneyString {\n return typeof value === 'string' && isMoneyLike(value)\n}\n\n/**\n * The ONE sanctioned escape hatch to a JS `number` — explicit because\n * the conversion is lossy past 2^53. For display math and chart axes;\n * never feed the result back into stored amounts (use `mulRate` /\n * `allocate` for exact arithmetic).\n */\nexport function moneyNumber(value: MoneyString | string | number): number {\n if (!isMoneyLike(value)) {\n throw new MoneyUnsupportedError(`moneyNumber: ${JSON.stringify(value)} is not a finite decimal`)\n }\n return Number(value)\n}\n\nfunction isMoneyLike(value: unknown): boolean {\n if (typeof value === 'number') return Number.isFinite(value)\n if (typeof value !== 'string') return false\n return decimalScaleOf(value) !== null\n}\n"],"mappings":"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","names":["keep","magnitude","obj","clone","currency","isMoneyValueObject","e","value"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-commit/history/ledger/patch.ts","../src/with-commit/history/ledger/store.ts"],"sourcesContent":["/**\n * RFC 6902 JSON Patch — compute + apply.\n *\n * This module is the \"delta history\" primitive: instead of\n * snapshotting the full record on every put (the behavior),\n * `Collection.put` computes a JSON Patch from the previous version to\n * the new version and stores only the patch in the ledger. To\n * reconstruct version N, we walk from the genesis snapshot forward\n * applying patches. Storage scales with **edit size**, not record\n * size — a 10 KB record edited 1000 times costs ~10 KB of deltas\n * instead of ~10 MB of snapshots.\n *\n * ## Why hand-roll instead of using a library?\n *\n * RFC 6902 has good libraries (`fast-json-patch`, `rfc6902`) but every\n * single one of them adds a runtime dependency to `@noy-db/core`. The\n * \"zero runtime dependencies\" promise is one of the core's load-bearing\n * features, and the patch surface we actually need is small enough\n * (~150 LoC) that vendoring is the right call.\n *\n * What we implement:\n * - `add` — insert a value at a path\n * - `remove` — delete the value at a path\n * - `replace` — overwrite the value at a path\n *\n * What we deliberately skip (out of scope for the ledger use):\n * - `move` and `copy` — optimizations; the diff algorithm doesn't\n * emit them, so the apply path doesn't need them\n * - `test` — used for transactional patches; we already have\n * optimistic concurrency via `_v` at the envelope layer\n * - Sophisticated array diffing (LCS, edit distance) — we treat\n * arrays as atomic values and emit a single `replace` op when\n * they differ. The accounting domain has small arrays where this\n * is fine; if we ever need patch-level array diffing we can add\n * it without changing the storage format.\n *\n * ## Path encoding (RFC 6902 §3)\n *\n * Paths look like `/foo/bar/0`. Each path segment is either an object\n * key or a numeric array index. Two characters need escaping inside\n * keys: `~` becomes `~0` and `/` becomes `~1`. We implement both.\n *\n * Empty path (`\"\"`) refers to the root document. Only `replace` makes\n * sense at the root, and our diff function emits it as a top-level\n * `replace` when `prev` and `next` differ in shape (object vs array,\n * primitive vs object, etc.).\n */\n\n/** A single JSON Patch operation. Subset of RFC 6902 — see file docstring. */\nexport type JsonPatchOp =\n | { readonly op: 'add'; readonly path: string; readonly value: unknown }\n | { readonly op: 'remove'; readonly path: string }\n | { readonly op: 'replace'; readonly path: string; readonly value: unknown }\n\n/** A complete JSON Patch document — an array of operations. */\nexport type JsonPatch = readonly JsonPatchOp[]\n\n// ─── Compute (diff) ──────────────────────────────────────────────────\n\n/**\n * Compute a JSON Patch that, when applied to `prev`, produces `next`.\n *\n * The algorithm is a straightforward recursive object walk:\n *\n * 1. If both inputs are plain objects (and not arrays/null):\n * - For each key in `prev`, recurse if `next` has it, else emit `remove`\n * - For each key in `next` not in `prev`, emit `add`\n * 2. If both inputs are arrays AND structurally equal, no-op.\n * Otherwise emit a single `replace` for the whole array.\n * 3. If both inputs are deeply equal primitives, no-op.\n * 4. Otherwise emit a `replace` at the current path.\n *\n * We do not minimize patches across move-like rearrangements — every\n * generated patch is straightforward enough to apply by hand if you\n * had to debug it.\n */\nexport function computePatch(prev: unknown, next: unknown): JsonPatch {\n const ops: JsonPatchOp[] = []\n diff(prev, next, '', ops)\n return ops\n}\n\nfunction diff(\n prev: unknown,\n next: unknown,\n path: string,\n out: JsonPatchOp[],\n): void {\n // Both null / both undefined → no-op (we don't differentiate them\n // in JSON terms; canonicalJson would reject undefined anyway).\n if (prev === next) return\n\n // One side null, the other not → straight replace.\n if (prev === null || next === null) {\n out.push({ op: 'replace', path, value: next })\n return\n }\n\n const prevIsArray = Array.isArray(prev)\n const nextIsArray = Array.isArray(next)\n const prevIsObject = typeof prev === 'object' && !prevIsArray\n const nextIsObject = typeof next === 'object' && !nextIsArray\n\n // Type changed (e.g., object → primitive, array → object). Replace.\n if (prevIsArray !== nextIsArray || prevIsObject !== nextIsObject) {\n out.push({ op: 'replace', path, value: next })\n return\n }\n\n // Both arrays. We don't do clever LCS-based diffing — emit a single\n // replace for the whole array if they differ. See file docstring for\n // the rationale.\n if (prevIsArray && nextIsArray) {\n if (!arrayDeepEqual(prev as unknown[], next as unknown[])) {\n out.push({ op: 'replace', path, value: next })\n }\n return\n }\n\n // Both plain objects. Recurse key by key.\n if (prevIsObject && nextIsObject) {\n const prevObj = prev as Record<string, unknown>\n const nextObj = next as Record<string, unknown>\n const prevKeys = Object.keys(prevObj)\n const nextKeys = Object.keys(nextObj)\n\n // Handle removes and overlapping recursions in one pass over prev.\n for (const key of prevKeys) {\n const childPath = path + '/' + escapePathSegment(key)\n if (!(key in nextObj)) {\n out.push({ op: 'remove', path: childPath })\n } else {\n diff(prevObj[key], nextObj[key], childPath, out)\n }\n }\n // Handle adds.\n for (const key of nextKeys) {\n if (!(key in prevObj)) {\n out.push({\n op: 'add',\n path: path + '/' + escapePathSegment(key),\n value: nextObj[key],\n })\n }\n }\n return\n }\n\n // Two primitives that aren't strictly equal — replace.\n out.push({ op: 'replace', path, value: next })\n}\n\nfunction arrayDeepEqual(a: unknown[], b: unknown[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null) return false\n if (typeof a !== typeof b) return false\n if (typeof a !== 'object') return false\n const aArray = Array.isArray(a)\n const bArray = Array.isArray(b)\n if (aArray !== bArray) return false\n if (aArray && bArray) return arrayDeepEqual(a, b as unknown[])\n const aObj = a as Record<string, unknown>\n const bObj = b as Record<string, unknown>\n const aKeys = Object.keys(aObj)\n const bKeys = Object.keys(bObj)\n if (aKeys.length !== bKeys.length) return false\n for (const key of aKeys) {\n if (!(key in bObj)) return false\n if (!deepEqual(aObj[key], bObj[key])) return false\n }\n return true\n}\n\n// ─── Apply ──────────────────────────────────────────────────────────\n\n/**\n * Apply a JSON Patch to a base document and return the result.\n *\n * The base document is **not mutated** — every op clones the parent\n * container before writing to it, so the caller's reference to `base`\n * stays untouched. This costs an extra allocation per op but makes\n * the apply pipeline reorderable and safe to interrupt.\n *\n * Throws on:\n * - Removing a path that doesn't exist\n * - Adding to a path whose parent doesn't exist\n * - A path component that doesn't match the document shape (e.g.,\n * trying to step into a primitive)\n *\n * Throwing is the right behavior for the ledger use case: a failed\n * apply means the chain is corrupted, which should be loud rather\n * than silently producing a wrong reconstruction.\n */\nexport function applyPatch<T = unknown>(base: T, patch: JsonPatch): T {\n let result: unknown = clone(base)\n for (const op of patch) {\n result = applyOp(result, op)\n }\n return result as T\n}\n\nfunction applyOp(doc: unknown, op: JsonPatchOp): unknown {\n // Empty path → operation targets the root. Only `replace` and `add`\n // make sense at the root, but we handle `remove` for completeness\n // (root removal returns null).\n if (op.path === '') {\n if (op.op === 'remove') return null\n return clone(op.value)\n }\n\n const segments = parsePath(op.path)\n return walkAndApply(doc, segments, op)\n}\n\nfunction walkAndApply(\n doc: unknown,\n segments: string[],\n op: JsonPatchOp,\n): unknown {\n if (segments.length === 0) {\n // Should never happen — empty path is handled in applyOp().\n throw new Error('walkAndApply: empty segments (internal error)')\n }\n\n const [head, ...rest] = segments\n if (head === undefined) throw new Error('walkAndApply: undefined segment')\n\n if (rest.length === 0) {\n return applyAtTerminal(doc, head, op)\n }\n\n // Recurse into the child container, then rebuild the parent with\n // the modified child.\n if (Array.isArray(doc)) {\n const idx = parseArrayIndex(head, doc.length)\n const child = doc[idx]\n const newChild = walkAndApply(child, rest, op)\n const next = doc.slice()\n next[idx] = newChild\n return next\n }\n if (doc !== null && typeof doc === 'object') {\n const obj = doc as Record<string, unknown>\n if (!(head in obj)) {\n throw new Error(`applyPatch: path segment \"${head}\" not found in object`)\n }\n const newChild = walkAndApply(obj[head], rest, op)\n return { ...obj, [head]: newChild }\n }\n throw new Error(\n `applyPatch: cannot step into ${typeof doc} at segment \"${head}\"`,\n )\n}\n\nfunction applyAtTerminal(\n doc: unknown,\n segment: string,\n op: JsonPatchOp,\n): unknown {\n if (Array.isArray(doc)) {\n const idx =\n segment === '-' ? doc.length : parseArrayIndex(segment, doc.length + 1)\n const next = doc.slice()\n if (op.op === 'remove') {\n next.splice(idx, 1)\n return next\n }\n if (op.op === 'add') {\n next.splice(idx, 0, clone(op.value))\n return next\n }\n if (op.op === 'replace') {\n if (idx >= doc.length) {\n throw new Error(\n `applyPatch: replace at out-of-bounds array index ${idx}`,\n )\n }\n next[idx] = clone(op.value)\n return next\n }\n }\n if (doc !== null && typeof doc === 'object') {\n const obj = doc as Record<string, unknown>\n if (op.op === 'remove') {\n if (!(segment in obj)) {\n throw new Error(\n `applyPatch: remove on missing key \"${segment}\"`,\n )\n }\n const next = { ...obj }\n delete next[segment]\n return next\n }\n if (op.op === 'add') {\n // RFC 6902: `add` on an existing key replaces it.\n return { ...obj, [segment]: clone(op.value) }\n }\n if (op.op === 'replace') {\n if (!(segment in obj)) {\n throw new Error(\n `applyPatch: replace on missing key \"${segment}\"`,\n )\n }\n return { ...obj, [segment]: clone(op.value) }\n }\n }\n throw new Error(\n `applyPatch: cannot apply ${op.op} at terminal segment \"${segment}\"`,\n )\n}\n\n// ─── Path encoding (RFC 6902 §3) ─────────────────────────────────────\n\n/**\n * Escape a single path segment per RFC 6902 §3:\n * `~` → `~0`\n * `/` → `~1`\n *\n * Order matters: `~` must be escaped first, otherwise the `~1` we\n * just emitted would be re-escaped to `~01`.\n */\nfunction escapePathSegment(segment: string): string {\n return segment.replace(/~/g, '~0').replace(/\\//g, '~1')\n}\n\nfunction unescapePathSegment(segment: string): string {\n return segment.replace(/~1/g, '/').replace(/~0/g, '~')\n}\n\nfunction parsePath(path: string): string[] {\n if (!path.startsWith('/')) {\n throw new Error(`applyPatch: path must start with '/', got \"${path}\"`)\n }\n return path\n .slice(1)\n .split('/')\n .map(unescapePathSegment)\n}\n\nfunction parseArrayIndex(segment: string, max: number): number {\n if (!/^\\d+$/.test(segment)) {\n throw new Error(\n `applyPatch: array index must be a non-negative integer, got \"${segment}\"`,\n )\n }\n const idx = Number.parseInt(segment, 10)\n if (idx < 0 || idx > max) {\n throw new Error(\n `applyPatch: array index ${idx} out of range [0, ${max}]`,\n )\n }\n return idx\n}\n\n// ─── Cheap structural clone ─────────────────────────────────────────\n\n/**\n * Plain-JSON clone via JSON.parse(JSON.stringify(value)).\n *\n * Faster than `structuredClone` for our use because (a) we know our\n * inputs are JSON-compatible (no Dates, Maps, or BigInts — anything\n * else gets rejected by canonicalJson upstream), and (b) `structuredClone`\n * has overhead for handling arbitrary structured data we don't need.\n *\n * For tiny ledger entries (< 1 KB), the JSON round-trip is in the\n * single-digit microsecond range.\n */\nfunction clone<T>(value: T): T {\n if (value === null || value === undefined) return value\n if (typeof value !== 'object') return value\n return JSON.parse(JSON.stringify(value)) as T\n}\n","/**\n * `LedgerStore` — read/write access to a compartment's hash-chained\n * audit log.\n *\n * The store is a thin wrapper around the adapter's `_ledger/` internal\n * collection. Every append:\n *\n * 1. Loads the current head (or treats an empty ledger as head = -1)\n * 2. Computes `prevHash` = sha256(canonicalJson(head))\n * 3. Builds the new entry with `index = head.index + 1`\n * 4. Encrypts the entry with the compartment's ledger DEK\n * 5. Writes the encrypted envelope to `_ledger/<paddedIndex>`\n *\n * `verify()` walks the chain from genesis forward and returns\n * `{ ok: true, head }` on success or `{ ok: false, divergedAt }` on the\n * first broken link.\n *\n * ## Thread / concurrency model\n *\n * For we assume a **single writer per vault**. Two\n * concurrent `append()` calls would race on the \"read head, write\n * head+1\" cycle and could produce a broken chain. The sync engine\n * is the primary concurrent-writer scenario, and it uses\n * optimistic-concurrency via `expectedVersion` on the adapter — but\n * the ledger path has no such guard today. Multi-writer hardening is a\n * follow-up.\n *\n * Single-writer usage IS safe, including across process restarts:\n * `head()` reads the adapter fresh each call, so a crash between the\n * adapter.put of a data record and the ledger append just means the\n * ledger is missing an entry for that record. `verify()` still\n * succeeds; a future `verifyIntegrity()` helper can cross-check the\n * ledger against the data collections to catch the gap.\n *\n * ## Why hide the ledger from `vault.collection()`?\n *\n * The `_ledger` name starts with `_`, matching the existing prefix\n * convention for internal collections (`_keyring`, `_sync`,\n * `_history`). The Vault's public `collection()` method already\n * returns entries for any name, but `loadAll()` filters out\n * underscore-prefixed collections so backups and exports don't leak\n * ledger metadata. We keep the ledger accessible ONLY via\n * `vault.ledger()` to enforce the hash-chain invariants — direct\n * puts via `collection('_ledger')` would bypass the `append()` logic.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../../kernel/types.js'\nimport { encrypt, openEnvelopeJson, type EnclaveKey } from '../../../kernel/enclave/index.js'\nimport type { ConflictError} from '../../../kernel/errors.js';\nimport { isConflictError, LedgerContentionError } from '../../../kernel/errors.js'\nimport {\n canonicalJson,\n hashEntry,\n paddedIndex,\n sha256Hex,\n type LedgerEntry,\n} from './entry.js'\nimport type { JsonPatch } from './patch.js'\nimport { applyPatch } from './patch.js'\nimport { LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION } from './constants.js'\nimport { envelopePayloadHash } from './hash.js'\n\n/**\n * Maximum optimistic-CAS retries on the ledger head. Each failed\n * attempt invalidates the head cache, re-reads, and retries with a\n * fresh next-index. After N failures we surface\n * `LedgerContentionError` so the caller can decide whether to retry,\n * queue, or alert.\n */\nconst MAX_APPEND_ATTEMPTS = 8\n\n// — re-export the constants + helper so any existing\n// `import { LEDGER_COLLECTION } from '...store.js'` paths keep\n// working. Internal core paths (vault.ts) import from the leaf\n// modules directly to avoid pulling this file's class into the\n// floor bundle.\nexport { LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, envelopePayloadHash }\n\n/**\n * Input shape for `LedgerStore.append()`. The caller supplies the\n * operation metadata; the store fills in `index` and `prevHash`.\n */\nexport interface AppendInput {\n op: LedgerEntry['op']\n collection: string\n id: string\n version: number\n actor: string\n payloadHash: string\n /**\n * Optional JSON Patch representing the delta from the previous\n * version to the new version. Present only for `put` operations\n * that had a previous version; omitted for genesis puts and for\n * deletes. When present, `LedgerStore.append` persists the patch\n * in `_ledger_deltas/<paddedIndex>` and records its sha256 hash\n * as the entry's `deltaHash` field.\n */\n delta?: JsonPatch\n /**\n * Present only for `op === 'amendment'` — structured audit\n * payload for multi-record repair operations performed via\n * `withTransactions(...)`. Carried through verbatim to the\n * resulting ledger entry.\n */\n amendment?: LedgerEntry['amendment']\n /**\n * Optional human-readable tag describing why this mutation happened.\n * Threaded from `collection.put(_, _, { reason })`.\n * Carried verbatim onto the resulting ledger entry's `reason` field;\n * omitted from canonical JSON when undefined.\n */\n reason?: string\n}\n\n/**\n * Result of `LedgerStore.verify()`. On success, `head` is the hash of\n * the last entry — the same value that should be published to any\n * external anchoring service (blockchain, OpenTimestamps, etc.). On\n * failure, `divergedAt` is the 0-based index of the first entry whose\n * recorded `prevHash` does not match the recomputed hash of its\n * predecessor. Entries at `divergedAt` and later are untrustworthy;\n * entries before that index are still valid.\n */\nexport type VerifyResult =\n | { readonly ok: true; readonly head: string; readonly length: number }\n | {\n readonly ok: false\n readonly divergedAt: number\n readonly expected: string\n readonly actual: string\n }\n\n/**\n * A LedgerStore is bound to a single vault. Callers obtain one\n * via `vault.ledger()` — there is no public constructor to keep\n * the hash-chain invariants in one place.\n *\n * The class holds no mutable state beyond its dependencies (adapter,\n * vault name, DEK resolver, actor id). Every method reads the\n * adapter fresh so multiple instances against the same vault\n * see each other's writes immediately (at the cost of re-parsing the\n * ledger on every head() / verify() call; acceptable at scale).\n */\nexport class LedgerStore {\n private readonly adapter: NoydbStore\n private readonly vault: string\n private readonly encrypted: boolean\n private readonly getDEK: (collectionName: string) => Promise<EnclaveKey>\n private readonly actor: string\n\n /**\n * In-memory cache of the chain head — the most recently appended\n * entry along with its precomputed hash. Without this, every\n * `append()` would re-load every prior entry to recompute the\n * prevHash, making N puts O(N²) — a 1K-record stress test goes from\n * < 100ms to a multi-second timeout.\n *\n * The cache is populated on first read (`append`, `head`, `verify`)\n * and updated in-place on every successful `append`. Single-writer\n * usage (the assumption) keeps it consistent. A second\n * LedgerStore instance writing to the same vault would not\n * see the first instance's appends in its cached state — that's the\n * concurrency caveat documented at the class level.\n *\n * Sentinel `undefined` means \"not yet loaded\"; an explicit `null`\n * value means \"loaded and confirmed empty\" — distinguishing these\n * matters because an empty ledger is a valid state (genesis prevHash\n * is the empty string), and we don't want to re-scan the adapter\n * just because the chain is freshly initialized.\n */\n private headCache: { entry: LedgerEntry; hash: string } | null | undefined = undefined\n\n constructor(opts: {\n adapter: NoydbStore\n vault: string\n encrypted: boolean\n getDEK: (collectionName: string) => Promise<EnclaveKey>\n actor: string\n }) {\n this.adapter = opts.adapter\n this.vault = opts.vault\n this.encrypted = opts.encrypted\n this.getDEK = opts.getDEK\n this.actor = opts.actor\n }\n\n /**\n * Lazily load (or return cached) the current chain head. The cache\n * sentinel is `undefined` until first access; after the first call,\n * the cache holds either a `{ entry, hash }` for non-empty ledgers\n * or `null` for empty ones.\n */\n private async getCachedHead(): Promise<{ entry: LedgerEntry; hash: string } | null> {\n if (this.headCache !== undefined) return this.headCache\n const entries = await this.loadAllEntries()\n const last = entries[entries.length - 1]\n if (!last) {\n this.headCache = null\n return null\n }\n this.headCache = { entry: last, hash: await hashEntry(last) }\n return this.headCache\n }\n\n /**\n * Append a new entry to the ledger. Returns the full entry that was\n * written (with its assigned index and computed prevHash) so the\n * caller can use the hash for downstream purposes (e.g., embedding\n * in a verifiable backup).\n *\n * This is the **only** way to add entries. Direct adapter writes to\n * `_ledger/` would bypass the chain math and would be caught by the\n * next `verify()` call as a divergence.\n *\n * ## Multi-writer correctness\n *\n * Append is implemented as an optimistic-CAS retry loop. On every\n * attempt:\n *\n * 1. Read fresh head (cache invalidated on retry).\n * 2. Compute `nextIndex = head.index + 1`, `prevHash = hash(head)`.\n * 3. Encrypt delta payload IN MEMORY (no adapter write yet) so we\n * can compute `deltaHash` before claiming the chain slot.\n * 4. Build + encrypt the entry envelope.\n * 5. `adapter.put(_ledger, paddedIndex, envelope, expectedVersion: 0)`\n * — the `expectedVersion: 0` asserts \"this slot must not exist.\"\n * Stores with `casAtomic: true` honor the CAS check; under\n * contention the second writer's put throws `ConflictError`.\n * 6. On `ConflictError`: invalidate the head cache, sleep with\n * bounded backoff + jitter, retry. After `MAX_APPEND_ATTEMPTS`\n * retries throw {@link LedgerContentionError}.\n * 7. On success: write the delta envelope (if any) at the same\n * index. Update the head cache.\n *\n * Entry-first ordering matters: writing the delta first under\n * contention would orphan delta records at indices the writer never\n * actually claimed. The deltaHash is computed off the encrypted\n * envelope's `_data` field, which doesn't require the envelope to\n * be persisted.\n *\n * Stores with `casAtomic: false` (file, s3, r2 by default) silently\n * accept the `expectedVersion: 0` argument and proceed without a\n * CAS check. Concurrent appends against those stores remain\n * best-effort — pair them with an advisory lock or with sync\n * single-writer discipline.\n */\n async append(input: AppendInput): Promise<LedgerEntry> {\n let lastConflict: ConflictError | undefined\n for (let attempt = 0; attempt < MAX_APPEND_ATTEMPTS; attempt++) {\n // Force a fresh head read on every retry. The first attempt may\n // hit the cache; subsequent attempts must re-scan the adapter\n // because the prior conflict means our cached state is stale.\n if (attempt > 0) {\n this.headCache = undefined\n }\n try {\n return await this.appendOnce(input)\n } catch (err) {\n if (isConflictError(err)) {\n lastConflict = err\n if (attempt < MAX_APPEND_ATTEMPTS - 1) {\n await sleepBackoff(attempt)\n }\n continue\n }\n throw err\n }\n }\n void lastConflict\n throw new LedgerContentionError(MAX_APPEND_ATTEMPTS)\n }\n\n /**\n * One attempt at the append cycle. Throws `ConflictError` when the\n * CAS check on the entry put fails — `append()` catches that and\n * retries. Any other error propagates to the caller.\n */\n private async appendOnce(input: AppendInput): Promise<LedgerEntry> {\n const cached = await this.getCachedHead()\n const lastEntry = cached?.entry\n const prevHash = cached?.hash ?? ''\n const nextIndex = lastEntry ? lastEntry.index + 1 : 0\n\n // Encrypt the delta in memory so we can compute deltaHash WITHOUT\n // claiming the deltas slot yet — entry-put is the chain claim.\n let deltaEnvelope: EncryptedEnvelope | undefined\n let deltaHash: string | undefined\n if (input.delta !== undefined) {\n deltaEnvelope = await this.encryptDelta(input.delta)\n deltaHash = await sha256Hex(deltaEnvelope._data)\n }\n\n // Build the entry. Conditionally include `deltaHash` so\n // canonicalJson (which rejects undefined) never sees it when\n // there's no delta.\n const entryBase = {\n index: nextIndex,\n prevHash,\n op: input.op,\n collection: input.collection,\n id: input.id,\n version: input.version,\n ts: new Date().toISOString(),\n actor: input.actor === '' ? this.actor : input.actor,\n payloadHash: input.payloadHash,\n } as const\n const entry: LedgerEntry = {\n ...entryBase,\n ...(deltaHash !== undefined ? { deltaHash } : {}),\n ...(input.amendment !== undefined ? { amendment: input.amendment } : {}),\n ...(input.reason !== undefined ? { reason: input.reason } : {}),\n }\n\n const envelope = await this.encryptEntry(entry)\n // expectedVersion: 0 ≡ \"the slot must not yet exist.\" Honored by\n // casAtomic stores; silently passed through by non-CAS stores.\n await this.adapter.put(\n this.vault,\n LEDGER_COLLECTION,\n paddedIndex(entry.index),\n envelope,\n 0,\n )\n\n // Chain slot claimed. Now write the delta record (if any).\n if (deltaEnvelope) {\n await this.adapter.put(\n this.vault,\n LEDGER_DELTAS_COLLECTION,\n paddedIndex(entry.index),\n deltaEnvelope,\n 0,\n )\n }\n\n // Update the head cache so the next append() doesn't re-scan the\n // adapter.\n this.headCache = { entry, hash: await hashEntry(entry) }\n return entry\n }\n\n /**\n * Load a delta payload by its entry index. Returns `null` if the\n * entry at that index doesn't reference a delta (genesis puts and\n * deletes leave the slot empty) or if the delta row is missing\n * (possible after a `pruneHistory` fold).\n *\n * The caller is responsible for deciding what to do with a missing\n * delta — `ledger.reconstruct()` uses it as a \"stop walking\n * backward\" signal and falls back to the on-disk current value.\n */\n async loadDelta(index: number): Promise<JsonPatch | null> {\n const envelope = await this.adapter.get(\n this.vault,\n LEDGER_DELTAS_COLLECTION,\n paddedIndex(index),\n )\n if (!envelope) return null\n if (!this.encrypted) {\n return JSON.parse(envelope._data) as JsonPatch\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const json = await openEnvelopeJson(envelope, dek)\n return JSON.parse(json) as JsonPatch\n }\n\n /**\n * Purge a record's tier-0-era plaintext deltas (#729). Deletes each\n * `_ledger_deltas/<paddedIndex>` row whose entry matches\n * `(collection, id)` and carries a delta, then returns the count\n * deleted.\n *\n * Chain-safe by construction: `verify()` recomputes the tamper-chain\n * from each `_ledger` entry's own canonical fields — including the\n * stored `deltaHash` field, which lives on the entry, not on the\n * `_ledger_deltas` row — and never re-reads `_ledger_deltas`.\n * Deleting a delta row therefore cannot break `verify()`.\n * `reconstruct()` already treats a missing delta (`loadDelta` →\n * `null`) as a pruned stop, so a purge just makes the pre-purge\n * plaintext unreachable rather than corrupting anything.\n *\n * No `_ledger` entry is touched and nothing is re-encrypted — this\n * is irreversible (the deleted plaintext cannot be recovered) but\n * leaves the entry metadata (that the record was mutated, at which\n * version/timestamp/actor) fully intact for audit purposes.\n */\n async purgeRecordDeltas(collection: string, id: string): Promise<number> {\n const entries = await this.loadAllEntries()\n let purged = 0\n for (const entry of entries) {\n if (entry.collection !== collection || entry.id !== id) continue\n if (entry.deltaHash === undefined) continue\n const key = paddedIndex(entry.index)\n // adapter.delete() is a void no-op on an already-missing key, so\n // check for the row's presence first — the count must reflect\n // rows actually removed (idempotent repeat purges return 0), not\n // the number of delta-bearing entries (which never changes).\n const existing = await this.adapter.get(this.vault, LEDGER_DELTAS_COLLECTION, key)\n if (!existing) continue\n await this.adapter.delete(this.vault, LEDGER_DELTAS_COLLECTION, key)\n purged++\n }\n return purged\n }\n\n /** Encrypt a JSON Patch into an envelope for storage. Mirrors encryptEntry. */\n private async encryptDelta(patch: JsonPatch): Promise<EncryptedEnvelope> {\n const json = JSON.stringify(patch)\n if (!this.encrypted) {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: json,\n _by: this.actor,\n }\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: iv,\n _data: data,\n _by: this.actor,\n }\n }\n\n /**\n * Read all entries in ascending-index order. Used internally by\n * `append()`, `head()`, `verify()`, and `entries()`. Decryption is\n * serial because the entries are tiny and the overhead of a Promise\n * pool would dominate at realistic chain lengths (< 100K entries).\n */\n async loadAllEntries(): Promise<LedgerEntry[]> {\n const keys = await this.adapter.list(this.vault, LEDGER_COLLECTION)\n // Sort lexicographically, which matches numeric order because\n // keys are zero-padded to 10 digits.\n keys.sort()\n const entries: LedgerEntry[] = []\n for (const key of keys) {\n const envelope = await this.adapter.get(\n this.vault,\n LEDGER_COLLECTION,\n key,\n )\n if (!envelope) continue\n entries.push(await this.decryptEntry(envelope))\n }\n return entries\n }\n\n /**\n * Return the current head of the ledger: the last entry, its hash,\n * and the total chain length. `null` on an empty ledger so callers\n * can distinguish \"no history yet\" from \"empty history\".\n */\n async head(): Promise<\n | { readonly entry: LedgerEntry; readonly hash: string; readonly length: number }\n | null\n > {\n const cached = await this.getCachedHead()\n if (!cached) return null\n // `length` is `entry.index + 1` because indices are zero-based and\n // contiguous. We don't need to re-scan the adapter to compute it.\n return {\n entry: cached.entry,\n hash: cached.hash,\n length: cached.entry.index + 1,\n }\n }\n\n /**\n * Return entries in the requested half-open range `[from, to)`.\n * Defaults: `from = 0`, `to = length`. The indices are clipped to\n * the valid range; no error is thrown for out-of-range queries.\n */\n async entries(opts: { from?: number; to?: number } = {}): Promise<LedgerEntry[]> {\n const all = await this.loadAllEntries()\n const from = Math.max(0, opts.from ?? 0)\n const to = Math.min(all.length, opts.to ?? all.length)\n return all.slice(from, to)\n }\n\n /**\n * Reconstruct a record's state at a given historical version by\n * walking the ledger's delta chain backward from the current state.\n *\n * ## Algorithm\n *\n * Ledger deltas are stored in **reverse** form — each entry's\n * patch describes how to undo that put, transforming the new\n * record back into the previous one. `reconstruct` exploits this\n * by:\n *\n * 1. Finding every ledger entry for `(collection, id)` in the\n * chain, sorted by index ascending.\n * 2. Starting from `current` (the present value of the record,\n * as held by the caller — typically fetched via\n * `Collection.get()`).\n * 3. Walking entries in **descending** index order and applying\n * each entry's reverse patch, stopping when we reach the\n * entry whose version equals `atVersion`.\n *\n * The result is the record as it existed immediately AFTER the\n * put at `atVersion`. To get the state at the genesis put\n * (version 1), the walk runs all the way back through every put\n * after the first.\n *\n * ## Caveats\n *\n * - **Delete entries** break the walk: once we see a delete, the\n * record didn't exist before that point, so there's nothing to\n * reconstruct. We return `null` in that case.\n * - **Missing deltas** (e.g., after `pruneHistory` folds old\n * entries into a base snapshot) also stop the walk. does\n * not ship pruneHistory, so today this only happens if an entry\n * was deleted out-of-band.\n * - The caller MUST pass the correct current value. Passing a\n * mutated object would corrupt the reconstruction — the patch\n * chain is only valid against the exact state that was in\n * effect when the most recent put happened.\n *\n * For, `reconstruct` is the only way to read a historical\n * version via deltas. The legacy `_history` collection still\n * holds full snapshots and `Collection.getVersion()` still reads\n * from there — the two paths coexist until pruneHistory lands in\n * a follow-up and delta becomes the default.\n */\n async reconstruct<T>(\n collection: string,\n id: string,\n current: T,\n atVersion: number,\n ): Promise<T | null> {\n const all = await this.loadAllEntries()\n // Filter to entries for this (collection, id), in ascending index.\n const matching = all.filter(\n (e) => e.collection === collection && e.id === id,\n )\n if (matching.length === 0) {\n // No ledger history at all; the current state IS version 1\n // (or there's nothing), so the only valid atVersion is the\n // current record's version. We can't verify that here, so\n // return current if atVersion is plausible, null otherwise.\n return null\n }\n\n // Walk entries in descending index order, applying each reverse\n // delta until we reach the target version.\n let state: T | null = current\n for (let i = matching.length - 1; i >= 0; i--) {\n const entry = matching[i]\n if (!entry) continue\n\n // Defensive: skip every non-put/non-delete op variant. The\n // outer filter on `e.collection === collection && e.id === id`\n // already excludes `amendment` entries (their collection/id are\n // empty strings), but a top-of-loop guard keeps the walker\n // robust if a future op variant slips through the filter.\n if (entry.op !== 'put' && entry.op !== 'delete') continue\n\n // Match check FIRST — before applying this entry's reverse\n // patch. `state` at this point is the record state immediately\n // after this entry's put (or before this entry's delete), so\n // if the caller asked for this exact version, we're done.\n if (entry.version === atVersion && entry.op !== 'delete') {\n return state\n }\n\n if (entry.op === 'delete') {\n // A delete erases the live state. If the caller asks for a\n // version older than the delete we should continue walking\n // (state becomes null and the next put resets it). But we\n // can't reconstruct that pre-delete state from the current\n // in-memory `state` — the delete has no reverse patch. So\n // anything past this point is unreachable; return null.\n return null\n }\n\n if (entry.deltaHash === undefined) {\n // Genesis put — the earliest state for this lifecycle. We\n // can't walk further back. If the caller asked for exactly\n // this version, return the current state (we already failed\n // the match check above because a fresh genesis after a\n // delete can have version === atVersion). Otherwise the\n // target is unreachable from here.\n if (entry.version === atVersion) return state\n return null\n }\n\n const patch = await this.loadDelta(entry.index)\n if (!patch) {\n // Delta row is missing (probably pruned). Stop walking.\n return null\n }\n\n if (state === null) {\n // We're trying to walk back across a delete range and there's\n // nothing to apply a reverse patch to. Bail.\n return null\n }\n\n state = applyPatch(state, patch)\n }\n\n // Ran off the end of the walk without matching. The target\n // version doesn't exist in this record's chain.\n return null\n }\n\n /**\n * Walk the chain from genesis forward and verify every link.\n *\n * Returns `{ ok: true, head, length }` if every entry's `prevHash`\n * matches the recomputed hash of its predecessor (and the genesis\n * entry's `prevHash` is the empty string).\n *\n * Returns `{ ok: false, divergedAt, expected, actual }` on the first\n * mismatch. `divergedAt` is the 0-based index of the BROKEN entry\n * — entries before that index still verify cleanly; entries at and\n * after `divergedAt` are untrustworthy.\n *\n * This method detects:\n * - Mutated entry content (fields changed)\n * - Reordered entries (if any adjacent pair swaps, the prevHash\n * of the second no longer matches)\n * - Inserted entries (the inserted entry's prevHash likely fails,\n * and the following entry's prevHash definitely fails)\n * - Deleted entries (the entry after the deletion sees a wrong\n * prevHash)\n *\n * It does NOT detect:\n * - Tampering with the DATA collections that bypassed the ledger\n * entirely (e.g., an attacker who modifies records without\n * appending matching ledger entries — this is why we also\n * plan a `verifyIntegrity()` helper in a follow-up)\n * - Truncation of the chain at the tail (dropping the last N\n * entries leaves a shorter but still consistent chain). External\n * anchoring of `head.hash` to a trusted service is the defense\n * against this.\n */\n async verify(): Promise<VerifyResult> {\n const entries = await this.loadAllEntries()\n let expectedPrevHash = ''\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i]\n if (!entry) continue\n if (entry.prevHash !== expectedPrevHash) {\n return {\n ok: false,\n divergedAt: i,\n expected: expectedPrevHash,\n actual: entry.prevHash,\n }\n }\n if (entry.index !== i) {\n // An entry whose stored index doesn't match its position in\n // the sorted list means someone rewrote the adapter keys.\n // Treat as divergence.\n return {\n ok: false,\n divergedAt: i,\n expected: `index=${i}`,\n actual: `index=${entry.index}`,\n }\n }\n expectedPrevHash = await hashEntry(entry)\n }\n return {\n ok: true,\n head: expectedPrevHash,\n length: entries.length,\n }\n }\n\n // ─── Encryption plumbing ─────────────────────────────────────────\n\n /**\n * Serialize + encrypt a ledger entry into an EncryptedEnvelope. The\n * envelope's `_v` field is set to `entry.index + 1` so the usual\n * optimistic-concurrency machinery has a reasonable version number\n * to compare against (the ledger is append-only, so concurrent\n * writes should always bump the index).\n */\n private async encryptEntry(entry: LedgerEntry): Promise<EncryptedEnvelope> {\n const json = canonicalJson(entry)\n if (!this.encrypted) {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: entry.index + 1,\n _ts: entry.ts,\n _iv: '',\n _data: json,\n _by: entry.actor,\n }\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: entry.index + 1,\n _ts: entry.ts,\n _iv: iv,\n _data: data,\n _by: entry.actor,\n }\n }\n\n /** Decrypt an envelope into a LedgerEntry. Throws on bad key / tamper. */\n private async decryptEntry(envelope: EncryptedEnvelope): Promise<LedgerEntry> {\n if (!this.encrypted) {\n return JSON.parse(envelope._data) as LedgerEntry\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const json = await openEnvelopeJson(envelope, dek)\n return JSON.parse(json) as LedgerEntry\n }\n}\n\n// `envelopePayloadHash` lives in `./hash.ts` so it can be\n// imported by core code without dragging this file's `LedgerStore`\n// class into the floor bundle. The re-export at the top of this\n// file keeps the `import { envelopePayloadHash } from '.../store.js'`\n// path working.\n\n/**\n * Exponential backoff with jitter for the append CAS retry loop.\n * Attempt 0 → ~5–10 ms, attempt 7 → ~640–1280 ms. Jitter avoids the\n * thundering-herd problem when multiple writers collide repeatedly.\n */\nfunction sleepBackoff(attempt: number): Promise<void> {\n const base = 5 * Math.pow(2, attempt)\n const jitter = Math.random() * base\n return new Promise((resolve) => setTimeout(resolve, base + jitter))\n}\n"],"mappings":"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","names":[]}
@@ -1,45 +0,0 @@
1
- import {
2
- NOYDB_FORMAT_VERSION
3
- } from "./chunk-W5W6KHN2.js";
4
-
5
- // src/kernel/enclave/record-keys/tombstone.ts
6
- function isTombstoneShape(envelope) {
7
- return envelope._data === "" && envelope._cek === void 0 && envelope._del !== true;
8
- }
9
- function isTombstone(envelope, encrypted) {
10
- if (!encrypted) return false;
11
- return isTombstoneShape(envelope);
12
- }
13
- function buildTombstone(version, actor) {
14
- return {
15
- _noydb: NOYDB_FORMAT_VERSION,
16
- _v: version,
17
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
18
- _iv: "",
19
- _data: "",
20
- ...actor ? { _by: actor } : {}
21
- };
22
- }
23
- function isDeleteMarker(envelope) {
24
- return envelope._del === true;
25
- }
26
- function buildDeleteMarker(version, actor) {
27
- return {
28
- _noydb: NOYDB_FORMAT_VERSION,
29
- _v: version,
30
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
31
- _iv: "",
32
- _data: "",
33
- _del: true,
34
- ...actor ? { _by: actor } : {}
35
- };
36
- }
37
-
38
- export {
39
- isTombstoneShape,
40
- isTombstone,
41
- buildTombstone,
42
- isDeleteMarker,
43
- buildDeleteMarker
44
- };
45
- //# sourceMappingURL=chunk-NWCGDAV6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/kernel/enclave/record-keys/tombstone.ts"],"sourcesContent":["/**\n * Tombstone shape + predicate for the per-record-key (CEK) layer.\n *\n * A *tombstone* is the residue a GDPR crypto-shred (`vault.forget()`)\n * leaves on disk: the live envelope rewritten to `{ _noydb, _v, _ts, _by,\n * _iv:'', _data:'' }`, dropping `_iv`/`_data`/`_cek`/`_det`. With the wrapped\n * per-record CEK gone, the body — and every history version under the same\n * CEK — is permanently undecryptable, while the record KEY survives so the\n * version counter and \"record existed and was erased\" persist for the audit\n * ledger.\n *\n * These two helpers are the pure, collection-independent core of that\n * contract: {@link buildTombstone} mints the shape, {@link isTombstone}\n * recognises it on the read path. The orchestration that *writes* a tombstone\n * (`_writeTombstone`) and drives `vault.forget()` lives with the collection /\n * vault; it calls into these.\n */\nimport { NOYDB_FORMAT_VERSION, type EncryptedEnvelope } from '../../types.js'\n\n/**\n * Shape-only tombstone recognition for layers that have no per-collection\n * context (the sync engine, #590): a tombstone carries an empty `_data` and\n * no wrapped CEK. No live envelope can match — record bodies never serialize\n * to the empty string (the `_sync` meta envelope carries non-empty `_data`,\n * and legacy migration envelopes carry non-empty `_iv`/`_data`).\n */\nexport function isTombstoneShape(envelope: EncryptedEnvelope): boolean {\n return envelope._data === '' && envelope._cek === undefined && envelope._del !== true\n}\n\n/**\n * Is this envelope a crypto-shred tombstone?\n *\n * A tombstone carries no body (`_data` empty) and no wrapped CEK (`_cek`\n * absent) — decrypting it would call `decrypt('', '', dek)` → an AES-GCM\n * throw, so every read choke point must short-circuit to `null` instead.\n *\n * `encrypted` is the collection's encryption flag: on an unencrypted\n * collection there are no envelopes to shred, so nothing is ever a tombstone.\n * (Legacy migration envelopes carry non-empty `_iv`/`_data`, so they are not\n * tombstones and stay readable.)\n */\nexport function isTombstone(envelope: EncryptedEnvelope, encrypted: boolean): boolean {\n if (!encrypted) return false\n return isTombstoneShape(envelope)\n}\n\n/**\n * Mint a tombstone envelope from a record's live version + actor.\n *\n * Keeps `_v` (the displaced version) so the counter is monotonic across the\n * shred, stamps a fresh `_ts`, and records `_by` when an actor is supplied.\n * Deliberately omits `_iv`/`_data`/`_cek`/`_det` — that omission *is* the\n * erasure.\n */\nexport function buildTombstone(version: number, actor: string): EncryptedEnvelope {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: version,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: '',\n ...(actor ? { _by: actor } : {}),\n }\n}\n\n/** #589: is this envelope an ordinary-delete marker (version-ordered, reads as absent)? */\nexport function isDeleteMarker(envelope: EncryptedEnvelope): boolean {\n return envelope._del === true\n}\n\n/**\n * Mint a delete marker from the deleted record's next version + actor (#589).\n * Minted at the NEXT version (`existing._v + 1`) — unlike `buildTombstone`, which\n * keeps the displaced `_v` — so it wins convergence over the pre-delete copy and a\n * re-create can still win over it at a higher version.\n */\nexport function buildDeleteMarker(version: number, actor: string): EncryptedEnvelope {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: version,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: '',\n _del: true,\n ...(actor ? { _by: actor } : {}),\n }\n}\n"],"mappings":";;;;;AA0BO,SAAS,iBAAiB,UAAsC;AACrE,SAAO,SAAS,UAAU,MAAM,SAAS,SAAS,UAAa,SAAS,SAAS;AACnF;AAcO,SAAS,YAAY,UAA6B,WAA6B;AACpF,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,iBAAiB,QAAQ;AAClC;AAUO,SAAS,eAAe,SAAiB,OAAkC;AAChF,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAK,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC5B,KAAK;AAAA,IACL,OAAO;AAAA,IACP,GAAI,QAAQ,EAAE,KAAK,MAAM,IAAI,CAAC;AAAA,EAChC;AACF;AAGO,SAAS,eAAe,UAAsC;AACnE,SAAO,SAAS,SAAS;AAC3B;AAQO,SAAS,kBAAkB,SAAiB,OAAkC;AACnF,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAK,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC5B,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,GAAI,QAAQ,EAAE,KAAK,MAAM,IAAI,CAAC;AAAA,EAChC;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-party/policy/storage.ts"],"sourcesContent":["/**\n * Persistence helpers for the vault-level policy document\n * (`_meta/policy`). Mirrors the bypass-AES pattern used by\n * `_meta/handle` — the policy document is plain JSON, the envelope's\n * `_iv` field is left empty.\n *\n * @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/session-tiers.md → Storage location\n *\n * @module\n */\nimport type { NoydbStore, EncryptedEnvelope, VaultPolicy } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\n\n/** Reserved collection name for vault-level metadata documents. */\nexport const META_COLLECTION = '_meta'\n/** Reserved id for the vault-level policy document. */\nexport const POLICY_RECORD_ID = 'policy'\n\n/**\n * Read the vault-level policy from `_meta/policy`. Returns `undefined`\n * when no policy has been persisted (fresh vault, or a vault written\n * before the policy module landed). The caller falls back to the\n * default preset.\n *\n * Tolerates corrupted documents the same way `_meta/handle` does: a\n * JSON parse failure surfaces as `undefined`, not a thrown error, so\n * a bad write never permanently locks a vault.\n */\nexport async function loadVaultPolicy(\n store: NoydbStore,\n vault: string,\n): Promise<VaultPolicy | undefined> {\n const envelope = await store.get(vault, META_COLLECTION, POLICY_RECORD_ID)\n if (!envelope) return undefined\n try {\n const parsed = JSON.parse(envelope._data) as unknown\n if (!isVaultPolicy(parsed)) return undefined\n return parsed\n } catch {\n return undefined\n }\n}\n\n/**\n * Persist the vault-level policy at `_meta/policy`. Idempotent — call\n * once at vault creation and again on `db.updatePolicy()` invocations.\n */\nexport async function saveVaultPolicy(\n store: NoydbStore,\n vault: string,\n policy: VaultPolicy,\n): Promise<void> {\n const envelope: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(policy),\n }\n await store.put(vault, META_COLLECTION, POLICY_RECORD_ID, envelope)\n}\n\nfunction isVaultPolicy(x: unknown): x is VaultPolicy {\n if (x === null || typeof x !== 'object') return false\n if (!('gates' in x)) return false\n const gates = (x as { gates: unknown }).gates\n return gates !== null && typeof gates === 'object'\n}\n"],"mappings":";;;;;AAcO,IAAM,kBAAkB;AAExB,IAAM,mBAAmB;AAYhC,eAAsB,gBACpB,OACA,OACkC;AAClC,QAAM,WAAW,MAAM,MAAM,IAAI,OAAO,iBAAiB,gBAAgB;AACzE,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,SAAS,KAAK;AACxC,QAAI,CAAC,cAAc,MAAM,EAAG,QAAO;AACnC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,gBACpB,OACA,OACA,QACe;AACf,QAAM,WAA8B;AAAA,IAClC,QAAQ;AAAA,IACR,IAAI;AAAA,IACJ,MAAK,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC5B,KAAK;AAAA,IACL,OAAO,KAAK,UAAU,MAAM;AAAA,EAC9B;AACA,QAAM,MAAM,IAAI,OAAO,iBAAiB,kBAAkB,QAAQ;AACpE;AAEA,SAAS,cAAc,GAA8B;AACnD,MAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO;AAChD,MAAI,EAAE,WAAW,GAAI,QAAO;AAC5B,QAAM,QAAS,EAAyB;AACxC,SAAO,UAAU,QAAQ,OAAO,UAAU;AAC5C;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/via/lookup/handle.ts"],"sourcesContent":["/**\n * `_dict_*` reserved-collection storage engine — `LookupHandle`.\n *\n * Verbatim move of `DictionaryHandle` out of `via/i18n/dictionary.ts`\n * (#650 Task 1 — via-lookup extraction, phase D). The class is renamed to\n * `LookupHandle` (the dict tier is one backing kind the eventual lookup\n * layer supports); `via/i18n/dictionary.ts` re-exports it as\n * `DictionaryHandle` for one release so existing importers compile\n * unchanged. `DictEntry` / `DictionaryOptions` / `DICT_COLLECTION_PREFIX` /\n * `dictCollectionName` move with it — they were exclusively consumed by\n * this class's constructor/methods.\n *\n * No behavior change from the pre-move class: same encryption door\n * (`reservedEnvelopes('_dict_')`, #629 Task 4), same ACL, same ledger\n * shape, same sync-cache/snapshot contract for dict joins.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport type { NoydbEventEmitter } from '../../kernel/events.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport type { ViaCryptoCtx } from '../../kernel/via/index.js'\nimport type { LedgerStore } from '../../with-commit/history/ledger/store.js'\nimport { envelopePayloadHash } from '../../with-commit/history/ledger/hash.js'\nimport {\n PermissionDeniedError,\n DictKeyMissingError,\n} from '../../kernel/errors.js'\n\n/**\n * Delete-marker predicate, duplicated (not imported) from\n * `kernel/enclave/record-keys/tombstone.ts`'s `isDeleteMarker` (#647 fix wave 1).\n * `via/lookup/**` may not statically import `kernel/enclave/` — not even the barrel\n * (Check 15, `via-enclave-isolation`) — and this predicate carries zero crypto/protected-body\n * surface (just the `_del` protocol-header field, not `_iv`/`_data`), so duplicating it here\n * mirrors the same call `port/with/i18n-strategy.ts` already makes for `isDictCollectionName`\n * (see its doc comment). Keep the two in sync if the marker shape ever changes.\n *\n * `buildDeleteMarker` (which DOES construct the protected `_iv`/`_data` body fields — Check 11,\n * `enclave-body-only`) can't be duplicated the same way; it's injected via the constructor\n * instead, built from the real `kernel/enclave` function at the Vault call site.\n */\nfunction isDeleteMarker(envelope: EncryptedEnvelope): boolean {\n return envelope._del === true\n}\n\n/** Reserved collection name prefix. Never collides with user collections. */\nexport const DICT_COLLECTION_PREFIX = '_dict_'\n\n/** Return the adapter collection name for a named dictionary. */\nexport function dictCollectionName(dictionaryName: string): string {\n return `${DICT_COLLECTION_PREFIX}${dictionaryName}`\n}\n\n// ─── Dictionary entry shape ────────────────────────────────────────────\n\n/**\n * One entry in a `_dict_*` collection. The record `id` (adapter-side\n * key) IS the stable dictionary key (e.g. `'paid'`). The `labels`\n * record maps locale codes to display strings.\n */\nexport interface DictEntry {\n /** Stable key — same as the record id in the adapter. */\n readonly key: string\n /** Locale → label map, e.g. `{ en: 'Paid', th: 'ชำระแล้ว' }`. */\n readonly labels: Record<string, string>\n}\n\n// ─── Per-dictionary options ────────────────────────────────────────────\n\n/**\n * Options for `vault.dictionary(name, options?)`.\n *\n * `writableBy` controls the minimum role for write operations (put,\n * putAll, delete, rename). Defaults to `'admin'` to match the standard\n * \"dictionary contents are owned by admins\" convention; set to\n * `'operator'` for user-editable dictionaries like custom tags.\n */\nexport interface DictionaryOptions {\n /** Minimum role allowed to write dictionary entries. Default: `'admin'`. */\n readonly writableBy?: 'owner' | 'admin' | 'operator'\n}\n\n// ─── LookupHandle ───────────────────────────────────────────────────────\n\n/**\n * Handle to a named dictionary within a vault.\n *\n * Obtained via `vault.dictionary(name)`. Provides strongly-typed\n * CRUD for dictionary entries, plus the `rename()` operation that is the\n * only sanctioned mass-mutation path for dictKey fields.\n *\n * All writes are encrypted under the compartment's DEK for the\n * `_dict_<name>` collection. Adapters never see plaintext.\n */\nexport class LookupHandle<Keys extends string = string> {\n private readonly collName: string\n\n /**\n * Synchronous write-through cache for dict-join support.\n * Populated on every `put()`, `delete()`, and `rename()`. The snapshot\n * is built from this cache by `snapshotEntries()` — the query executor\n * calls this synchronously inside `.toArray()`.\n *\n * `null` means \"not yet initialized\" — callers should use `list()`\n * to warm the cache before using dict joins on pre-existing data.\n */\n private readonly _syncCache = new Map<string, DictEntry>()\n\n /**\n * Return all cached entries as `{ key, labels, ...labels }` records —\n * usable synchronously by the join executor's `snapshot()` call.\n * Returns an empty array when the cache has never been populated.\n */\n snapshotEntries(): readonly Record<string, unknown>[] {\n return Array.from(this._syncCache.values()).map((e) => ({\n key: e.key,\n labels: e.labels,\n ...e.labels,\n }))\n }\n\n constructor(\n private readonly adapter: NoydbStore,\n private readonly compartmentName: string,\n private readonly dictionaryName: string,\n private readonly keyring: UnlockedKeyring,\n /**\n * The `reservedEnvelopes('_dict_')` capability — this handle's sanctioned\n * crypto door onto its `_dict_<name>` collection (#629 Task 4). Bound by\n * the Vault; never a keyring, raw DEK, or enclave import.\n */\n private readonly reservedEnvelopes: ReturnType<ViaCryptoCtx['reservedEnvelopes']>,\n private readonly encrypted: boolean,\n private readonly ledger: LedgerStore | undefined,\n private readonly options: DictionaryOptions,\n /**\n * Callback provided by the Vault to find and rewrite records\n * in any registered collection that has a dictKeyField pointing at\n * this dictionary, used by `rename()`.\n */\n private readonly findAndUpdateReferences:\n | ((\n dictionaryName: string,\n oldKey: string,\n newKey: string,\n ) => Promise<void>)\n | undefined,\n private readonly emitter: NoydbEventEmitter,\n /**\n * #647 fix wave 1 — mints a version-ordered delete-marker envelope (the reserved-tier\n * mirror of #589's ordinary-collection delete marker). Injected rather than imported:\n * `buildDeleteMarker` constructs the envelope's protected-body fields (`_iv`/`_data`), and\n * `via/lookup/**` may not reach `kernel/enclave/` directly (Check 11\n * `enclave-body-only` / Check 15 `via-enclave-isolation`) — the Vault binds the real\n * `kernel/enclave` function at construction time, same pattern as `reservedEnvelopes` above.\n */\n private readonly buildDeleteMarker: (version: number, actor: string) => EncryptedEnvelope,\n /** #650 Task 4 (#647) — dirty-log participation hook (origin `local-write`), threaded from `BuildLookupHandleOptions`. */\n private readonly onDirty?: (collection: string, id: string, action: 'put' | 'delete', version: number) => Promise<void>,\n /** #650 Task 4 — thin `graphDispatch.collect`-equivalent; opens/collects/flushes a one-shot wave for this touch. */\n private readonly onRecordMutated?: (collection: string, id: string, action: 'put' | 'delete', version: number) => Promise<void>,\n /** #650 Task 5 (#648) — the real reference check `delete()`'s strict branch calls: restrict\n * throws `DictKeyInUseError`, cascade/nullify apply their propagation. Vault-bound; a no-op\n * when the dimension has no declared lookup-referencing edges. */\n private readonly checkReferencesOnDelete?: (key: string) => Promise<unknown>,\n ) {\n this.collName = dictCollectionName(dictionaryName)\n }\n\n // ─── Access checks ────────────────────────────────────────────────\n\n private requireWriteAccess(): void {\n const minRole = this.options.writableBy ?? 'admin'\n const roleRank: Record<string, number> = {\n client: 1,\n viewer: 2,\n operator: 3,\n admin: 4,\n owner: 5,\n }\n const callerRank = roleRank[this.keyring.role] ?? 0\n const requiredRank = roleRank[minRole] ?? 4\n if (callerRank < requiredRank) {\n throw new PermissionDeniedError(\n `Dictionary \"${this.dictionaryName}\" writes require \"${minRole}\" role or above. ` +\n `Current role: \"${this.keyring.role}\".`,\n )\n }\n }\n\n // ─── Internal helpers ─────────────────────────────────────────────\n\n private async encryptEntry(entry: DictEntry, version: number): Promise<EncryptedEnvelope> {\n if (!this.encrypted) {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: version,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(entry),\n _by: this.keyring.userId,\n }\n }\n const envelope = await this.reservedEnvelopes.encrypt(this.collName, JSON.stringify(entry), version)\n return { ...envelope, _by: this.keyring.userId }\n }\n\n private async decryptEntry(envelope: EncryptedEnvelope): Promise<DictEntry> {\n if (!this.encrypted) {\n return JSON.parse(envelope._data) as DictEntry\n }\n const json = await this.reservedEnvelopes.decrypt(this.collName, envelope)\n return JSON.parse(json) as DictEntry\n }\n\n /** @internal #650 Task 4 — re-read `key`'s CURRENT adapter truth into `_syncCache` (delete the\n * cache entry when the key is now gone). Called by `Vault._invalidateSyncApplied` after a\n * reserved-lookup row applies via sync, so an already-warmed snapshot/membership/altIndex read\n * never sees a stale verdict for a pulled vocabulary edit — the same invalidation a local\n * put()/delete()/rename() already triggers via `_syncCache.set`/`delete` above. */\n async _refreshSyncCache(key: string): Promise<void> {\n const envelope = await this.adapter.get(this.compartmentName, this.collName, key)\n if (!envelope || isDeleteMarker(envelope)) { this._syncCache.delete(key); return }\n this._syncCache.set(key, await this.decryptEntry(envelope))\n }\n\n // ─── Public API ───────────────────────────────────────────────────\n\n /**\n * Add or overwrite a single dictionary entry.\n *\n * @param key The stable key to store (e.g. `'paid'`).\n * @param labels Locale → label map (e.g. `{ en: 'Paid', th: 'ชำระแล้ว' }`).\n */\n async put(key: Keys, labels: Record<string, string>): Promise<void> {\n this.requireWriteAccess()\n\n const entry: DictEntry = { key, labels }\n const existing = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n const version = existing ? existing._v + 1 : 1\n const envelope = await this.encryptEntry(entry, version)\n\n await this.adapter.put(\n this.compartmentName,\n this.collName,\n key,\n envelope,\n existing ? existing._v : undefined,\n )\n\n // Maintain synchronous cache for dict-join snapshot\n this._syncCache.set(key, entry)\n\n // #650 Task 4 (#647) — choke-point participation: dirty-log tracking BEFORE the emit (mirrors\n // Collection._onRecordMutated's local-write order), then the wave-open hook AFTER.\n await this.onDirty?.(this.collName, key, 'put', version)\n\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: key,\n action: 'put',\n })\n\n await this.onRecordMutated?.(this.collName, key, 'put', version)\n\n if (this.ledger) {\n await this.ledger.append({\n op: 'put',\n collection: this.collName,\n id: key,\n version,\n actor: this.keyring.userId,\n // — must be the real envelope hash so\n // vault.verifyBackupIntegrity()'s data-cross-check matches.\n payloadHash: await envelopePayloadHash(envelope),\n })\n }\n }\n\n /**\n * Batch-add or overwrite multiple dictionary entries in one call.\n *\n * @param entries `{ key: { locale: label } }` map.\n */\n async putAll(entries: Record<Keys, Record<string, string>>): Promise<void> {\n this.requireWriteAccess()\n for (const [key, labels] of Object.entries(entries) as [Keys, Record<string, string>][]) {\n await this.put(key, labels)\n }\n }\n\n /**\n * Load the label map for a single key.\n *\n * @returns The label map, or `null` if the key doesn't exist.\n */\n async get(key: Keys): Promise<Record<string, string> | null> {\n const envelope = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n if (!envelope || isDeleteMarker(envelope)) return null\n const entry = await this.decryptEntry(envelope)\n return entry.labels\n }\n\n /**\n * Delete a dictionary key.\n *\n * Default mode is `'strict'` — throws `DictKeyInUseError` if any\n * registered collection has a record referencing this key. Pass\n * `{ mode: 'warn' }` to skip the check (dev-mode cleanup only).\n *\n * Under sync, deletion writes a version-ordered delete-marker row rather than removing the\n * adapter key outright (#647 fix wave 1) — markers accumulate over time; GC is future work,\n * acceptable for vocabulary-scale data.\n */\n async delete(key: Keys, opts: { mode?: 'strict' | 'warn' } = {}): Promise<void> {\n this.requireWriteAccess()\n\n const existing = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n if (!existing || isDeleteMarker(existing)) {\n throw new DictKeyMissingError(this.dictionaryName, key)\n }\n\n // #650 Task 5 (#648) — the real reference check: restrict throws `DictKeyInUseError` naming\n // the referencing collection, cascade/nullify apply their propagation. `mode: 'warn'` skips\n // this entirely (today's dev-mode-cleanup escape hatch, unconditional delete). A dimension\n // with no declared lookup-referencing edges is a no-op (undeclared refs keep dangling).\n const mode = opts.mode ?? 'strict'\n if (mode === 'strict') {\n await this.checkReferencesOnDelete?.(key)\n }\n\n // #647 fix wave 1 — mirrors Collection._doDelete's identical `this.onDirty`-gated branch\n // (#589): under sync, a raw adapter.delete() is invisible to a peer's next pull (the key\n // simply stops appearing in remote.list()) — a deleted key never propagates and can be\n // silently resurrected by a later edit. Write a version-ordered DELETE-MARKER instead, so\n // the removal round-trips like any other row. No-op (plain adapter.delete) when sync isn't wired.\n const nextVersion = existing._v + 1\n if (this.onDirty) {\n await this.adapter.put(this.compartmentName, this.collName, key, this.buildDeleteMarker(nextVersion, this.keyring.userId))\n } else {\n await this.adapter.delete(this.compartmentName, this.collName, key)\n }\n\n // Maintain synchronous cache for dict-join snapshot\n this._syncCache.delete(key)\n\n // #650 Task 4 (#647) — same choke-point ordering as put() above.\n // #647 fix wave 1: the dirty entry's ACTION is 'put' (not 'delete') — mirrors\n // Collection._onRecordMutated's 'local-delete' case exactly: the marker rides push's\n // CAS-put path, not push's raw remote.delete() path (which would strip the marker off the\n // remote too and reopen this same hole). The version is the MARKER's version, so push's CAS\n // (expectedVersion = entry.version - 1) matches the pre-delete remote copy.\n await this.onDirty?.(this.collName, key, 'put', nextVersion)\n\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: key,\n action: 'delete',\n })\n\n await this.onRecordMutated?.(this.collName, key, 'delete', nextVersion)\n\n if (this.ledger) {\n await this.ledger.append({\n op: 'delete',\n collection: this.collName,\n id: key,\n version: existing._v,\n actor: this.keyring.userId,\n // — for delete the prior envelope is what was just\n // removed; we hash it so the chain captures intent. The\n // verifyBackupIntegrity data-cross-check skips delete\n // entries entirely (the live record is gone), but the\n // chain still benefits from a stable non-empty hash.\n payloadHash: await envelopePayloadHash(existing),\n })\n }\n }\n\n /**\n * Rename a dictionary key — the only sanctioned mass-mutation path.\n *\n * Atomically:\n * 1. Adds the new key with the same labels as the old key.\n * 2. Updates every registered record that stores the old key to\n * store the new key instead.\n * 3. Deletes the old key.\n * 4. Appends a single ledger entry recording the rename.\n *\n * Respects ACL: throws `PermissionDeniedError` before any mutation\n * if the caller can't write. The cascade is best-effort atomic\n * within this call — no two-phase commit across adapter calls.\n *\n * Cascade-on-delete is NOT supported. Use `rename()` when you need\n * to change a key that records reference.\n */\n async rename(oldKey: Keys, newKey: string): Promise<void> {\n this.requireWriteAccess()\n\n // 1. Load old entry\n const existing = await this.adapter.get(\n this.compartmentName,\n this.collName,\n oldKey,\n )\n if (!existing || isDeleteMarker(existing)) {\n throw new DictKeyMissingError(this.dictionaryName, oldKey)\n }\n const oldEntry = await this.decryptEntry(existing)\n\n // 2. Write new key\n const newEntry: DictEntry = { key: newKey, labels: oldEntry.labels }\n const newEnvelope = await this.encryptEntry(newEntry, 1)\n await this.adapter.put(\n this.compartmentName,\n this.collName,\n newKey,\n newEnvelope,\n )\n\n // #670 — publish newKey to the sync cache BEFORE step 3 rewrites referencing records: a\n // vocabulary:'closed' referencing field's enforceWrite reads checkLookupMembership ->\n // snapshotEntries() -> this._syncCache, so if newKey isn't a member yet, step 3's own write of\n // newKey into those records self-refuses with UnknownLookupKeyError. Transition semantics:\n // mid-rename BOTH oldKey and newKey are legitimately members (records are transitioning\n // old->new during the rewrite) — oldKey stays a member until its removal completes below.\n this._syncCache.set(newKey, newEntry)\n\n // 3. Update all referencing records in registered collections\n if (this.findAndUpdateReferences) {\n await this.findAndUpdateReferences(this.dictionaryName, oldKey, newKey)\n }\n\n // 4. Remove old key — #647 fix wave 1: same delete-marker convention as delete() above, so\n // the old key's removal propagates on sync instead of leaving a phantom stale row on a peer\n // that only observes the new key arriving.\n const oldKeyNextVersion = existing._v + 1\n if (this.onDirty) {\n await this.adapter.put(this.compartmentName, this.collName, oldKey, this.buildDeleteMarker(oldKeyNextVersion, this.keyring.userId))\n } else {\n await this.adapter.delete(this.compartmentName, this.collName, oldKey)\n }\n\n // Maintain synchronous cache for dict-join snapshot — oldKey's removal from the cache lands\n // here (after step 3), completing the old->new membership transition begun above.\n this._syncCache.delete(oldKey)\n\n // #650 Task 4 (#647) — same choke-point ordering as put()/delete() above, for BOTH touches.\n // #647 fix wave 1: oldKey's dirty action is 'put' (not 'delete') — see delete()'s comment\n // above for why the marker must ride push's CAS-put path, at the marker's version.\n await this.onDirty?.(this.collName, oldKey, 'put', oldKeyNextVersion)\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: oldKey,\n action: 'delete',\n })\n await this.onRecordMutated?.(this.collName, oldKey, 'delete', oldKeyNextVersion)\n\n await this.onDirty?.(this.collName, newKey, 'put', 1)\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: newKey,\n action: 'put',\n })\n await this.onRecordMutated?.(this.collName, newKey, 'put', 1)\n\n // 5. Ledger — record the rename as delete(oldKey) + put(newKey)\n // so verifyBackupIntegrity()'s data-cross-check matches reality\n // (the oldKey envelope is gone; the newKey envelope is what was\n // just written). Two entries instead of one — the chain still\n // captures the rename intent via the matching ts + actor.\n if (this.ledger) {\n await this.ledger.append({\n op: 'delete',\n collection: this.collName,\n id: oldKey,\n version: existing._v,\n actor: this.keyring.userId,\n payloadHash: await envelopePayloadHash(existing),\n })\n await this.ledger.append({\n op: 'put',\n collection: this.collName,\n id: newKey,\n version: 1,\n actor: this.keyring.userId,\n payloadHash: await envelopePayloadHash(newEnvelope),\n })\n }\n }\n\n /**\n * List all entries in this dictionary.\n *\n * @returns Array of `{ key, labels }` objects.\n */\n async list(): Promise<DictEntry[]> {\n const keys = await this.adapter.list(this.compartmentName, this.collName)\n const entries: DictEntry[] = []\n for (const key of keys) {\n const envelope = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n // #647 fix wave 1 — a delete-marker row is a removed key, not a live entry: exclude it\n // from the listing and drop any stale cache entry a prior warm may have left under it.\n if (!envelope || isDeleteMarker(envelope)) { this._syncCache.delete(key); continue }\n const entry = await this.decryptEntry(envelope)\n entries.push(entry)\n // Warm the synchronous cache\n this._syncCache.set(key, entry)\n }\n return entries\n }\n\n /**\n * Resolve a key to its label for the given locale.\n *\n * Used by the collection's locale-aware read path to populate\n * `<field>Label` virtual fields. Returns `undefined` when the\n * key doesn't exist or has no label for the requested locale\n * (after exhausting the fallback chain).\n */\n async resolveLabel(\n key: string,\n locale: string,\n fallback?: string | readonly string[],\n ): Promise<string | undefined> {\n const labels = await this.get(key as Keys)\n if (!labels) return undefined\n\n // Try primary locale\n if (labels[locale] !== undefined) return labels[locale]\n\n // Try fallback chain\n const chain = Array.isArray(fallback) ? (fallback as readonly string[]) : fallback ? [fallback as string] : []\n for (const fb of chain) {\n if (fb === 'any') {\n // Return any available label\n const any = Object.values(labels)[0]\n if (any !== undefined) return any\n } else if (labels[fb] !== undefined) {\n return labels[fb]\n }\n }\n\n return undefined\n }\n}\n\n// Compat alias — existing importers of `DictionaryHandle` (the pre-#650\n// name) keep compiling unchanged. `via/i18n/dictionary.ts` re-exports\n// this same binding for one release.\nexport { LookupHandle as DictionaryHandle }\n"],"mappings":"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","names":[]}