@noy-db/hub 0.2.0-pre.11 → 0.2.0-pre.12

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 (265) hide show
  1. package/README.md +126 -0
  2. package/dist/aggregate/index.cjs +289 -12
  3. package/dist/aggregate/index.cjs.map +1 -1
  4. package/dist/aggregate/index.d.cts +2 -2
  5. package/dist/aggregate/index.d.ts +2 -2
  6. package/dist/aggregate/index.js +7 -7
  7. package/dist/aggregate/index.js.map +1 -1
  8. package/dist/attestation/index.cjs.map +1 -1
  9. package/dist/attestation/index.d.cts +3 -3
  10. package/dist/attestation/index.d.ts +3 -3
  11. package/dist/attestation/index.js +6 -6
  12. package/dist/blobs/index.cjs +28 -0
  13. package/dist/blobs/index.cjs.map +1 -1
  14. package/dist/blobs/index.d.cts +4 -4
  15. package/dist/blobs/index.d.ts +4 -4
  16. package/dist/blobs/index.js +5 -5
  17. package/dist/bundle/index.cjs +1452 -16
  18. package/dist/bundle/index.cjs.map +1 -1
  19. package/dist/bundle/index.d.cts +5 -5
  20. package/dist/bundle/index.d.ts +5 -5
  21. package/dist/bundle/index.js +9 -9
  22. package/dist/{chunk-7CEGU63S.js → chunk-4BHFNKTP.js} +2 -2
  23. package/dist/{chunk-5OEJ6GOT.js → chunk-5ARRXIVR.js} +2 -2
  24. package/dist/{chunk-77DWLQRY.js → chunk-6AD5TBF2.js} +31 -3
  25. package/dist/chunk-6AD5TBF2.js.map +1 -0
  26. package/dist/{chunk-YM7LFCG7.js → chunk-6BYBVRZU.js} +3 -3
  27. package/dist/{chunk-73YLDCNF.js → chunk-7JJE3OMJ.js} +5 -5
  28. package/dist/{chunk-GKI4SDP7.js → chunk-7LVRIW4G.js} +4 -4
  29. package/dist/{chunk-O6EJ6WTI.js → chunk-AGRC7NQQ.js} +62 -2
  30. package/dist/chunk-AGRC7NQQ.js.map +1 -0
  31. package/dist/{chunk-IMYKDWB4.js → chunk-B7GGYNKQ.js} +2 -2
  32. package/dist/{chunk-BDV7INMP.js → chunk-BXOUVUES.js} +4 -4
  33. package/dist/{chunk-FO3UEG4S.js → chunk-C2CIIQRG.js} +2 -2
  34. package/dist/{chunk-ZROPXHJY.js → chunk-CHBXWJZQ.js} +2 -2
  35. package/dist/{chunk-RYIL3PI2.js → chunk-CILT6V3V.js} +2 -2
  36. package/dist/{chunk-YVZRTCGG.js → chunk-DLTU4M2I.js} +6 -6
  37. package/dist/{chunk-GAUEWM7D.js → chunk-EKNUBIIQ.js} +4 -4
  38. package/dist/{chunk-QCXNMCHN.js → chunk-GFPR7VJS.js} +4 -4
  39. package/dist/{chunk-V2PZC6AW.js → chunk-HBAJDI2N.js} +5 -5
  40. package/dist/{chunk-PVUUIWHY.js → chunk-HLGDYFWR.js} +10 -3
  41. package/dist/chunk-HLGDYFWR.js.map +1 -0
  42. package/dist/{chunk-RRNA5GKT.js → chunk-IEPT7HVP.js} +2 -2
  43. package/dist/{chunk-R233SLY3.js → chunk-IUBHXEPJ.js} +2 -2
  44. package/dist/{chunk-CH22FZHT.js → chunk-L6BYRCYB.js} +2 -2
  45. package/dist/{chunk-PC3ZZBTO.js → chunk-LOA2VCMS.js} +5 -5
  46. package/dist/{chunk-TGALXXLV.js → chunk-LSEW3ZZ2.js} +3 -3
  47. package/dist/{chunk-Y26YV5R3.js → chunk-LWSD4QPT.js} +3 -3
  48. package/dist/{chunk-SLV4LAKX.js → chunk-LYNNZEQD.js} +1 -1
  49. package/dist/chunk-LYNNZEQD.js.map +1 -0
  50. package/dist/{chunk-26NK23DZ.js → chunk-M45IRXDM.js} +3 -3
  51. package/dist/{chunk-CXJG63MA.js → chunk-NP6EZT44.js} +20 -6
  52. package/dist/chunk-NP6EZT44.js.map +1 -0
  53. package/dist/{chunk-ZBBW7YQN.js → chunk-O53RIZCC.js} +5 -5
  54. package/dist/chunk-OPDTLHFA.js +783 -0
  55. package/dist/chunk-OPDTLHFA.js.map +1 -0
  56. package/dist/{chunk-LSTBFLL2.js → chunk-P3Z5Y2TS.js} +2 -2
  57. package/dist/{chunk-QSOYKKMD.js → chunk-P4EDT5ZP.js} +2 -2
  58. package/dist/{chunk-PC6ZEDRL.js → chunk-RHQYVHFH.js} +2 -2
  59. package/dist/{chunk-3DGHRDCX.js → chunk-RRDWXNBQ.js} +3 -3
  60. package/dist/{chunk-6MFH4BMK.js → chunk-SJJQKNMP.js} +4 -4
  61. package/dist/{chunk-EBVBE7UK.js → chunk-SZ4N3IL5.js} +5 -5
  62. package/dist/{chunk-MPOLUAMI.js → chunk-TMHJEYW7.js} +497 -57
  63. package/dist/chunk-TMHJEYW7.js.map +1 -0
  64. package/dist/{chunk-JQ4NEJJ6.js → chunk-UA6G45ME.js} +3 -3
  65. package/dist/{chunk-6YLPHBKR.js → chunk-UOC7JMZO.js} +13 -4
  66. package/dist/chunk-UOC7JMZO.js.map +1 -0
  67. package/dist/{chunk-DAP2XL7Q.js → chunk-VOXMU6LB.js} +2 -2
  68. package/dist/chunk-WNRGOVLG.js +64 -0
  69. package/dist/chunk-WNRGOVLG.js.map +1 -0
  70. package/dist/{chunk-YW5DBAPG.js → chunk-WUG3E423.js} +4 -4
  71. package/dist/{chunk-LJXYPGRH.js → chunk-XHM2SARW.js} +3 -3
  72. package/dist/{chunk-RC6SU5NO.js → chunk-XSIFXX54.js} +2 -2
  73. package/dist/{chunk-CXFOITNS.js → chunk-ZC7MNVYN.js} +2 -2
  74. package/dist/{chunk-6T2UDBKG.js → chunk-ZCFS7U4J.js} +2 -2
  75. package/dist/consent/index.cjs.map +1 -1
  76. package/dist/consent/index.d.cts +4 -4
  77. package/dist/consent/index.d.ts +4 -4
  78. package/dist/consent/index.js +3 -3
  79. package/dist/{crypto-2CRLG4F4.js → crypto-AJB72OKN.js} +3 -3
  80. package/dist/{delegation-ZTRT2PRV.js → delegation-6FCWDRUS.js} +5 -5
  81. package/dist/derivations/index.cjs.map +1 -1
  82. package/dist/derivations/index.d.cts +5 -5
  83. package/dist/derivations/index.d.ts +5 -5
  84. package/dist/derivations/index.js +4 -4
  85. package/dist/{dev-unlock-BH6y3Hx0.d.ts → dev-unlock-D3mpVFRc.d.ts} +1 -1
  86. package/dist/{dev-unlock-H1Xwxc3U.d.cts → dev-unlock-ckqa_Nso.d.cts} +1 -1
  87. package/dist/executor-7KSCEIFA.js +8 -0
  88. package/dist/executor-D2QMNGRJ.js +8 -0
  89. package/dist/executor-O5AZK7UW.js +11 -0
  90. package/dist/{fanout-sidecar-F3ZRFU4H.js → fanout-sidecar-ZSKEQ6NI.js} +2 -2
  91. package/dist/guards/index.cjs +53 -1
  92. package/dist/guards/index.cjs.map +1 -1
  93. package/dist/guards/index.d.cts +12 -6
  94. package/dist/guards/index.d.ts +12 -6
  95. package/dist/guards/index.js +5 -3
  96. package/dist/{hash-D89JdDbj.d.ts → hash-CTZVkXLx.d.ts} +1 -1
  97. package/dist/{hash-_sDFvtmX.d.cts → hash-rDSSd_oW.d.cts} +1 -1
  98. package/dist/history/index.cjs.map +1 -1
  99. package/dist/history/index.d.cts +5 -5
  100. package/dist/history/index.d.ts +5 -5
  101. package/dist/history/index.js +5 -5
  102. package/dist/i18n/index.cjs.map +1 -1
  103. package/dist/i18n/index.d.cts +4 -4
  104. package/dist/i18n/index.d.ts +4 -4
  105. package/dist/i18n/index.js +6 -6
  106. package/dist/immutable-guard-C51vAHuh.d.cts +67 -0
  107. package/dist/immutable-guard-DyD0qg2k.d.ts +67 -0
  108. package/dist/index-CkFHr4OP.d.ts +1190 -0
  109. package/dist/index-Cmop06zJ.d.cts +1190 -0
  110. package/dist/index.cjs +1620 -58
  111. package/dist/index.cjs.map +1 -1
  112. package/dist/index.d.cts +46 -13
  113. package/dist/index.d.ts +46 -13
  114. package/dist/index.js +76 -44
  115. package/dist/index.js.map +1 -1
  116. package/dist/indexing/index.cjs.map +1 -1
  117. package/dist/indexing/index.js +2 -2
  118. package/dist/issue-YIYG4OW5.js +12 -0
  119. package/dist/{ledger-NYCGJX2D.js → ledger-5JMVF7PY.js} +5 -5
  120. package/dist/materialized-views/index.cjs.map +1 -1
  121. package/dist/materialized-views/index.d.cts +5 -6
  122. package/dist/materialized-views/index.d.ts +5 -6
  123. package/dist/materialized-views/index.js +6 -6
  124. package/dist/noydb-D5SLAJ6V.js +34 -0
  125. package/dist/overlay-views/index.cjs.map +1 -1
  126. package/dist/overlay-views/index.d.cts +5 -5
  127. package/dist/overlay-views/index.d.ts +5 -5
  128. package/dist/overlay-views/index.js +4 -4
  129. package/dist/periods/index.cjs.map +1 -1
  130. package/dist/periods/index.d.cts +4 -4
  131. package/dist/periods/index.d.ts +4 -4
  132. package/dist/periods/index.js +5 -5
  133. package/dist/{public-envelope-QOXZEHKH.js → public-envelope-PFLZI5MO.js} +4 -4
  134. package/dist/query/index.cjs +293 -10
  135. package/dist/query/index.cjs.map +1 -1
  136. package/dist/query/index.d.cts +2 -2
  137. package/dist/query/index.d.ts +2 -2
  138. package/dist/query/index.js +4 -4
  139. package/dist/registry-BVQ5ITMF.js +8 -0
  140. package/dist/registry-JLP3QOLD.js +8 -0
  141. package/dist/{registry-ST2VNFZC.js → registry-NCY445U5.js} +3 -3
  142. package/dist/{revoke-RT7QYB4G.js → revoke-7RLGQWZ7.js} +6 -6
  143. package/dist/session/index.cjs.map +1 -1
  144. package/dist/session/index.d.cts +5 -5
  145. package/dist/session/index.d.ts +5 -5
  146. package/dist/session/index.js +3 -3
  147. package/dist/shadow/index.cjs.map +1 -1
  148. package/dist/shadow/index.d.cts +4 -4
  149. package/dist/shadow/index.d.ts +4 -4
  150. package/dist/shadow/index.js +2 -2
  151. package/dist/{signer-QNU66JF5.js → signer-6JF44I4A.js} +5 -5
  152. package/dist/snapshots/index.cjs.map +1 -1
  153. package/dist/snapshots/index.d.cts +4 -4
  154. package/dist/snapshots/index.d.ts +4 -4
  155. package/dist/snapshots/index.js +4 -4
  156. package/dist/{stale-VKXSXJF4.js → stale-UBLP3RJ3.js} +2 -2
  157. package/dist/store/index.cjs.map +1 -1
  158. package/dist/store/index.d.cts +4 -4
  159. package/dist/store/index.d.ts +4 -4
  160. package/dist/store/index.js +2 -2
  161. package/dist/strategy-rtpKDfTC.d.cts +2029 -0
  162. package/dist/strategy-rtpKDfTC.d.ts +2029 -0
  163. package/dist/sync/index.cjs.map +1 -1
  164. package/dist/sync/index.d.cts +3 -3
  165. package/dist/sync/index.d.ts +3 -3
  166. package/dist/sync/index.js +4 -4
  167. package/dist/team/index.cjs.map +1 -1
  168. package/dist/team/index.d.cts +4 -4
  169. package/dist/team/index.d.ts +4 -4
  170. package/dist/team/index.js +8 -8
  171. package/dist/tx/index.cjs +8 -1
  172. package/dist/tx/index.cjs.map +1 -1
  173. package/dist/tx/index.d.cts +4 -4
  174. package/dist/tx/index.d.ts +4 -4
  175. package/dist/tx/index.js +3 -3
  176. package/dist/{types-DiSXn3a4.d.cts → types-BGwjsDef.d.cts} +511 -6
  177. package/dist/{types-CD8mc8zR.d.ts → types-DRdfwgTG.d.ts} +511 -6
  178. package/dist/{ulid-DQ1hcJvZ.d.cts → ulid-D4d0Xto3.d.cts} +1 -1
  179. package/dist/{ulid-8p83wbR4.d.ts → ulid-DOTPZ5_h.d.ts} +1 -1
  180. package/dist/util/index.cjs.map +1 -1
  181. package/dist/util/index.js +1 -1
  182. package/dist/vault-group-Z4KB75ZH.js +450 -0
  183. package/dist/vault-group-Z4KB75ZH.js.map +1 -0
  184. package/dist/{with-derivation-DWMTpgEH.d.ts → with-derivation-B082Y_WQ.d.ts} +1 -1
  185. package/dist/{with-derivation-CVT7-dUt.d.cts → with-derivation-CB1EdcFF.d.cts} +1 -1
  186. package/dist/{with-materialized-view-BTTU3BNK.d.cts → with-materialized-view-CzRg1Dpr.d.cts} +1 -1
  187. package/dist/{with-materialized-view-X0CoL8-L.d.ts → with-materialized-view-Dw4SwjKl.d.ts} +1 -1
  188. package/dist/{with-overlayed-view-DcacRRsS.d.cts → with-overlayed-view-C9YFKXzn.d.cts} +1 -1
  189. package/dist/{with-overlayed-view-DQjO_DSG.d.ts → with-overlayed-view-CaCXeW26.d.ts} +1 -1
  190. package/package.json +3 -3
  191. package/dist/chunk-2LPPNWF6.js +0 -340
  192. package/dist/chunk-2LPPNWF6.js.map +0 -1
  193. package/dist/chunk-6YLPHBKR.js.map +0 -1
  194. package/dist/chunk-77DWLQRY.js.map +0 -1
  195. package/dist/chunk-C3WE6UJY.js +0 -19
  196. package/dist/chunk-C3WE6UJY.js.map +0 -1
  197. package/dist/chunk-CXJG63MA.js.map +0 -1
  198. package/dist/chunk-MPOLUAMI.js.map +0 -1
  199. package/dist/chunk-O6EJ6WTI.js.map +0 -1
  200. package/dist/chunk-PVUUIWHY.js.map +0 -1
  201. package/dist/chunk-SLV4LAKX.js.map +0 -1
  202. package/dist/executor-S76VN45G.js +0 -8
  203. package/dist/executor-UCXLIGLW.js +0 -11
  204. package/dist/executor-ZCNZJMGR.js +0 -8
  205. package/dist/index-B8bjExET.d.cts +0 -2434
  206. package/dist/index-DfUbNad8.d.ts +0 -2434
  207. package/dist/issue-IVTVSKWW.js +0 -12
  208. package/dist/noydb-SH4RLE47.js +0 -34
  209. package/dist/registry-UFIK7CSR.js +0 -8
  210. package/dist/registry-ZGYYSM5I.js +0 -8
  211. package/dist/strategy-CT2LCKAX.d.cts +0 -613
  212. package/dist/strategy-CT2LCKAX.d.ts +0 -613
  213. package/dist/with-guard-BRvt53da.d.ts +0 -18
  214. package/dist/with-guard-Dx2zZnTA.d.cts +0 -18
  215. /package/dist/{chunk-7CEGU63S.js.map → chunk-4BHFNKTP.js.map} +0 -0
  216. /package/dist/{chunk-5OEJ6GOT.js.map → chunk-5ARRXIVR.js.map} +0 -0
  217. /package/dist/{chunk-YM7LFCG7.js.map → chunk-6BYBVRZU.js.map} +0 -0
  218. /package/dist/{chunk-73YLDCNF.js.map → chunk-7JJE3OMJ.js.map} +0 -0
  219. /package/dist/{chunk-GKI4SDP7.js.map → chunk-7LVRIW4G.js.map} +0 -0
  220. /package/dist/{chunk-IMYKDWB4.js.map → chunk-B7GGYNKQ.js.map} +0 -0
  221. /package/dist/{chunk-BDV7INMP.js.map → chunk-BXOUVUES.js.map} +0 -0
  222. /package/dist/{chunk-FO3UEG4S.js.map → chunk-C2CIIQRG.js.map} +0 -0
  223. /package/dist/{chunk-ZROPXHJY.js.map → chunk-CHBXWJZQ.js.map} +0 -0
  224. /package/dist/{chunk-RYIL3PI2.js.map → chunk-CILT6V3V.js.map} +0 -0
  225. /package/dist/{chunk-YVZRTCGG.js.map → chunk-DLTU4M2I.js.map} +0 -0
  226. /package/dist/{chunk-GAUEWM7D.js.map → chunk-EKNUBIIQ.js.map} +0 -0
  227. /package/dist/{chunk-QCXNMCHN.js.map → chunk-GFPR7VJS.js.map} +0 -0
  228. /package/dist/{chunk-V2PZC6AW.js.map → chunk-HBAJDI2N.js.map} +0 -0
  229. /package/dist/{chunk-RRNA5GKT.js.map → chunk-IEPT7HVP.js.map} +0 -0
  230. /package/dist/{chunk-R233SLY3.js.map → chunk-IUBHXEPJ.js.map} +0 -0
  231. /package/dist/{chunk-CH22FZHT.js.map → chunk-L6BYRCYB.js.map} +0 -0
  232. /package/dist/{chunk-PC3ZZBTO.js.map → chunk-LOA2VCMS.js.map} +0 -0
  233. /package/dist/{chunk-TGALXXLV.js.map → chunk-LSEW3ZZ2.js.map} +0 -0
  234. /package/dist/{chunk-Y26YV5R3.js.map → chunk-LWSD4QPT.js.map} +0 -0
  235. /package/dist/{chunk-26NK23DZ.js.map → chunk-M45IRXDM.js.map} +0 -0
  236. /package/dist/{chunk-ZBBW7YQN.js.map → chunk-O53RIZCC.js.map} +0 -0
  237. /package/dist/{chunk-LSTBFLL2.js.map → chunk-P3Z5Y2TS.js.map} +0 -0
  238. /package/dist/{chunk-QSOYKKMD.js.map → chunk-P4EDT5ZP.js.map} +0 -0
  239. /package/dist/{chunk-PC6ZEDRL.js.map → chunk-RHQYVHFH.js.map} +0 -0
  240. /package/dist/{chunk-3DGHRDCX.js.map → chunk-RRDWXNBQ.js.map} +0 -0
  241. /package/dist/{chunk-6MFH4BMK.js.map → chunk-SJJQKNMP.js.map} +0 -0
  242. /package/dist/{chunk-EBVBE7UK.js.map → chunk-SZ4N3IL5.js.map} +0 -0
  243. /package/dist/{chunk-JQ4NEJJ6.js.map → chunk-UA6G45ME.js.map} +0 -0
  244. /package/dist/{chunk-DAP2XL7Q.js.map → chunk-VOXMU6LB.js.map} +0 -0
  245. /package/dist/{chunk-YW5DBAPG.js.map → chunk-WUG3E423.js.map} +0 -0
  246. /package/dist/{chunk-LJXYPGRH.js.map → chunk-XHM2SARW.js.map} +0 -0
  247. /package/dist/{chunk-RC6SU5NO.js.map → chunk-XSIFXX54.js.map} +0 -0
  248. /package/dist/{chunk-CXFOITNS.js.map → chunk-ZC7MNVYN.js.map} +0 -0
  249. /package/dist/{chunk-6T2UDBKG.js.map → chunk-ZCFS7U4J.js.map} +0 -0
  250. /package/dist/{crypto-2CRLG4F4.js.map → crypto-AJB72OKN.js.map} +0 -0
  251. /package/dist/{delegation-ZTRT2PRV.js.map → delegation-6FCWDRUS.js.map} +0 -0
  252. /package/dist/{executor-S76VN45G.js.map → executor-7KSCEIFA.js.map} +0 -0
  253. /package/dist/{executor-UCXLIGLW.js.map → executor-D2QMNGRJ.js.map} +0 -0
  254. /package/dist/{executor-ZCNZJMGR.js.map → executor-O5AZK7UW.js.map} +0 -0
  255. /package/dist/{fanout-sidecar-F3ZRFU4H.js.map → fanout-sidecar-ZSKEQ6NI.js.map} +0 -0
  256. /package/dist/{issue-IVTVSKWW.js.map → issue-YIYG4OW5.js.map} +0 -0
  257. /package/dist/{ledger-NYCGJX2D.js.map → ledger-5JMVF7PY.js.map} +0 -0
  258. /package/dist/{noydb-SH4RLE47.js.map → noydb-D5SLAJ6V.js.map} +0 -0
  259. /package/dist/{public-envelope-QOXZEHKH.js.map → public-envelope-PFLZI5MO.js.map} +0 -0
  260. /package/dist/{registry-ST2VNFZC.js.map → registry-BVQ5ITMF.js.map} +0 -0
  261. /package/dist/{registry-UFIK7CSR.js.map → registry-JLP3QOLD.js.map} +0 -0
  262. /package/dist/{registry-ZGYYSM5I.js.map → registry-NCY445U5.js.map} +0 -0
  263. /package/dist/{revoke-RT7QYB4G.js.map → revoke-7RLGQWZ7.js.map} +0 -0
  264. /package/dist/{signer-QNU66JF5.js.map → signer-6JF44I4A.js.map} +0 -0
  265. /package/dist/{stale-VKXSXJF4.js.map → stale-UBLP3RJ3.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -46,7 +46,7 @@ var init_types = __esm({
46
46
  });
47
47
 
48
48
  // src/errors.ts
49
- var NoydbError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, ReadOnlyAtInstantError, ReadOnlyFrameError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError, PeriodClosedError, RecordLockedError, FieldFrozenError, InvariantError, AmendmentForbiddenError, DirectoryDisabledError, TierNotGrantedError, ElevationExpiredError, AlreadyElevatedError, TierDemoteDeniedError, DelegationTargetMissingError, ConflictError, LedgerContentionError, BundleVersionConflictError, NetworkError, NotFoundError, ValidationError, SchemaValidationError, SchemaUpdateError, NonAdditiveSchemaChangeError, SchemaLockedError, SchemaFenceError, MigrationRequiredError, QuiesceTimeoutError, GroupCardinalityError, IndexRequiredError, UniqueConstraintError, UnsupportedIndexOptionError, IndexWriteFailureError, BundleIntegrityError, BundleSealMismatchError, ReservedCollectionNameError, DictKeyMissingError, DictKeyInUseError, MissingTranslationError, LocaleNotSpecifiedError, ScriptViolationError, TranslatorNotConfiguredError, BackupLedgerError, BackupCorruptedError, AttestationError, SessionExpiredError, SessionNotFoundError, SessionPolicyError, JoinTooLargeError, CrossJoinTooLargeError, CrossJoinSourceUnknownError, DanglingReferenceError, FilenameSanitizationError, PathEscapeError, DerivationCycleError, DerivationDepthError, DerivationOutputUnknownError, DerivationOutputShapeError, DerivationCapExceededError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, MaterializedViewConfigError, OverlayBaseIsVirtualError, OverlayCollectionUnavailableError, OverlayNameCollisionError, OverlayIdMismatchError, SnapshotNotFoundError;
49
+ var NoydbError, DecryptionError, TamperedError, InvalidKeyError, KeyringCorruptError, NoAccessError, ReadOnlyError, ReadOnlyAtInstantError, ReadOnlyFrameError, PermissionDeniedError, ExportCapabilityError, KeyringExpiredError, ImportCapabilityError, StoreCapabilityError, PrivilegeEscalationError, PeriodClosedError, RecordLockedError, FieldFrozenError, InvariantError, AmendmentForbiddenError, DirectoryDisabledError, TierNotGrantedError, ElevationExpiredError, AlreadyElevatedError, TierDemoteDeniedError, DelegationTargetMissingError, ConflictError, LedgerContentionError, SequenceContentionError, SequenceOfflineError, BundleVersionConflictError, NetworkError, NotFoundError, ValidationError, SchemaValidationError, SchemaUpdateError, NonAdditiveSchemaChangeError, SchemaLockedError, SchemaFenceError, MigrationRequiredError, QuiesceTimeoutError, GroupCardinalityError, IndexRequiredError, UniqueConstraintError, UnsupportedIndexOptionError, IndexWriteFailureError, BundleIntegrityError, BundleSealMismatchError, ReservedCollectionNameError, DictKeyMissingError, DictKeyInUseError, MissingTranslationError, LocaleNotSpecifiedError, ScriptViolationError, TranslatorNotConfiguredError, BackupLedgerError, BackupCorruptedError, AttestationError, SessionExpiredError, SessionNotFoundError, SessionPolicyError, JoinTooLargeError, CrossJoinTooLargeError, CrossJoinSourceUnknownError, DanglingReferenceError, FilenameSanitizationError, PathEscapeError, DerivationCycleError, DerivationDepthError, DerivationOutputUnknownError, DerivationOutputShapeError, DerivationCapExceededError, MaterializedViewCycleError, MaterializedViewSourceUnknownError, MaterializedViewTooLargeError, MaterializedViewConfigError, OverlayBaseIsVirtualError, OverlayCollectionUnavailableError, OverlayNameCollisionError, OverlayIdMismatchError, SnapshotNotFoundError, UnknownShardError, ShardProvisioningError, VaultTemplateNotFoundError;
50
50
  var init_errors = __esm({
51
51
  "src/errors.ts"() {
52
52
  "use strict";
@@ -340,6 +340,28 @@ var init_errors = __esm({
340
340
  this.attempts = attempts;
341
341
  }
342
342
  };
343
+ SequenceContentionError = class extends NoydbError {
344
+ sequence;
345
+ attempts;
346
+ constructor(sequence, attempts) {
347
+ super(
348
+ "SEQUENCE_CONTENTION",
349
+ `vault.sequence("${sequence}").next(): failed to allocate after ${attempts} optimistic-CAS retries`
350
+ );
351
+ this.name = "SequenceContentionError";
352
+ this.sequence = sequence;
353
+ this.attempts = attempts;
354
+ }
355
+ };
356
+ SequenceOfflineError = class extends NoydbError {
357
+ constructor() {
358
+ super(
359
+ "SEQUENCE_OFFLINE",
360
+ "vault.sequence().next() requires an online CAS-capable store (capabilities.casAtomic). Gap-free numbering cannot be serialized offline."
361
+ );
362
+ this.name = "SequenceOfflineError";
363
+ }
364
+ };
343
365
  BundleVersionConflictError = class extends NoydbError {
344
366
  /** The bundle handle of the newer remote version that rejected the push. */
345
367
  remoteVersion;
@@ -451,14 +473,14 @@ var init_errors = __esm({
451
473
  recordId;
452
474
  fields;
453
475
  conflictingId;
454
- constructor(collection, recordId2, fields, conflictingId) {
476
+ constructor(collection, recordId3, fields, conflictingId) {
455
477
  super(
456
478
  "UNIQUE_CONSTRAINT",
457
- `Unique constraint on ${collection}.[${fields.join(", ")}] violated: record "${recordId2}" duplicates a value already held by "${conflictingId}".`
479
+ `Unique constraint on ${collection}.[${fields.join(", ")}] violated: record "${recordId3}" duplicates a value already held by "${conflictingId}".`
458
480
  );
459
481
  this.name = "UniqueConstraintError";
460
482
  this.collection = collection;
461
- this.recordId = recordId2;
483
+ this.recordId = recordId3;
462
484
  this.fields = fields;
463
485
  this.conflictingId = conflictingId;
464
486
  }
@@ -921,6 +943,39 @@ Resolutions:
921
943
  this.version = version;
922
944
  }
923
945
  };
946
+ UnknownShardError = class extends NoydbError {
947
+ partitionKey;
948
+ constructor(partitionKey, groupName) {
949
+ super(
950
+ "SHARD_UNKNOWN",
951
+ `No shard for partition key "${partitionKey}" in vault group "${groupName}" and autoCreate is disabled. Call group.createShard(${JSON.stringify(partitionKey)}) first, or enable sharding.autoCreate.`
952
+ );
953
+ this.name = "UnknownShardError";
954
+ this.partitionKey = partitionKey;
955
+ }
956
+ };
957
+ ShardProvisioningError = class extends NoydbError {
958
+ vaultId;
959
+ constructor(vaultId, partitionKey) {
960
+ super(
961
+ "SHARD_PROVISIONING",
962
+ `Registry has a row for partition "${partitionKey}" (vault "${vaultId}") but that vault is not provisioned in the store. Refusing to recreate it \u2014 the registry and store have diverged. Investigate before retrying.`
963
+ );
964
+ this.name = "ShardProvisioningError";
965
+ this.vaultId = vaultId;
966
+ }
967
+ };
968
+ VaultTemplateNotFoundError = class extends NoydbError {
969
+ templateName;
970
+ constructor(templateName) {
971
+ super(
972
+ "VAULT_TEMPLATE_NOT_FOUND",
973
+ `No vault template registered under "${templateName}". Register it with db.withVaultTemplate(${JSON.stringify(templateName)}, { version, configure }) before opening the vault group.`
974
+ );
975
+ this.name = "VaultTemplateNotFoundError";
976
+ this.templateName = templateName;
977
+ }
978
+ };
924
979
  }
925
980
  });
926
981
 
@@ -1685,7 +1740,7 @@ var init_hash = __esm({
1685
1740
  });
1686
1741
 
1687
1742
  // src/history/ledger/store.ts
1688
- function sleepBackoff(attempt) {
1743
+ function sleepBackoff2(attempt) {
1689
1744
  const base = 5 * Math.pow(2, attempt);
1690
1745
  const jitter = Math.random() * base;
1691
1746
  return new Promise((resolve) => setTimeout(resolve, base + jitter));
@@ -1807,7 +1862,7 @@ var init_store = __esm({
1807
1862
  if (err instanceof ConflictError) {
1808
1863
  lastConflict = err;
1809
1864
  if (attempt < MAX_APPEND_ATTEMPTS - 1) {
1810
- await sleepBackoff(attempt);
1865
+ await sleepBackoff2(attempt);
1811
1866
  }
1812
1867
  continue;
1813
1868
  }
@@ -2221,6 +2276,278 @@ var init_public_envelope = __esm({
2221
2276
  }
2222
2277
  });
2223
2278
 
2279
+ // src/money/fixed-point.ts
2280
+ function expandExponent(s) {
2281
+ const m = /^([+-]?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
2282
+ if (!m) return s;
2283
+ const sign = m[1] === "-" ? "-" : "";
2284
+ const intp = m[2];
2285
+ const frac = m[3] ?? "";
2286
+ const exp = Number(m[4]);
2287
+ const digits = intp + frac;
2288
+ const pointPos = intp.length + exp;
2289
+ let body;
2290
+ if (pointPos <= 0) {
2291
+ body = "0." + "0".repeat(-pointPos) + digits;
2292
+ } else if (pointPos >= digits.length) {
2293
+ body = digits + "0".repeat(pointPos - digits.length);
2294
+ } else {
2295
+ body = digits.slice(0, pointPos) + "." + digits.slice(pointPos);
2296
+ }
2297
+ return sign + body;
2298
+ }
2299
+ function toCanonicalDecimalString(input) {
2300
+ let s;
2301
+ if (typeof input === "number") {
2302
+ if (!Number.isFinite(input)) return null;
2303
+ s = String(input);
2304
+ } else {
2305
+ s = input.trim();
2306
+ }
2307
+ s = expandExponent(s);
2308
+ if (s.startsWith("+")) s = s.slice(1);
2309
+ if (!/^-?(\d+(\.\d*)?|\.\d+)$/.test(s)) return null;
2310
+ return s;
2311
+ }
2312
+ function shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, mode) {
2313
+ switch (mode) {
2314
+ case "up":
2315
+ return true;
2316
+ case "down":
2317
+ return false;
2318
+ case "ceil":
2319
+ return !negative;
2320
+ case "floor":
2321
+ return negative;
2322
+ case "half-up":
2323
+ return firstDiscarded >= 5;
2324
+ case "half-down":
2325
+ return firstDiscarded > 5 || firstDiscarded === 5 && hasMoreNonZeroAfterFirst;
2326
+ case "half-even":
2327
+ if (firstDiscarded > 5) return true;
2328
+ if (firstDiscarded < 5) return false;
2329
+ return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1;
2330
+ }
2331
+ }
2332
+ function parseToScaledInt(input, scale, rounding) {
2333
+ const canonical = toCanonicalDecimalString(input);
2334
+ if (canonical === null) return { ok: false, reason: "nonfinite" };
2335
+ const negative = canonical.startsWith("-");
2336
+ const unsigned = negative ? canonical.slice(1) : canonical;
2337
+ const dot = unsigned.indexOf(".");
2338
+ const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot);
2339
+ const fracPart = dot === -1 ? "" : unsigned.slice(dot + 1);
2340
+ const intDigits = intPart === "" ? "0" : intPart;
2341
+ if (fracPart.length <= scale) {
2342
+ const keep2 = fracPart.padEnd(scale, "0");
2343
+ const magnitude2 = BigInt(intDigits + keep2);
2344
+ return { ok: true, value: negative && magnitude2 !== 0n ? -magnitude2 : magnitude2 };
2345
+ }
2346
+ const keep = fracPart.slice(0, scale);
2347
+ const tail = fracPart.slice(scale);
2348
+ const magnitudeDigits = intDigits + keep;
2349
+ let magnitude = BigInt(magnitudeDigits);
2350
+ if (/^0+$/.test(tail)) {
2351
+ return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
2352
+ }
2353
+ if (rounding === void 0) return { ok: false, reason: "precision" };
2354
+ const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1]);
2355
+ const firstDiscarded = Number(tail[0]);
2356
+ const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1));
2357
+ if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {
2358
+ magnitude += 1n;
2359
+ }
2360
+ return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
2361
+ }
2362
+ function formatScaledInt(value, scale) {
2363
+ const negative = value < 0n;
2364
+ const abs = (negative ? -value : value).toString();
2365
+ if (scale === 0) return (negative ? "-" : "") + abs;
2366
+ const padded = abs.padStart(scale + 1, "0");
2367
+ const cut = padded.length - scale;
2368
+ const intPart = padded.slice(0, cut);
2369
+ const fracPart = padded.slice(cut);
2370
+ return (negative ? "-" : "") + intPart + "." + fracPart;
2371
+ }
2372
+ var init_fixed_point = __esm({
2373
+ "src/money/fixed-point.ts"() {
2374
+ "use strict";
2375
+ }
2376
+ });
2377
+
2378
+ // src/money/iso4217.ts
2379
+ function scaleForCurrency(code) {
2380
+ const v = MINOR_UNITS[code];
2381
+ return v === void 0 ? null : v;
2382
+ }
2383
+ var MINOR_UNITS;
2384
+ var init_iso4217 = __esm({
2385
+ "src/money/iso4217.ts"() {
2386
+ "use strict";
2387
+ MINOR_UNITS = {
2388
+ // 2-decimal majors
2389
+ EUR: 2,
2390
+ USD: 2,
2391
+ GBP: 2,
2392
+ CHF: 2,
2393
+ CAD: 2,
2394
+ AUD: 2,
2395
+ NZD: 2,
2396
+ SGD: 2,
2397
+ HKD: 2,
2398
+ CNY: 2,
2399
+ INR: 2,
2400
+ BRL: 2,
2401
+ MXN: 2,
2402
+ ZAR: 2,
2403
+ RUB: 2,
2404
+ TRY: 2,
2405
+ PLN: 2,
2406
+ SEK: 2,
2407
+ NOK: 2,
2408
+ DKK: 2,
2409
+ CZK: 2,
2410
+ HUF: 2,
2411
+ RON: 2,
2412
+ ILS: 2,
2413
+ THB: 2,
2414
+ PHP: 2,
2415
+ MYR: 2,
2416
+ IDR: 2,
2417
+ AED: 2,
2418
+ SAR: 2,
2419
+ QAR: 2,
2420
+ EGP: 2,
2421
+ // 0-decimal
2422
+ JPY: 0,
2423
+ KRW: 0,
2424
+ ISK: 0,
2425
+ CLP: 0,
2426
+ VND: 0,
2427
+ XOF: 0,
2428
+ XAF: 0,
2429
+ PYG: 0,
2430
+ // 3-decimal
2431
+ BHD: 3,
2432
+ KWD: 3,
2433
+ OMR: 3,
2434
+ TND: 3,
2435
+ JOD: 3,
2436
+ IQD: 3,
2437
+ LYD: 3
2438
+ };
2439
+ }
2440
+ });
2441
+
2442
+ // src/money/descriptor.ts
2443
+ function isMultiOptions(o) {
2444
+ return "currencies" in o;
2445
+ }
2446
+ function money(options) {
2447
+ const hasFixed = "currency" in options;
2448
+ const hasMulti = "currencies" in options;
2449
+ if (hasFixed && hasMulti) {
2450
+ throw new TypeError("money: `currency` and `currencies` are mutually exclusive");
2451
+ }
2452
+ if (!hasFixed && !hasMulti) {
2453
+ throw new TypeError("money: one of `currency` or `currencies` is required");
2454
+ }
2455
+ const rounding = options.rounding;
2456
+ if (isMultiOptions(options)) {
2457
+ const overrides = options.scaleOverrides ?? {};
2458
+ const allowList = options.currencies;
2459
+ const allows = (c) => allowList === "any" ? true : allowList.includes(c);
2460
+ const scaleFor = (c) => {
2461
+ if (!allows(c)) throw new MoneyCurrencyError(c, "not-allowed");
2462
+ const s = overrides[c] ?? scaleForCurrency(c);
2463
+ if (s === null || s === void 0) throw new MoneyCurrencyError(c, "unknown-scale");
2464
+ return s;
2465
+ };
2466
+ if (allowList !== "any") for (const c of allowList) scaleFor(c);
2467
+ const soleCurrency = () => allowList !== "any" && allowList.length === 1 ? allowList[0] : void 0;
2468
+ return {
2469
+ _noydbMoney: true,
2470
+ mode: "multi",
2471
+ options,
2472
+ rounding,
2473
+ fixedCurrency: void 0,
2474
+ scaleFor,
2475
+ allows,
2476
+ soleCurrency
2477
+ };
2478
+ }
2479
+ const currency = options.currency;
2480
+ const resolvedScale = options.scale ?? scaleForCurrency(currency);
2481
+ if (resolvedScale === null || resolvedScale === void 0) {
2482
+ throw new MoneyCurrencyError(currency, "unknown-scale");
2483
+ }
2484
+ return {
2485
+ _noydbMoney: true,
2486
+ mode: "fixed",
2487
+ options,
2488
+ rounding,
2489
+ fixedCurrency: currency,
2490
+ scaleFor(c) {
2491
+ if (c !== currency) throw new MoneyCurrencyError(c, "not-allowed");
2492
+ return resolvedScale;
2493
+ },
2494
+ allows: (c) => c === currency,
2495
+ soleCurrency: () => currency
2496
+ };
2497
+ }
2498
+ function isMoneyDescriptor(x) {
2499
+ return typeof x === "object" && x !== null && x._noydbMoney === true;
2500
+ }
2501
+ var MoneyPrecisionError, MoneyCurrencyError, MoneyUnsupportedError;
2502
+ var init_descriptor = __esm({
2503
+ "src/money/descriptor.ts"() {
2504
+ "use strict";
2505
+ init_iso4217();
2506
+ init_errors();
2507
+ MoneyPrecisionError = class extends NoydbError {
2508
+ constructor(field, value, scale) {
2509
+ super(
2510
+ "MONEY_PRECISION",
2511
+ `money: value ${JSON.stringify(value)} for field "${field}" exceeds scale ${scale} and no rounding mode is configured`
2512
+ );
2513
+ this.field = field;
2514
+ this.value = value;
2515
+ this.scale = scale;
2516
+ this.name = "MoneyPrecisionError";
2517
+ }
2518
+ field;
2519
+ value;
2520
+ scale;
2521
+ };
2522
+ MoneyCurrencyError = class extends NoydbError {
2523
+ constructor(currency, reason, field) {
2524
+ super(
2525
+ "MONEY_CURRENCY",
2526
+ reason === "not-allowed" ? `money: currency "${currency}" is not allowed${field ? ` for field "${field}"` : ""}` : `money: no scale known for currency "${currency}"${field ? ` (field "${field}")` : ""} \u2014 pass an explicit scale / scaleOverrides`
2527
+ );
2528
+ this.currency = currency;
2529
+ this.reason = reason;
2530
+ this.field = field;
2531
+ this.name = "MoneyCurrencyError";
2532
+ }
2533
+ currency;
2534
+ reason;
2535
+ field;
2536
+ };
2537
+ MoneyUnsupportedError = class extends NoydbError {
2538
+ constructor(field, message) {
2539
+ super(
2540
+ "MONEY_UNSUPPORTED",
2541
+ message ?? `money: operation is not supported on field "${field}" \u2014 use sum() and count() and divide at the boundary`
2542
+ );
2543
+ this.field = field;
2544
+ this.name = "MoneyUnsupportedError";
2545
+ }
2546
+ field;
2547
+ };
2548
+ }
2549
+ });
2550
+
2224
2551
  // src/team/tiers.ts
2225
2552
  function dekKey(collection, tier) {
2226
2553
  if (tier <= 0) return collection;
@@ -2340,6 +2667,189 @@ var init_predicate = __esm({
2340
2667
  }
2341
2668
  });
2342
2669
 
2670
+ // src/money/money-reducer.ts
2671
+ function toScaledInt(v) {
2672
+ if (typeof v === "string" || typeof v === "number" || typeof v === "bigint") {
2673
+ try {
2674
+ return BigInt(v);
2675
+ } catch {
2676
+ return null;
2677
+ }
2678
+ }
2679
+ return null;
2680
+ }
2681
+ function readMoney(record, field, desc) {
2682
+ const raw = readPath(record, field);
2683
+ if (raw === null || raw === void 0) return null;
2684
+ if (desc.mode === "fixed") {
2685
+ const value2 = toScaledInt(raw);
2686
+ return value2 === null ? null : { currency: desc.fixedCurrency, value: value2 };
2687
+ }
2688
+ if (typeof raw !== "object") return null;
2689
+ const o = raw;
2690
+ if (typeof o.currency !== "string") return null;
2691
+ const value = toScaledInt(o.amount);
2692
+ return value === null ? null : { currency: o.currency, value };
2693
+ }
2694
+ function targetScaleFor(desc, currency) {
2695
+ if (desc.allows(currency)) return desc.scaleFor(currency);
2696
+ const s = scaleForCurrency(currency);
2697
+ if (s === null) {
2698
+ throw new Error(`money: cannot determine scale for conversion target "${currency}"`);
2699
+ }
2700
+ return s;
2701
+ }
2702
+ function parseRate(rate) {
2703
+ const s = String(rate).trim();
2704
+ const neg = s.startsWith("-");
2705
+ const body = neg ? s.slice(1) : s;
2706
+ const dot = body.indexOf(".");
2707
+ const intPart = dot === -1 ? body : body.slice(0, dot);
2708
+ const fracPart = dot === -1 ? "" : body.slice(dot + 1);
2709
+ const int = BigInt((intPart === "" ? "0" : intPart) + fracPart);
2710
+ return { int: neg ? -int : int, scale: fracPart.length };
2711
+ }
2712
+ function divRoundHalfEven(n, d) {
2713
+ const q = n / d;
2714
+ const r = n % d;
2715
+ const twiceR = (r < 0n ? -r : r) * 2n;
2716
+ if (twiceR < d) return q;
2717
+ if (twiceR > d) return q + (n < 0n ? -1n : 1n);
2718
+ return q % 2n === 0n ? q : q + (n < 0n ? -1n : 1n);
2719
+ }
2720
+ function convertScaled(value, srcScale, rate, targetScale) {
2721
+ const { int: rateInt, scale: rateScale } = parseRate(rate);
2722
+ const product = value * rateInt;
2723
+ const curScale = srcScale + rateScale;
2724
+ if (curScale === targetScale) return product;
2725
+ if (curScale < targetScale) return product * 10n ** BigInt(targetScale - curScale);
2726
+ return divRoundHalfEven(product, 10n ** BigInt(curScale - targetScale));
2727
+ }
2728
+ function finalizeSum(state, desc, convertTo, fx) {
2729
+ if (convertTo !== void 0) {
2730
+ if (fx === void 0) {
2731
+ throw new Error(`money: sum convertTo "${convertTo}" requires an fx rate map`);
2732
+ }
2733
+ const targetScale = targetScaleFor(desc, convertTo);
2734
+ let total = 0n;
2735
+ for (const [cur, v] of state) {
2736
+ if (cur === convertTo) {
2737
+ total += convertScaled(v, desc.scaleFor(cur), 1, targetScale);
2738
+ continue;
2739
+ }
2740
+ const rate = fx[`${cur}->${convertTo}`];
2741
+ if (rate === void 0) {
2742
+ throw new Error(`money: no fx rate for "${cur}->${convertTo}"`);
2743
+ }
2744
+ total += convertScaled(v, desc.scaleFor(cur), rate, targetScale);
2745
+ }
2746
+ return formatScaledInt(total, targetScale);
2747
+ }
2748
+ if (desc.mode === "fixed") {
2749
+ const cur = desc.fixedCurrency;
2750
+ return formatScaledInt(state.get(cur) ?? 0n, desc.scaleFor(cur));
2751
+ }
2752
+ const out = {};
2753
+ for (const [cur, v] of state) out[cur] = formatScaledInt(v, desc.scaleFor(cur));
2754
+ return out;
2755
+ }
2756
+ function moneySumReducer(field, desc, convertTo, fx) {
2757
+ return {
2758
+ op: "sum",
2759
+ field,
2760
+ init: () => /* @__PURE__ */ new Map(),
2761
+ step: (state, record) => {
2762
+ const m = readMoney(record, field, desc);
2763
+ if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) + m.value);
2764
+ return state;
2765
+ },
2766
+ remove: (state, record) => {
2767
+ const m = readMoney(record, field, desc);
2768
+ if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) - m.value);
2769
+ return state;
2770
+ },
2771
+ finalize: (state) => finalizeSum(state, desc, convertTo, fx)
2772
+ };
2773
+ }
2774
+ function extremum(values, op) {
2775
+ let out = values[0];
2776
+ for (let i = 1; i < values.length; i++) {
2777
+ const v = values[i];
2778
+ if (op === "min" ? v < out : v > out) out = v;
2779
+ }
2780
+ return out;
2781
+ }
2782
+ function moneyMinMaxReducer(op, field, desc) {
2783
+ return {
2784
+ op,
2785
+ field,
2786
+ init: () => /* @__PURE__ */ new Map(),
2787
+ step: (state, record) => {
2788
+ const m = readMoney(record, field, desc);
2789
+ if (m) {
2790
+ const arr = state.get(m.currency);
2791
+ if (arr) arr.push(m.value);
2792
+ else state.set(m.currency, [m.value]);
2793
+ }
2794
+ return state;
2795
+ },
2796
+ remove: (state, record) => {
2797
+ const m = readMoney(record, field, desc);
2798
+ if (m) {
2799
+ const arr = state.get(m.currency);
2800
+ if (arr) {
2801
+ const idx = arr.indexOf(m.value);
2802
+ if (idx >= 0) arr.splice(idx, 1);
2803
+ }
2804
+ }
2805
+ return state;
2806
+ },
2807
+ finalize: (state) => {
2808
+ if (desc.mode === "fixed") {
2809
+ const cur = desc.fixedCurrency;
2810
+ const arr = state.get(cur);
2811
+ if (!arr || arr.length === 0) return null;
2812
+ return formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
2813
+ }
2814
+ const out = {};
2815
+ for (const [cur, arr] of state) {
2816
+ if (arr.length > 0) out[cur] = formatScaledInt(extremum(arr, op), desc.scaleFor(cur));
2817
+ }
2818
+ return out;
2819
+ }
2820
+ };
2821
+ }
2822
+ function wrapMoneyReducers(spec, moneyFields) {
2823
+ let changed = false;
2824
+ const out = {};
2825
+ for (const [key, reducer] of Object.entries(spec)) {
2826
+ const field = reducer.field;
2827
+ const desc = field ? moneyFields[field] : void 0;
2828
+ if (desc && reducer.op === "avg") {
2829
+ throw new MoneyUnsupportedError(
2830
+ field,
2831
+ `avg() is not supported on money field "${field}" in v1 \u2014 use sum() and count() and divide at the boundary.`
2832
+ );
2833
+ }
2834
+ if (desc && (reducer.op === "sum" || reducer.op === "min" || reducer.op === "max")) {
2835
+ changed = true;
2836
+ out[key] = reducer.op === "sum" ? moneySumReducer(field, desc, reducer.convertTo, reducer.fx) : moneyMinMaxReducer(reducer.op, field, desc);
2837
+ } else {
2838
+ out[key] = reducer;
2839
+ }
2840
+ }
2841
+ return changed ? out : spec;
2842
+ }
2843
+ var init_money_reducer = __esm({
2844
+ "src/money/money-reducer.ts"() {
2845
+ "use strict";
2846
+ init_predicate();
2847
+ init_fixed_point();
2848
+ init_iso4217();
2849
+ init_descriptor();
2850
+ }
2851
+ });
2852
+
2343
2853
  // src/aggregate/aggregation.ts
2344
2854
  function reduceRecords(records, spec) {
2345
2855
  const state = {};
@@ -2557,19 +3067,22 @@ var init_groupby = __esm({
2557
3067
  init_aggregation();
2558
3068
  init_canonical_key();
2559
3069
  init_errors();
3070
+ init_money_reducer();
2560
3071
  GROUPBY_WARN_CARDINALITY = 1e4;
2561
3072
  GROUPBY_MAX_CARDINALITY = 1e5;
2562
3073
  warnedCardinalityFields = /* @__PURE__ */ new Set();
2563
3074
  GroupedQueryBase = class {
2564
- constructor(executeRecords, fieldOrFields, upstreams, dictLabelResolver) {
3075
+ constructor(executeRecords, fieldOrFields, upstreams, dictLabelResolver, moneyFields) {
2565
3076
  this.executeRecords = executeRecords;
2566
3077
  this.upstreams = upstreams;
2567
3078
  this.dictLabelResolver = dictLabelResolver;
3079
+ this.moneyFields = moneyFields;
2568
3080
  this.fields = typeof fieldOrFields === "string" ? [fieldOrFields] : [...fieldOrFields];
2569
3081
  }
2570
3082
  executeRecords;
2571
3083
  upstreams;
2572
3084
  dictLabelResolver;
3085
+ moneyFields;
2573
3086
  /**
2574
3087
  * Field set this grouped query buckets on. Stored in declaration
2575
3088
  * order — the same order is preserved on every result row by
@@ -2577,6 +3090,10 @@ var init_groupby = __esm({
2577
3090
  * `[field]`.
2578
3091
  */
2579
3092
  fields;
3093
+ /** Apply money-aware reducer rewriting when money fields are declared. */
3094
+ wrapSpec(spec) {
3095
+ return this.moneyFields ? wrapMoneyReducers(spec, this.moneyFields) : spec;
3096
+ }
2580
3097
  };
2581
3098
  GroupedQuery = class extends GroupedQueryBase {
2582
3099
  /**
@@ -2589,7 +3106,7 @@ var init_groupby = __esm({
2589
3106
  return new GroupedAggregation(
2590
3107
  this.executeRecords,
2591
3108
  this.fields,
2592
- spec,
3109
+ this.wrapSpec(spec),
2593
3110
  this.upstreams,
2594
3111
  this.dictLabelResolver
2595
3112
  );
@@ -2600,7 +3117,7 @@ var init_groupby = __esm({
2600
3117
  return new GroupedAggregation(
2601
3118
  this.executeRecords,
2602
3119
  this.fields,
2603
- spec,
3120
+ this.wrapSpec(spec),
2604
3121
  this.upstreams,
2605
3122
  this.dictLabelResolver
2606
3123
  );
@@ -2716,14 +3233,21 @@ var init_executor = __esm({
2716
3233
  * Compare existing vs incoming for each `frozenFields.fields` entry
2717
3234
  * when `frozenFields.when(existing)` is true. Throws
2718
3235
  * `FieldFrozenError` listing every changed frozen field.
3236
+ *
3237
+ * @param skipFields — field names that are schema-owned computed fields.
3238
+ * These are excluded from the comparison because `incoming` carries the
3239
+ * raw user input (computed fields not yet evaluated), so comparing
3240
+ * `existing[field]` vs `incoming[field]` would always look like a
3241
+ * change even when the computed result is unchanged.
2719
3242
  */
2720
- async checkFrozenFields(guard, id, existing, incoming) {
3243
+ async checkFrozenFields(guard, id, existing, incoming, skipFields) {
2721
3244
  const ff = guard.frozenFields;
2722
3245
  if (!ff) return;
2723
3246
  if (existing === null) return;
2724
3247
  if (!ff.when(existing)) return;
2725
3248
  const changed = [];
2726
3249
  for (const f of ff.fields) {
3250
+ if (skipFields?.has(String(f))) continue;
2727
3251
  if (existing[f] !== incoming[f]) {
2728
3252
  if (!deepEqual2(existing[f], incoming[f])) changed.push(String(f));
2729
3253
  }
@@ -4052,6 +4576,476 @@ var init_delegation = __esm({
4052
4576
  }
4053
4577
  });
4054
4578
 
4579
+ // src/federation/classify-skip.ts
4580
+ function classifyShardSkip(err) {
4581
+ return err instanceof NoAccessError ? "no-grant" : "error";
4582
+ }
4583
+ var init_classify_skip = __esm({
4584
+ "src/federation/classify-skip.ts"() {
4585
+ "use strict";
4586
+ init_errors();
4587
+ }
4588
+ });
4589
+
4590
+ // src/federation/cross-vault-live.ts
4591
+ var CrossVaultLive;
4592
+ var init_cross_vault_live = __esm({
4593
+ "src/federation/cross-vault-live.ts"() {
4594
+ "use strict";
4595
+ CrossVaultLive = class {
4596
+ snapshot;
4597
+ error = null;
4598
+ ready;
4599
+ subs = /* @__PURE__ */ new Set();
4600
+ unsubChange;
4601
+ opts;
4602
+ stopped = false;
4603
+ computing = false;
4604
+ dirty = false;
4605
+ scheduled = false;
4606
+ timer = null;
4607
+ resolveReady;
4608
+ settledOnce = false;
4609
+ constructor(opts) {
4610
+ this.opts = opts;
4611
+ this.snapshot = opts.initialSnapshot;
4612
+ this.ready = new Promise((res) => {
4613
+ this.resolveReady = res;
4614
+ });
4615
+ this.unsubChange = opts.subscribeToChanges((e) => {
4616
+ if (this.stopped || !opts.isRelevant(e)) return;
4617
+ this.schedule();
4618
+ });
4619
+ this.schedule();
4620
+ }
4621
+ subscribe(cb) {
4622
+ if (this.stopped) return () => {
4623
+ };
4624
+ this.subs.add(cb);
4625
+ return () => this.subs.delete(cb);
4626
+ }
4627
+ stop() {
4628
+ if (this.stopped) return;
4629
+ this.stopped = true;
4630
+ this.unsubChange();
4631
+ if (this.timer !== null) clearTimeout(this.timer);
4632
+ this.subs.clear();
4633
+ if (!this.settledOnce) this.resolveReady();
4634
+ }
4635
+ schedule() {
4636
+ if (this.stopped) return;
4637
+ if (this.computing) {
4638
+ this.dirty = true;
4639
+ return;
4640
+ }
4641
+ if (this.scheduled) return;
4642
+ this.scheduled = true;
4643
+ const run = () => {
4644
+ this.scheduled = false;
4645
+ void this.runCompute();
4646
+ };
4647
+ const ms = this.opts.debounceMs ?? 0;
4648
+ if (ms > 0) this.timer = setTimeout(run, ms);
4649
+ else queueMicrotask(run);
4650
+ }
4651
+ async runCompute() {
4652
+ if (this.stopped) return;
4653
+ this.computing = true;
4654
+ this.dirty = false;
4655
+ try {
4656
+ const next = await this.opts.compute();
4657
+ if (this.stopped) return;
4658
+ this.snapshot = next;
4659
+ this.error = null;
4660
+ } catch (err) {
4661
+ if (this.stopped) return;
4662
+ this.error = err instanceof Error ? err : new Error(String(err));
4663
+ } finally {
4664
+ this.computing = false;
4665
+ if (!this.stopped) {
4666
+ if (!this.settledOnce) {
4667
+ this.settledOnce = true;
4668
+ this.resolveReady();
4669
+ }
4670
+ for (const cb of this.subs) cb();
4671
+ if (this.dirty) this.schedule();
4672
+ }
4673
+ }
4674
+ }
4675
+ };
4676
+ }
4677
+ });
4678
+
4679
+ // src/federation/aggregate-across.ts
4680
+ var CrossVaultAggregation, CrossVaultGroupedAggregation;
4681
+ var init_aggregate_across = __esm({
4682
+ "src/federation/aggregate-across.ts"() {
4683
+ "use strict";
4684
+ init_aggregation();
4685
+ init_groupby();
4686
+ init_cross_vault_live();
4687
+ CrossVaultAggregation = class {
4688
+ constructor(src, spec, bind) {
4689
+ this.src = src;
4690
+ this.spec = spec;
4691
+ this.bind = bind;
4692
+ }
4693
+ src;
4694
+ spec;
4695
+ bind;
4696
+ async run(options = {}) {
4697
+ const { records, skippedVaults } = await this.src.fanoutRecords(options);
4698
+ return { result: reduceRecords(records, this.spec), skippedVaults };
4699
+ }
4700
+ live(options = {}) {
4701
+ if (!this.bind) throw new Error("CrossVaultAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.aggregate()");
4702
+ const spec = this.spec;
4703
+ const src = this.src;
4704
+ const core = new CrossVaultLive({
4705
+ subscribeToChanges: this.bind.subscribeToChanges,
4706
+ isRelevant: this.bind.isRelevant,
4707
+ compute: async () => {
4708
+ const { records, skippedVaults } = await src.fanoutRecords(options);
4709
+ return { value: reduceRecords(records, spec), skipped: skippedVaults };
4710
+ },
4711
+ initialSnapshot: { value: void 0, skipped: [] },
4712
+ ...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
4713
+ });
4714
+ return {
4715
+ get value() {
4716
+ return core.snapshot.value;
4717
+ },
4718
+ get skippedVaults() {
4719
+ return core.snapshot.skipped;
4720
+ },
4721
+ get error() {
4722
+ return core.error;
4723
+ },
4724
+ ready: core.ready,
4725
+ subscribe: (cb) => core.subscribe(cb),
4726
+ stop: () => core.stop()
4727
+ };
4728
+ }
4729
+ };
4730
+ CrossVaultGroupedAggregation = class {
4731
+ constructor(src, field, spec, bind) {
4732
+ this.src = src;
4733
+ this.field = field;
4734
+ this.spec = spec;
4735
+ this.bind = bind;
4736
+ }
4737
+ src;
4738
+ field;
4739
+ spec;
4740
+ bind;
4741
+ async run(options = {}) {
4742
+ const { records, skippedVaults } = await this.src.fanoutRecords(options);
4743
+ return {
4744
+ results: groupAndReduce(records, this.field, this.spec),
4745
+ skippedVaults
4746
+ };
4747
+ }
4748
+ live(options = {}) {
4749
+ if (!this.bind) throw new Error("CrossVaultGroupedAggregation: live() requires a LiveBinding \u2014 use ShardedQuery.groupBy().aggregate()");
4750
+ const field = this.field;
4751
+ const spec = this.spec;
4752
+ const src = this.src;
4753
+ const core = new CrossVaultLive({
4754
+ subscribeToChanges: this.bind.subscribeToChanges,
4755
+ isRelevant: this.bind.isRelevant,
4756
+ compute: async () => {
4757
+ const { records, skippedVaults } = await src.fanoutRecords(options);
4758
+ return {
4759
+ records: groupAndReduce(records, field, spec),
4760
+ skipped: skippedVaults
4761
+ };
4762
+ },
4763
+ initialSnapshot: { records: [], skipped: [] },
4764
+ ...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
4765
+ });
4766
+ return {
4767
+ get value() {
4768
+ return core.snapshot.records;
4769
+ },
4770
+ get skippedVaults() {
4771
+ return core.snapshot.skipped;
4772
+ },
4773
+ get error() {
4774
+ return core.error;
4775
+ },
4776
+ ready: core.ready,
4777
+ subscribe: (cb) => core.subscribe(cb),
4778
+ stop: () => core.stop()
4779
+ };
4780
+ }
4781
+ };
4782
+ }
4783
+ });
4784
+
4785
+ // src/federation/vault-group.ts
4786
+ var vault_group_exports = {};
4787
+ __export(vault_group_exports, {
4788
+ ShardedCollection: () => ShardedCollection,
4789
+ ShardedGroupedQuery: () => ShardedGroupedQuery,
4790
+ ShardedQuery: () => ShardedQuery,
4791
+ VaultGroup: () => VaultGroup
4792
+ });
4793
+ function assertSafePartitionKey(partitionKey) {
4794
+ if (partitionKey.length === 0) {
4795
+ throw new ValidationError("partitionKey must be a non-empty string");
4796
+ }
4797
+ if (!SAFE_PARTITION_KEY.test(partitionKey)) {
4798
+ throw new ValidationError(
4799
+ `partitionKey "${partitionKey}" contains characters outside [A-Za-z0-9._-]. Map your records to a store-safe key in sharding.keyOf.`
4800
+ );
4801
+ }
4802
+ if (partitionKey.includes(SHARD_SEPARATOR)) {
4803
+ throw new ValidationError(
4804
+ `partitionKey "${partitionKey}" must not contain "--" \u2014 it is reserved as the shard vault-id separator and would risk shard-id collisions.`
4805
+ );
4806
+ }
4807
+ }
4808
+ var SHARD_SEPARATOR, SAFE_PARTITION_KEY, VaultGroup, ShardedCollection, ShardedQuery, ShardedGroupedQuery;
4809
+ var init_vault_group = __esm({
4810
+ "src/federation/vault-group.ts"() {
4811
+ "use strict";
4812
+ init_errors();
4813
+ init_classify_skip();
4814
+ init_cross_vault_live();
4815
+ init_aggregate_across();
4816
+ SHARD_SEPARATOR = "--";
4817
+ SAFE_PARTITION_KEY = /^[A-Za-z0-9._-]+$/;
4818
+ VaultGroup = class {
4819
+ constructor(db, name, registry, sharding, template) {
4820
+ this.db = db;
4821
+ this.name = name;
4822
+ this.registry = registry;
4823
+ this.sharding = sharding;
4824
+ this.template = template;
4825
+ if (name.includes(SHARD_SEPARATOR)) {
4826
+ throw new ValidationError(
4827
+ `VaultGroup name "${name}" must not contain "--" (reserved shard vault-id separator).`
4828
+ );
4829
+ }
4830
+ }
4831
+ db;
4832
+ name;
4833
+ registry;
4834
+ sharding;
4835
+ template;
4836
+ /** Deterministic vault name for a partition key, namespaced by the group. */
4837
+ shardVaultId(partitionKey) {
4838
+ assertSafePartitionKey(partitionKey);
4839
+ return `${this.name}${SHARD_SEPARATOR}${partitionKey}`;
4840
+ }
4841
+ /** All registry rows (hydrates the registry collection first). */
4842
+ async allRows() {
4843
+ await this.registry.list();
4844
+ return this.registry.query().toArray();
4845
+ }
4846
+ /** Open an existing shard and apply the template. */
4847
+ async openShard(partitionKey) {
4848
+ const vault = await this.db.openVault(this.shardVaultId(partitionKey), { create: false });
4849
+ this.template.configure(vault);
4850
+ return vault;
4851
+ }
4852
+ /**
4853
+ * Idempotently provision a shard for `partitionKey`. Returns the
4854
+ * configured vault handle.
4855
+ *
4856
+ * - row + vault present → no-op, return handle
4857
+ * - row present, vault gone → ShardProvisioningError
4858
+ * - row absent (vault present or not) → open-or-create, configure, write row
4859
+ */
4860
+ async createShard(partitionKey) {
4861
+ const vaultId = this.shardVaultId(partitionKey);
4862
+ const row = await this.registry.get(partitionKey);
4863
+ const provisioned = await this.db._shardVaultProvisioned(vaultId);
4864
+ if (row && !provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
4865
+ if (row && provisioned) return this.openShard(partitionKey);
4866
+ const vault = await this.db.openVault(vaultId);
4867
+ this.template.configure(vault);
4868
+ await this.registry.put(partitionKey, {
4869
+ vaultId,
4870
+ partitionKey,
4871
+ templateName: this.sharding.vaultTemplate,
4872
+ schemaVersion: this.template.version,
4873
+ createdAt: Date.now()
4874
+ });
4875
+ return vault;
4876
+ }
4877
+ /**
4878
+ * Drill down to a single shard's full Collection API. Throws if the shard is unknown.
4879
+ * Also throws ShardProvisioningError if the registry row exists but the vault has been deleted
4880
+ * (registry/store divergence).
4881
+ */
4882
+ async shard(partitionKey) {
4883
+ const vaultId = this.shardVaultId(partitionKey);
4884
+ const row = await this.registry.get(partitionKey);
4885
+ if (!row) throw new UnknownShardError(partitionKey, this.name);
4886
+ const provisioned = await this.db._shardVaultProvisioned(vaultId);
4887
+ if (!provisioned) throw new ShardProvisioningError(vaultId, partitionKey);
4888
+ return this.openShard(partitionKey);
4889
+ }
4890
+ /** A sharded view over one logical collection across all shards. */
4891
+ collection(collectionName) {
4892
+ return new ShardedCollection(this, collectionName);
4893
+ }
4894
+ /** @internal — eligible (openable-candidate) rows + drift/divergence skips. */
4895
+ async resolveEligible(options = {}) {
4896
+ const rows = await this.allRows();
4897
+ const skipped = [];
4898
+ const versionOk = [];
4899
+ for (const row of rows) {
4900
+ if (options.minVersion !== void 0 && row.schemaVersion < options.minVersion) {
4901
+ skipped.push({ vaultId: row.vaultId, reason: "schema-drift" });
4902
+ } else versionOk.push(row);
4903
+ }
4904
+ const provisioned = await Promise.all(versionOk.map((r) => this.db._shardVaultProvisioned(r.vaultId)));
4905
+ const eligible = [];
4906
+ versionOk.forEach((row, i) => {
4907
+ if (provisioned[i]) eligible.push(row);
4908
+ else skipped.push({ vaultId: row.vaultId, reason: "error", error: new ShardProvisioningError(row.vaultId, row.partitionKey) });
4909
+ });
4910
+ return { eligible, skipped };
4911
+ }
4912
+ };
4913
+ ShardedCollection = class {
4914
+ constructor(group, collectionName) {
4915
+ this.group = group;
4916
+ this.collectionName = collectionName;
4917
+ }
4918
+ group;
4919
+ collectionName;
4920
+ /** Route a write to the shard owning `keyOf(record)`. */
4921
+ async put(id, record) {
4922
+ const key = this.group.sharding.keyOf(record);
4923
+ const row = await this.group.registry.get(key);
4924
+ let vault;
4925
+ if (!row) {
4926
+ if (this.group.sharding.autoCreate === false) {
4927
+ throw new UnknownShardError(key, this.group.name);
4928
+ }
4929
+ vault = await this.group.createShard(key);
4930
+ } else {
4931
+ vault = await this.group.openShard(key);
4932
+ }
4933
+ await vault.collection(this.collectionName).put(id, record);
4934
+ }
4935
+ /** Begin a cross-shard fan-out query. */
4936
+ query() {
4937
+ return new ShardedQuery(this.group, this.collectionName, []);
4938
+ }
4939
+ };
4940
+ ShardedQuery = class _ShardedQuery {
4941
+ constructor(group, collectionName, clauses) {
4942
+ this.group = group;
4943
+ this.collectionName = collectionName;
4944
+ this.clauses = clauses;
4945
+ }
4946
+ group;
4947
+ collectionName;
4948
+ clauses;
4949
+ where(field, op, value) {
4950
+ return new _ShardedQuery(this.group, this.collectionName, [
4951
+ ...this.clauses,
4952
+ { field, op, value }
4953
+ ]);
4954
+ }
4955
+ /** @internal — fan out the where-filtered records across eligible shards. */
4956
+ async fanoutRecords(options = {}) {
4957
+ const { eligible, skipped } = await this.group.resolveEligible(options);
4958
+ const across = await this.group.db.queryAcross(
4959
+ eligible.map((r) => r.vaultId),
4960
+ async (vault) => {
4961
+ this.group.template.configure(vault);
4962
+ const coll = vault.collection(this.collectionName);
4963
+ await coll.list();
4964
+ let q = coll.query();
4965
+ for (const c of this.clauses) q = q.where(c.field, c.op, c.value);
4966
+ return q.toArray();
4967
+ },
4968
+ { concurrency: options.concurrency ?? 1, create: false }
4969
+ );
4970
+ const results = [];
4971
+ for (const r of across) {
4972
+ if (r.error) skipped.push({ vaultId: r.vault, reason: classifyShardSkip(r.error), error: r.error });
4973
+ else for (const item of r.result) results.push(item);
4974
+ }
4975
+ return { records: results, skippedVaults: skipped };
4976
+ }
4977
+ /** Fan out across eligible shards and merge results. */
4978
+ async toArray(options = {}) {
4979
+ const { records, skippedVaults } = await this.fanoutRecords(options);
4980
+ return { results: records, skippedVaults };
4981
+ }
4982
+ /** @internal — build the change-subscription + relevance binding for this query's group+collection. */
4983
+ liveBinding() {
4984
+ const group = this.group;
4985
+ const collectionName = this.collectionName;
4986
+ return {
4987
+ subscribeToChanges: (h) => {
4988
+ group.db.on("change", h);
4989
+ return () => group.db.off("change", h);
4990
+ },
4991
+ isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`)
4992
+ };
4993
+ }
4994
+ /** Returns a reactive cross-shard live query — a facade over CrossVaultLive. */
4995
+ live(options = {}) {
4996
+ const bind = this.liveBinding();
4997
+ const core = new CrossVaultLive({
4998
+ ...bind,
4999
+ compute: async () => {
5000
+ const { records, skippedVaults } = await this.fanoutRecords(options);
5001
+ return { records, skipped: skippedVaults };
5002
+ },
5003
+ initialSnapshot: { records: [], skipped: [] },
5004
+ ...options.debounceMs !== void 0 ? { debounceMs: options.debounceMs } : {}
5005
+ });
5006
+ return {
5007
+ get value() {
5008
+ return core.snapshot.records;
5009
+ },
5010
+ get skippedVaults() {
5011
+ return core.snapshot.skipped;
5012
+ },
5013
+ get error() {
5014
+ return core.error;
5015
+ },
5016
+ ready: core.ready,
5017
+ subscribe: (cb) => core.subscribe(cb),
5018
+ stop: () => core.stop()
5019
+ };
5020
+ }
5021
+ /** One-shot distributed aggregate — central reduce over all shard records. */
5022
+ aggregate(spec) {
5023
+ return new CrossVaultAggregation(this, spec, this.liveBinding());
5024
+ }
5025
+ /** Begin a grouped cross-shard aggregate. */
5026
+ groupBy(field) {
5027
+ return new ShardedGroupedQuery(this, field);
5028
+ }
5029
+ };
5030
+ ShardedGroupedQuery = class {
5031
+ constructor(query, field) {
5032
+ this.query = query;
5033
+ this.field = field;
5034
+ }
5035
+ query;
5036
+ field;
5037
+ aggregate(spec) {
5038
+ return new CrossVaultGroupedAggregation(
5039
+ { fanoutRecords: (o) => this.query.fanoutRecords(o) },
5040
+ this.field,
5041
+ spec,
5042
+ this.query.liveBinding()
5043
+ );
5044
+ }
5045
+ };
5046
+ }
5047
+ });
5048
+
4055
5049
  // src/index.ts
4056
5050
  var src_exports = {};
4057
5051
  __export(src_exports, {
@@ -4076,6 +5070,7 @@ __export(src_exports, {
4076
5070
  CollectionFrame: () => CollectionFrame,
4077
5071
  CollectionIndexes: () => CollectionIndexes,
4078
5072
  CollectionInstant: () => CollectionInstant,
5073
+ ComputedFieldError: () => ComputedFieldError,
4079
5074
  ConflictError: () => ConflictError,
4080
5075
  CrossJoinSourceUnknownError: () => CrossJoinSourceUnknownError,
4081
5076
  CrossJoinTooLargeError: () => CrossJoinTooLargeError,
@@ -4139,6 +5134,9 @@ __export(src_exports, {
4139
5134
  MemorySealingKeyProvider: () => MemorySealingKeyProvider,
4140
5135
  MigrationRequiredError: () => MigrationRequiredError,
4141
5136
  MissingTranslationError: () => MissingTranslationError,
5137
+ MoneyCurrencyError: () => MoneyCurrencyError,
5138
+ MoneyPrecisionError: () => MoneyPrecisionError,
5139
+ MoneyUnsupportedError: () => MoneyUnsupportedError,
4142
5140
  NOYDB_BACKUP_VERSION: () => NOYDB_BACKUP_VERSION,
4143
5141
  NOYDB_BUNDLE_FORMAT_VERSION: () => NOYDB_BUNDLE_FORMAT_VERSION,
4144
5142
  NOYDB_BUNDLE_MAGIC: () => NOYDB_BUNDLE_MAGIC,
@@ -4191,9 +5189,13 @@ __export(src_exports, {
4191
5189
  SchemaUpdateError: () => SchemaUpdateError,
4192
5190
  SchemaValidationError: () => SchemaValidationError,
4193
5191
  ScriptViolationError: () => ScriptViolationError,
5192
+ SequenceContentionError: () => SequenceContentionError,
5193
+ SequenceOfflineError: () => SequenceOfflineError,
5194
+ SequenceStore: () => SequenceStore,
4194
5195
  SessionExpiredError: () => SessionExpiredError,
4195
5196
  SessionNotFoundError: () => SessionNotFoundError,
4196
5197
  SessionPolicyError: () => SessionPolicyError,
5198
+ ShardProvisioningError: () => ShardProvisioningError,
4197
5199
  SnapshotNotFoundError: () => SnapshotNotFoundError,
4198
5200
  StoreCapabilityError: () => StoreCapabilityError,
4199
5201
  SyncEngine: () => SyncEngine,
@@ -4209,6 +5211,7 @@ __export(src_exports, {
4209
5211
  USER_ENVELOPE_COLLECTION: () => USER_ENVELOPE_COLLECTION,
4210
5212
  USER_ENVELOPE_MAX_BYTES: () => USER_ENVELOPE_MAX_BYTES,
4211
5213
  UniqueConstraintError: () => UniqueConstraintError,
5214
+ UnknownShardError: () => UnknownShardError,
4212
5215
  UnsupportedIndexOptionError: () => UnsupportedIndexOptionError,
4213
5216
  UserApi: () => UserApi,
4214
5217
  UserEnvelopeOversizedError: () => UserEnvelopeOversizedError,
@@ -4217,6 +5220,7 @@ __export(src_exports, {
4217
5220
  Vault: () => Vault,
4218
5221
  VaultFrame: () => VaultFrame,
4219
5222
  VaultInstant: () => VaultInstant,
5223
+ VaultTemplateNotFoundError: () => VaultTemplateNotFoundError,
4220
5224
  WeakPassphraseError: () => WeakPassphraseError,
4221
5225
  activeSessionCount: () => activeSessionCount,
4222
5226
  additiveOnly: () => additiveOnly,
@@ -4273,6 +5277,7 @@ __export(src_exports, {
4273
5277
  envelopePayloadHash: () => envelopePayloadHash,
4274
5278
  estimateEntropy: () => estimateEntropy,
4275
5279
  estimateRecordBytes: () => estimateRecordBytes,
5280
+ evalComputedFields: () => evalComputedFields,
4276
5281
  evaluateClause: () => evaluateClause,
4277
5282
  evaluateExportCapability: () => evaluateExportCapability,
4278
5283
  evaluateFieldClause: () => evaluateFieldClause,
@@ -4289,6 +5294,7 @@ __export(src_exports, {
4289
5294
  hasRecoveryEnrolled: () => hasRecoveryEnrolled,
4290
5295
  hashEntry: () => hashEntry,
4291
5296
  i18nText: () => i18nText,
5297
+ immutableGuard: () => immutableGuard,
4292
5298
  inferScripts: () => inferScripts,
4293
5299
  isDevUnlockActive: () => isDevUnlockActive,
4294
5300
  isDictCollectionName: () => isDictCollectionName,
@@ -4296,6 +5302,7 @@ __export(src_exports, {
4296
5302
  isDiscriminant: () => isDiscriminant,
4297
5303
  isI18nTextDescriptor: () => isI18nTextDescriptor,
4298
5304
  isMagicLinkGrantExpired: () => isMagicLinkGrantExpired,
5305
+ isMoneyDescriptor: () => isMoneyDescriptor,
4299
5306
  isPreCompressed: () => isPreCompressed,
4300
5307
  isPublicEnvelope: () => isPublicEnvelope,
4301
5308
  isSessionAlive: () => isSessionAlive,
@@ -4327,6 +5334,7 @@ __export(src_exports, {
4327
5334
  mintPaperRecoveryEntry: () => mintPaperRecoveryEntry,
4328
5335
  mintShamirRecoveryEntry: () => mintShamirRecoveryEntry,
4329
5336
  mintWrappedDeksBlob: () => mintWrappedDeksBlob,
5337
+ money: () => money,
4330
5338
  paddedIndex: () => paddedIndex,
4331
5339
  parseBytes: () => parseBytes,
4332
5340
  parseIndex: () => parseIndex,
@@ -4367,6 +5375,7 @@ __export(src_exports, {
4367
5375
  saveShamirRecoveryEntries: () => saveShamirRecoveryEntries,
4368
5376
  saveUserEnvelope: () => saveUserEnvelope,
4369
5377
  saveVaultPolicy: () => saveVaultPolicy,
5378
+ scaleForCurrency: () => scaleForCurrency,
4370
5379
  sha256Hex: () => sha256Hex3,
4371
5380
  sum: () => sum,
4372
5381
  unwrapDeksFromBlob: () => unwrapDeksFromBlob,
@@ -4380,6 +5389,7 @@ __export(src_exports, {
4380
5389
  validateSchemaOutput: () => validateSchemaOutput,
4381
5390
  validateSessionPolicy: () => validateSessionPolicy,
4382
5391
  visibilityRecordId: () => visibilityRecordId,
5392
+ withArchive: () => withArchive,
4383
5393
  withCache: () => withCache,
4384
5394
  withCircuitBreaker: () => withCircuitBreaker,
4385
5395
  withDerivation: () => withDerivation,
@@ -6371,6 +7381,171 @@ function withHealthCheck(opts = {}) {
6371
7381
  init_errors();
6372
7382
  init_errors();
6373
7383
 
7384
+ // src/archive/engine.ts
7385
+ function isHeld(policy, record) {
7386
+ if (!policy.legalHold) return false;
7387
+ try {
7388
+ return policy.legalHold(record);
7389
+ } catch {
7390
+ return true;
7391
+ }
7392
+ }
7393
+ async function runArchive(ctx, options = {}) {
7394
+ const maxArchives = options.maxArchives ?? Infinity;
7395
+ const dryRun = options.dryRun === true;
7396
+ let archived = 0;
7397
+ let held = 0;
7398
+ let scanned = 0;
7399
+ const byCollection = {};
7400
+ outer: for (const collection of ctx.collectionsWithPolicy()) {
7401
+ const policy = ctx.getPolicy(collection);
7402
+ if (!policy) continue;
7403
+ byCollection[collection] = { archived: 0, held: 0 };
7404
+ for (const id of await ctx.listRecordIds(collection)) {
7405
+ if (archived >= maxArchives) break outer;
7406
+ const record = await ctx.getRecord(collection, id).catch(() => null);
7407
+ if (record === null) continue;
7408
+ scanned += 1;
7409
+ let eligible = false;
7410
+ try {
7411
+ eligible = policy.archiveWhen(record);
7412
+ } catch {
7413
+ eligible = false;
7414
+ }
7415
+ if (!eligible) continue;
7416
+ if (isHeld(policy, record)) {
7417
+ held += 1;
7418
+ byCollection[collection].held += 1;
7419
+ continue;
7420
+ }
7421
+ if (!dryRun) {
7422
+ const env = await ctx.getEnvelope(collection, id);
7423
+ if (!env) continue;
7424
+ await ctx.archiveStore.put(ctx.vaultId, collection, id, env);
7425
+ await ctx.removeFromPrimary(collection, id);
7426
+ }
7427
+ archived += 1;
7428
+ byCollection[collection].archived += 1;
7429
+ }
7430
+ }
7431
+ return { archived, held, scanned, byCollection };
7432
+ }
7433
+ async function runRestore(ctx, collection, id) {
7434
+ const env = await ctx.archiveStore.get(ctx.vaultId, collection, id);
7435
+ if (!env) return false;
7436
+ await ctx.restoreToPrimary(collection, id, env);
7437
+ await ctx.archiveStore.delete(ctx.vaultId, collection, id);
7438
+ return true;
7439
+ }
7440
+ async function runListArchived(ctx, collection) {
7441
+ const collections = collection ? [collection] : ctx.collectionsWithPolicy();
7442
+ const out = [];
7443
+ for (const c of collections) {
7444
+ const ids = await ctx.archiveStore.list(ctx.vaultId, c);
7445
+ for (const id of ids) out.push({ collection: c, id });
7446
+ }
7447
+ return out;
7448
+ }
7449
+
7450
+ // src/archive/index.ts
7451
+ function withArchive(opts) {
7452
+ return { store: opts.store };
7453
+ }
7454
+
7455
+ // src/sequence/index.ts
7456
+ init_types();
7457
+ init_crypto();
7458
+ init_errors();
7459
+ var SEQUENCE_COLLECTION = "_sequences";
7460
+ var MAX_NEXT_ATTEMPTS = 16;
7461
+ async function sleepBackoff(attempt) {
7462
+ const ceil = Math.min(2 ** attempt, 32);
7463
+ const ms = Math.floor(Math.random() * ceil);
7464
+ await new Promise((r) => setTimeout(r, ms));
7465
+ }
7466
+ var SequenceStore = class {
7467
+ adapter;
7468
+ vault;
7469
+ encrypted;
7470
+ getDEK;
7471
+ actor;
7472
+ /**
7473
+ * Memoized DEK promise. The `_sequences` collection DEK is created on
7474
+ * first access; without sharing one promise, a burst of concurrent
7475
+ * `next()` calls would each trigger DEK creation and diverge (one
7476
+ * writer's ciphertext unreadable by another). One shared promise → one
7477
+ * DEK.
7478
+ */
7479
+ dekPromise = null;
7480
+ constructor(opts) {
7481
+ this.adapter = opts.adapter;
7482
+ this.vault = opts.vault;
7483
+ this.encrypted = opts.encrypted;
7484
+ this.getDEK = opts.getDEK;
7485
+ this.actor = opts.actor;
7486
+ }
7487
+ /** A handle bound to one sequence name. */
7488
+ handle(name) {
7489
+ return {
7490
+ next: () => this.next(name),
7491
+ peek: () => this.peek(name)
7492
+ };
7493
+ }
7494
+ assertOnline() {
7495
+ if (this.adapter.capabilities?.casAtomic !== true) {
7496
+ throw new SequenceOfflineError();
7497
+ }
7498
+ }
7499
+ dek() {
7500
+ if (!this.dekPromise) this.dekPromise = this.getDEK(SEQUENCE_COLLECTION);
7501
+ return this.dekPromise;
7502
+ }
7503
+ async read(name) {
7504
+ const env = await this.adapter.get(this.vault, SEQUENCE_COLLECTION, name);
7505
+ if (!env) return { env: null, value: 0 };
7506
+ const json = this.encrypted ? await decrypt(env._iv, env._data, await this.dek()) : env._data;
7507
+ const state = JSON.parse(json);
7508
+ return { env, value: state.value };
7509
+ }
7510
+ async encryptState(state, version) {
7511
+ const json = JSON.stringify(state);
7512
+ if (!this.encrypted) {
7513
+ return { _noydb: NOYDB_FORMAT_VERSION, _v: version, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: "", _data: json, _by: this.actor };
7514
+ }
7515
+ const { iv, data } = await encrypt(json, await this.dek());
7516
+ return { _noydb: NOYDB_FORMAT_VERSION, _v: version, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: iv, _data: data, _by: this.actor };
7517
+ }
7518
+ async peek(name) {
7519
+ return (await this.read(name)).value;
7520
+ }
7521
+ async next(name) {
7522
+ this.assertOnline();
7523
+ let lastConflict;
7524
+ for (let attempt = 0; attempt < MAX_NEXT_ATTEMPTS; attempt++) {
7525
+ const { env, value } = await this.read(name);
7526
+ const nextValue = value + 1;
7527
+ const expectedVersion = env?._v ?? 0;
7528
+ const envelope = await this.encryptState({ value: nextValue }, expectedVersion + 1);
7529
+ try {
7530
+ await this.adapter.put(this.vault, SEQUENCE_COLLECTION, name, envelope, expectedVersion);
7531
+ return nextValue;
7532
+ } catch (err) {
7533
+ if (err instanceof ConflictError) {
7534
+ lastConflict = err;
7535
+ if (attempt < MAX_NEXT_ATTEMPTS - 1) await sleepBackoff(attempt);
7536
+ continue;
7537
+ }
7538
+ throw err;
7539
+ }
7540
+ }
7541
+ void lastConflict;
7542
+ throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
7543
+ }
7544
+ };
7545
+
7546
+ // src/index.ts
7547
+ init_errors();
7548
+
6374
7549
  // src/bundle/format.ts
6375
7550
  var NOYDB_BUNDLE_MAGIC = new Uint8Array([78, 68, 66, 49]);
6376
7551
  var NOYDB_BUNDLE_PREFIX_BYTES = 10;
@@ -7251,15 +8426,15 @@ function isEquivalent(a, b) {
7251
8426
 
7252
8427
  // src/history/history.ts
7253
8428
  var HISTORY_COLLECTION = "_history";
7254
- function matchesPrefix(id, collection, recordId2) {
7255
- if (recordId2) {
7256
- return id.startsWith(`${collection}:${recordId2}:`);
8429
+ function matchesPrefix(id, collection, recordId3) {
8430
+ if (recordId3) {
8431
+ return id.startsWith(`${collection}:${recordId3}:`);
7257
8432
  }
7258
8433
  return id.startsWith(`${collection}:`);
7259
8434
  }
7260
- async function getHistory(adapter, vault, collection, recordId2, options) {
8435
+ async function getHistory(adapter, vault, collection, recordId3, options) {
7261
8436
  const allIds = await adapter.list(vault, HISTORY_COLLECTION);
7262
- const matchingIds = allIds.filter((id) => matchesPrefix(id, collection, recordId2)).sort().reverse();
8437
+ const matchingIds = allIds.filter((id) => matchesPrefix(id, collection, recordId3)).sort().reverse();
7263
8438
  const entries = [];
7264
8439
  for (const id of matchingIds) {
7265
8440
  const envelope = await adapter.get(vault, HISTORY_COLLECTION, id);
@@ -10034,6 +11209,119 @@ function applyI18nLocale(record, i18nFields, locale, fallback, layer = "read") {
10034
11209
  return result;
10035
11210
  }
10036
11211
 
11212
+ // src/money/normalize.ts
11213
+ init_fixed_point();
11214
+ init_descriptor();
11215
+ function isMoneyValueObject(v) {
11216
+ return typeof v === "object" && v !== null && "currency" in v;
11217
+ }
11218
+ function quantizeAmount(field, input, scale, rounding) {
11219
+ const r = parseToScaledInt(input, scale, rounding);
11220
+ if (!r.ok) {
11221
+ if (r.reason === "precision") throw new MoneyPrecisionError(field, input, scale);
11222
+ throw new TypeError(`money: field "${field}" value ${JSON.stringify(input)} is not a finite decimal`);
11223
+ }
11224
+ return r.value.toString();
11225
+ }
11226
+ function quantizeMoneyFields(record, moneyFields) {
11227
+ const out = { ...record };
11228
+ for (const [field, desc] of Object.entries(moneyFields)) {
11229
+ const raw = out[field];
11230
+ if (raw === null || raw === void 0) continue;
11231
+ if (desc.mode === "fixed") {
11232
+ const currency2 = desc.fixedCurrency;
11233
+ out[field] = quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
11234
+ continue;
11235
+ }
11236
+ let amount;
11237
+ let currency;
11238
+ if (isMoneyValueObject(raw)) {
11239
+ currency = String(raw.currency);
11240
+ amount = raw.amount;
11241
+ } else {
11242
+ const sole = desc.soleCurrency();
11243
+ if (sole === void 0) {
11244
+ throw new TypeError(
11245
+ `money: field "${field}" is multi-currency \u2014 write { amount, currency }, not a bare amount`
11246
+ );
11247
+ }
11248
+ currency = sole;
11249
+ amount = raw;
11250
+ }
11251
+ const scale = desc.scaleFor(currency);
11252
+ out[field] = { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
11253
+ }
11254
+ return out;
11255
+ }
11256
+ function formatCurrency(decimal, currency, scale, locale) {
11257
+ const fmt = new Intl.NumberFormat(locale, {
11258
+ style: "currency",
11259
+ currency,
11260
+ minimumFractionDigits: scale,
11261
+ maximumFractionDigits: scale
11262
+ });
11263
+ return fmt.format(decimal);
11264
+ }
11265
+ function decodeMoneyFields(record, moneyFields, locale) {
11266
+ const out = { ...record };
11267
+ const format = locale !== "raw";
11268
+ const fmtLocale = typeof locale === "string" && locale !== "raw" ? locale : "en-US";
11269
+ for (const [field, desc] of Object.entries(moneyFields)) {
11270
+ const stored = out[field];
11271
+ if (stored === null || stored === void 0) continue;
11272
+ let currency;
11273
+ let scaledIntString;
11274
+ if (desc.mode === "fixed") {
11275
+ if (typeof stored !== "string" && typeof stored !== "number") continue;
11276
+ currency = desc.fixedCurrency;
11277
+ scaledIntString = String(stored);
11278
+ } else {
11279
+ if (!isMoneyValueObject(stored)) continue;
11280
+ const amount = stored.amount;
11281
+ if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") continue;
11282
+ currency = stored.currency;
11283
+ scaledIntString = String(amount);
11284
+ }
11285
+ const scale = desc.scaleFor(currency);
11286
+ const decimal = formatScaledInt(BigInt(scaledIntString), scale);
11287
+ out[field] = desc.mode === "fixed" ? decimal : { amount: decimal, currency };
11288
+ if (format) {
11289
+ out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale);
11290
+ out[`${field}Number`] = Number(decimal);
11291
+ }
11292
+ }
11293
+ return out;
11294
+ }
11295
+
11296
+ // src/computed/index.ts
11297
+ init_errors();
11298
+ var ComputedFieldError = class extends NoydbError {
11299
+ constructor(field, id, cause) {
11300
+ super(
11301
+ "COMPUTED_FIELD",
11302
+ `computed field "${field}" threw for record "${id}": ` + (cause instanceof Error ? cause.message : String(cause))
11303
+ );
11304
+ this.field = field;
11305
+ this.id = id;
11306
+ this.cause = cause;
11307
+ this.name = "ComputedFieldError";
11308
+ }
11309
+ field;
11310
+ id;
11311
+ cause;
11312
+ };
11313
+ function evalComputedFields(record, computed, id) {
11314
+ const out = { ...record };
11315
+ for (const [field, fn] of Object.entries(computed)) {
11316
+ try {
11317
+ out[field] = fn(out);
11318
+ } catch (cause) {
11319
+ throw new ComputedFieldError(field, id, cause);
11320
+ }
11321
+ }
11322
+ return out;
11323
+ }
11324
+
10037
11325
  // src/i18n/strategy.ts
10038
11326
  function notEnabled(op) {
10039
11327
  return new Error(
@@ -10352,6 +11640,7 @@ var NO_AGGREGATE = {
10352
11640
  };
10353
11641
 
10354
11642
  // src/query/builder.ts
11643
+ init_money_reducer();
10355
11644
  var EMPTY_PLAN = {
10356
11645
  clauses: [],
10357
11646
  orderBy: [],
@@ -10797,6 +12086,10 @@ var Query = class _Query {
10797
12086
  * partition boundaries without an API break.
10798
12087
  */
10799
12088
  aggregate(spec) {
12089
+ const moneyFields = this.source.moneyFields;
12090
+ if (moneyFields) {
12091
+ spec = wrapMoneyReducers(spec, moneyFields);
12092
+ }
10800
12093
  const source = this.source;
10801
12094
  const clauses = this.plan.clauses;
10802
12095
  const joinCtx = this.joinContext;
@@ -10844,13 +12137,15 @@ var Query = class _Query {
10844
12137
  executeRecords,
10845
12138
  field,
10846
12139
  upstreams,
10847
- dictLabelResolver
12140
+ dictLabelResolver,
12141
+ this.source.moneyFields
10848
12142
  );
10849
12143
  }
10850
12144
  return this.aggregateStrategy.groupByN(
10851
12145
  executeRecords,
10852
12146
  fields,
10853
- upstreams
12147
+ upstreams,
12148
+ this.source.moneyFields
10854
12149
  );
10855
12150
  }
10856
12151
  /**
@@ -11391,7 +12686,8 @@ function count(opts) {
11391
12686
  init: () => 0,
11392
12687
  step: (state) => state + 1,
11393
12688
  remove: (state) => state - 1,
11394
- finalize: (state) => state
12689
+ finalize: (state) => state,
12690
+ merge: (a, b) => a + b
11395
12691
  };
11396
12692
  }
11397
12693
  function sum(field, opts) {
@@ -11400,10 +12696,15 @@ function sum(field, opts) {
11400
12696
  return {
11401
12697
  op: "sum",
11402
12698
  field,
12699
+ // Money-only metadata, read by `wrapMoneyReducers`. No effect on a
12700
+ // generic numeric sum.
12701
+ ...opts?.convertTo !== void 0 ? { convertTo: opts.convertTo } : {},
12702
+ ...opts?.fx !== void 0 ? { fx: opts.fx } : {},
11403
12703
  init: () => 0,
11404
12704
  step: (state, record) => state + readNumber(record, field),
11405
12705
  remove: (state, record) => state - readNumber(record, field),
11406
- finalize: (state) => state
12706
+ finalize: (state) => state,
12707
+ merge: (a, b) => a + b
11407
12708
  };
11408
12709
  }
11409
12710
  function avg(field, opts) {
@@ -11421,7 +12722,8 @@ function avg(field, opts) {
11421
12722
  sum: state.sum - readNumber(record, field),
11422
12723
  count: state.count - 1
11423
12724
  }),
11424
- finalize: (state) => state.count === 0 ? null : state.sum / state.count
12725
+ finalize: (state) => state.count === 0 ? null : state.sum / state.count,
12726
+ merge: (a, b) => ({ sum: a.sum + b.sum, count: a.count + b.count })
11425
12727
  };
11426
12728
  }
11427
12729
  function pushValue(state, value) {
@@ -11451,7 +12753,8 @@ function min(field, opts) {
11451
12753
  if (v < out) out = v;
11452
12754
  }
11453
12755
  return out;
11454
- }
12756
+ },
12757
+ merge: (a, b) => ({ values: [...a.values, ...b.values] })
11455
12758
  };
11456
12759
  }
11457
12760
  function max(field, opts) {
@@ -11471,11 +12774,17 @@ function max(field, opts) {
11471
12774
  if (v > out) out = v;
11472
12775
  }
11473
12776
  return out;
11474
- }
12777
+ },
12778
+ merge: (a, b) => ({ values: [...a.values, ...b.values] })
11475
12779
  };
11476
12780
  }
11477
12781
  function readNumber(record, field) {
11478
12782
  const value = readPath(record, field);
12783
+ if (typeof value === "object" && value !== null && "amount" in value && "currency" in value) {
12784
+ throw new Error(
12785
+ `aggregate: field "${field}" holds a money value but was not money-aware \u2014 declare it in the collection's moneyFields so sum/min/max stay exact`
12786
+ );
12787
+ }
11479
12788
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
11480
12789
  }
11481
12790
 
@@ -11889,8 +13198,8 @@ function coerceRefKey2(value) {
11889
13198
 
11890
13199
  // src/indexing/persisted-indexes.ts
11891
13200
  var IDX_PREFIX = "_idx/";
11892
- function encodeIdxId(field, recordId2) {
11893
- return `${IDX_PREFIX}${field}/${recordId2}`;
13201
+ function encodeIdxId(field, recordId3) {
13202
+ return `${IDX_PREFIX}${field}/${recordId3}`;
11894
13203
  }
11895
13204
  function decodeIdxId(id) {
11896
13205
  if (!id.startsWith(IDX_PREFIX)) return null;
@@ -11898,9 +13207,9 @@ function decodeIdxId(id) {
11898
13207
  const firstSlash = rest.indexOf("/");
11899
13208
  if (firstSlash <= 0) return null;
11900
13209
  const field = rest.slice(0, firstSlash);
11901
- const recordId2 = rest.slice(firstSlash + 1);
11902
- if (recordId2.length === 0) return null;
11903
- return { field, recordId: recordId2 };
13210
+ const recordId3 = rest.slice(firstSlash + 1);
13211
+ if (recordId3.length === 0) return null;
13212
+ return { field, recordId: recordId3 };
11904
13213
  }
11905
13214
 
11906
13215
  // src/indexing/lazy-builder.ts
@@ -12886,6 +14195,18 @@ var Collection = class {
12886
14195
  * fields when a locale is requested.
12887
14196
  */
12888
14197
  dictKeyFields;
14198
+ /**
14199
+ * Money field descriptors keyed by field path. Declared via the
14200
+ * `moneyFields` collection option: `put()` quantizes to a scaled-int
14201
+ * string, `get()`/`list()` decode back. Mutable so {@link _applyMoneyFields}
14202
+ * can attach descriptors to a collection MV-analysis pre-created.
14203
+ */
14204
+ moneyFields;
14205
+ /**
14206
+ * Computed scalar fields, evaluated first on every `put()`. Mutable for
14207
+ * the same MV-pre-creation reconcile as {@link moneyFields}.
14208
+ */
14209
+ computed;
12889
14210
  /**
12890
14211
  * Async callback provided by the Vault that resolves a dict key
12891
14212
  * to its label for a given locale. Used by the locale-read path for
@@ -13027,6 +14348,8 @@ var Collection = class {
13027
14348
  this.joinResolver = opts.joinResolver;
13028
14349
  this.i18nFields = opts.i18nFields;
13029
14350
  this.dictKeyFields = opts.dictKeyFields;
14351
+ this.moneyFields = opts.moneyFields;
14352
+ this.computed = opts.computed;
13030
14353
  this.dictLabelResolver = opts.dictLabelResolver;
13031
14354
  this.i18nPutValidator = opts.i18nPutValidator;
13032
14355
  this.autoTranslateHook = opts.autoTranslateHook;
@@ -13151,6 +14474,19 @@ var Collection = class {
13151
14474
  getSchema() {
13152
14475
  return this.schema;
13153
14476
  }
14477
+ /**
14478
+ * @internal — attach money descriptors post-construction. MV dependency
14479
+ * analysis auto-creates a source collection (without options) during
14480
+ * `openVault`, before the user's `collection(name, { moneyFields })`
14481
+ * declaration; this reconciles that ordering. First-wins. Not public.
14482
+ */
14483
+ _applyMoneyFields(moneyFields) {
14484
+ if (this.moneyFields === void 0) this.moneyFields = moneyFields;
14485
+ }
14486
+ /** @internal — attach computed fields post-construction. See {@link _applyMoneyFields}. */
14487
+ _applyComputed(computed) {
14488
+ if (this.computed === void 0) this.computed = computed;
14489
+ }
13154
14490
  /**
13155
14491
  * Get a single record by ID.
13156
14492
  *
@@ -13322,7 +14658,7 @@ var Collection = class {
13322
14658
  existingRecord = null;
13323
14659
  }
13324
14660
  }
13325
- await this.subsystemBus.dispatchGate("beforePut", {
14661
+ const gateEvent = {
13326
14662
  op: existingEnv ? "update" : "create",
13327
14663
  vault: this.vault,
13328
14664
  collection: this.name,
@@ -13332,12 +14668,20 @@ var Collection = class {
13332
14668
  existingVersion: existingEnv?._v ?? 0,
13333
14669
  existingTs: existingEnv?._ts,
13334
14670
  userId: this.keyring.userId,
13335
- role: this.keyring.role
13336
- });
14671
+ role: this.keyring.role,
14672
+ ...this.computed !== void 0 ? { computedFieldNames: new Set(Object.keys(this.computed)) } : {}
14673
+ };
14674
+ await this.subsystemBus.dispatchGate("beforePut", gateEvent);
14675
+ }
14676
+ if (this.computed !== void 0) {
14677
+ record = evalComputedFields(record, this.computed, id);
13337
14678
  }
13338
14679
  if (this.schema !== void 0) {
13339
14680
  record = await validateSchemaInput(this.schema, record, `put(${id})`);
13340
14681
  }
14682
+ if (this.moneyFields) {
14683
+ record = quantizeMoneyFields(record, this.moneyFields);
14684
+ }
13341
14685
  if (this.i18nFields) {
13342
14686
  const obj = record;
13343
14687
  for (const [field, descriptor] of Object.entries(this.i18nFields)) {
@@ -14159,7 +15503,8 @@ var Collection = class {
14159
15503
  // fields. The Query builder consults these when present and falls
14160
15504
  // back to a linear scan otherwise.
14161
15505
  getIndexes: () => this.getIndexes(),
14162
- lookupById: (id) => this.cache.get(id)?.record
15506
+ lookupById: (id) => this.cache.get(id)?.record,
15507
+ ...this.moneyFields ? { moneyFields: this.moneyFields } : {}
14163
15508
  };
14164
15509
  const resolver = this.joinResolver;
14165
15510
  const leftCollection = this.name;
@@ -14661,11 +16006,11 @@ var Collection = class {
14661
16006
  }
14662
16007
  }
14663
16008
  persisted.clear();
14664
- for (const recordId2 of canonicalIds) {
14665
- const envelope = await this.adapter.get(this.vault, this.name, recordId2);
16009
+ for (const recordId3 of canonicalIds) {
16010
+ const envelope = await this.adapter.get(this.vault, this.name, recordId3);
14666
16011
  if (!envelope) continue;
14667
16012
  const record = await this.decryptRecord(envelope, { skipValidation: true });
14668
- await this.maintainPersistedIndexesOnPut(recordId2, record, null, envelope._v);
16013
+ await this.maintainPersistedIndexesOnPut(recordId3, record, null, envelope._v);
14669
16014
  }
14670
16015
  this.persistedIndexesLoaded = true;
14671
16016
  }
@@ -14841,10 +16186,14 @@ var Collection = class {
14841
16186
  async applyLocaleToRecord(record, localeOpts) {
14842
16187
  const hasI18n = this.i18nFields && Object.keys(this.i18nFields).length > 0;
14843
16188
  const hasDict = this.dictKeyFields && Object.keys(this.dictKeyFields).length > 0;
14844
- if (!hasI18n && !hasDict) return record;
16189
+ const hasMoney = this.moneyFields && Object.keys(this.moneyFields).length > 0;
16190
+ if (!hasI18n && !hasDict && !hasMoney) return record;
14845
16191
  const locale = localeOpts?.locale ?? this.defaultLocale;
14846
- if (!locale) return record;
14847
16192
  let result = record;
16193
+ if (hasMoney && this.moneyFields) {
16194
+ result = decodeMoneyFields(result, this.moneyFields, typeof locale === "string" ? locale : void 0);
16195
+ }
16196
+ if (!locale) return result;
14848
16197
  if (hasI18n && this.i18nFields) {
14849
16198
  result = this.i18nStrategy.applyI18nLocale(result, this.i18nFields, locale, localeOpts?.fallback);
14850
16199
  }
@@ -16243,6 +17592,7 @@ async function runCompaction(ctx, options = {}) {
16243
17592
  let evicted = 0;
16244
17593
  let records = 0;
16245
17594
  let auditEntries = 0;
17595
+ let held = 0;
16246
17596
  let collectionsWithPolicy = 0;
16247
17597
  outer: for (const collectionName of allCollections) {
16248
17598
  if (collectionName.startsWith("_")) continue;
@@ -16253,25 +17603,29 @@ async function runCompaction(ctx, options = {}) {
16253
17603
  collectionsWithPolicy += 1;
16254
17604
  byCollection[collectionName] = { records: 0, evicted: 0 };
16255
17605
  const ids = await ctx.listRecords(collectionName);
16256
- for (const recordId2 of ids) {
17606
+ for (const recordId3 of ids) {
16257
17607
  if (evicted >= maxEvictions) break outer;
16258
- const record = await ctx.getRecord(collectionName, recordId2).catch(() => null);
17608
+ const record = await ctx.getRecord(collectionName, recordId3).catch(() => null);
16259
17609
  if (record === null) continue;
16260
17610
  records += 1;
16261
17611
  byCollection[collectionName].records += 1;
16262
- const slots = await ctx.listSlots(collectionName, recordId2).catch(() => []);
17612
+ const slots = await ctx.listSlots(collectionName, recordId3).catch(() => []);
16263
17613
  for (const slot of slots) {
16264
17614
  if (evicted >= maxEvictions) break outer;
16265
17615
  const policy = config[slot.name];
16266
17616
  if (!policy) continue;
16267
17617
  const reason = evaluatePolicy(policy, record, slot, now);
16268
17618
  if (!reason) continue;
17619
+ if (isHeld2(policy, record, now)) {
17620
+ held += 1;
17621
+ continue;
17622
+ }
16269
17623
  if (!dryRun) {
16270
- await ctx.deleteSlot(collectionName, recordId2, slot.name);
17624
+ await ctx.deleteSlot(collectionName, recordId3, slot.name);
16271
17625
  await writeAuditEntry(ctx, {
16272
- id: generateEvictionId(collectionName, recordId2, slot.name),
17626
+ id: generateEvictionId(collectionName, recordId3, slot.name),
16273
17627
  collection: collectionName,
16274
- recordId: recordId2,
17628
+ recordId: recordId3,
16275
17629
  slotName: slot.name,
16276
17630
  blobHash: slot.eTag,
16277
17631
  reason,
@@ -16290,9 +17644,32 @@ async function runCompaction(ctx, options = {}) {
16290
17644
  records,
16291
17645
  collections: collectionsWithPolicy,
16292
17646
  auditEntries,
17647
+ held,
16293
17648
  byCollection
16294
17649
  };
16295
17650
  }
17651
+ function isHeld2(policy, record, now) {
17652
+ if (policy.legalHold) {
17653
+ try {
17654
+ if (policy.legalHold(record)) return true;
17655
+ } catch {
17656
+ return true;
17657
+ }
17658
+ }
17659
+ if (policy.retainUntil) {
17660
+ try {
17661
+ const until = policy.retainUntil(record);
17662
+ if (until !== null && until !== void 0) {
17663
+ const t = until instanceof Date ? until.getTime() : typeof until === "number" ? until : Date.parse(String(until));
17664
+ if (!Number.isFinite(t)) return true;
17665
+ if (t > now.getTime()) return true;
17666
+ }
17667
+ } catch {
17668
+ return true;
17669
+ }
17670
+ }
17671
+ return false;
17672
+ }
16296
17673
  function evaluatePolicy(policy, record, slot, now) {
16297
17674
  let ttlTriggered = false;
16298
17675
  let predicateTriggered = false;
@@ -16315,11 +17692,11 @@ function evaluatePolicy(policy, record, slot, now) {
16315
17692
  if (predicateTriggered) return "predicate";
16316
17693
  return null;
16317
17694
  }
16318
- function generateEvictionId(collection, recordId2, slotName) {
17695
+ function generateEvictionId(collection, recordId3, slotName) {
16319
17696
  const rand = globalThis.crypto.getRandomValues(new Uint8Array(8));
16320
17697
  let suffix = "";
16321
17698
  for (const b of rand) suffix += b.toString(16).padStart(2, "0");
16322
- return `${collection}__${recordId2}__${slotName}__${suffix}`;
17699
+ return `${collection}__${recordId3}__${slotName}__${suffix}`;
16323
17700
  }
16324
17701
  async function writeAuditEntry(ctx, entry) {
16325
17702
  const json = JSON.stringify(entry);
@@ -16370,7 +17747,7 @@ async function deriveMagicLinkContentKey(serverSecret, token, vault) {
16370
17747
  ["encrypt", "decrypt"]
16371
17748
  );
16372
17749
  }
16373
- async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek, recordId2, opts) {
17750
+ async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek, recordId3, opts) {
16374
17751
  const collectionName = opts.collection ?? null;
16375
17752
  const sourceKey = collectionName ? dekKey(collectionName, opts.tier) : `__any#${opts.tier}`;
16376
17753
  const sourceDek = grantor.deks.get(sourceKey);
@@ -16383,7 +17760,7 @@ async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek,
16383
17760
  const until = typeof opts.until === "string" ? opts.until : opts.until.toISOString();
16384
17761
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
16385
17762
  const payload = {
16386
- id: recordId2,
17763
+ id: recordId3,
16387
17764
  toUser: opts.toUser,
16388
17765
  fromUser: grantor.userId,
16389
17766
  tier: opts.tier,
@@ -16403,11 +17780,11 @@ async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek,
16403
17780
  _data: data,
16404
17781
  _by: grantor.userId
16405
17782
  };
16406
- await store.put(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId2, envelope);
16407
- return { recordId: recordId2, payload };
17783
+ await store.put(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId3, envelope);
17784
+ return { recordId: recordId3, payload };
16408
17785
  }
16409
- async function readMagicLinkGrantRecord(store, vault, contentKey, recordId2) {
16410
- const env = await store.get(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId2);
17786
+ async function readMagicLinkGrantRecord(store, vault, contentKey, recordId3) {
17787
+ const env = await store.get(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId3);
16411
17788
  if (!env) return null;
16412
17789
  try {
16413
17790
  const json = await decrypt(env._iv, env._data, contentKey);
@@ -17009,6 +18386,10 @@ var Vault = class {
17009
18386
  * call throws with a pointer at `@noy-db/hub/blobs`.
17010
18387
  */
17011
18388
  blobStrategy;
18389
+ /** Cold-storage archival strategy (the archive target store). */
18390
+ archiveStrategy;
18391
+ /** Per-collection record archival policies. Indexed by collection name. */
18392
+ archiveRegistry = /* @__PURE__ */ new Map();
17012
18393
  indexStrategy;
17013
18394
  aggregateStrategy;
17014
18395
  crdtStrategy;
@@ -17112,6 +18493,8 @@ var Vault = class {
17112
18493
  * docstring.
17113
18494
  */
17114
18495
  ledgerStore = null;
18496
+ /** Lazily-built atomic-sequence store. See {@link sequence}. */
18497
+ sequenceStore = null;
17115
18498
  /**
17116
18499
  * Background writes for persisted-schema envelopes (#schema-dump v0
17117
18500
  * slice 1). One promise per `collection({ persistJsonSchema: true })`
@@ -17213,6 +18596,7 @@ var Vault = class {
17213
18596
  this.onRegisterConflictResolver = opts.onRegisterConflictResolver;
17214
18597
  this.syncAdapter = opts.syncAdapter;
17215
18598
  this.blobStrategy = opts.blobStrategy;
18599
+ this.archiveStrategy = opts.archiveStrategy;
17216
18600
  this.indexStrategy = opts.indexStrategy;
17217
18601
  this.aggregateStrategy = opts.aggregateStrategy;
17218
18602
  this.crdtStrategy = opts.crdtStrategy;
@@ -17275,8 +18659,9 @@ var Vault = class {
17275
18659
  *. `put()` validates keys against the declared set; reads
17276
18660
  * with `{ locale }` add `<field>Label` virtual fields.
17277
18661
  *
17278
- * Throws `ReservedCollectionNameError` for names starting with `_dict_`.
17279
- * Use `vault.dictionary(name)` to access dictionary collections.
18662
+ * Throws `ReservedCollectionNameError` for names starting with `_dict_` or
18663
+ * equal to `_sequences`. Use `vault.dictionary(name)` for dict collections
18664
+ * and `vault.sequence(name)` for sequence counters.
17280
18665
  *
17281
18666
  * Lazy mode + indexes is rejected at construction time — see the
17282
18667
  * Collection constructor for the rationale.
@@ -17295,7 +18680,16 @@ var Vault = class {
17295
18680
  if (isDictCollectionName(collectionName)) {
17296
18681
  throw new ReservedCollectionNameError(collectionName);
17297
18682
  }
18683
+ if (collectionName === SEQUENCE_COLLECTION) {
18684
+ throw new ReservedCollectionNameError(collectionName);
18685
+ }
17298
18686
  let coll = this.collectionCache.get(collectionName);
18687
+ if (coll && options?.moneyFields) {
18688
+ coll._applyMoneyFields(options.moneyFields);
18689
+ }
18690
+ if (coll && options?.computed) {
18691
+ coll._applyComputed(options.computed);
18692
+ }
17299
18693
  if (!coll) {
17300
18694
  if (options?.refs) {
17301
18695
  this.refRegistry.register(collectionName, options.refs);
@@ -17306,6 +18700,9 @@ var Vault = class {
17306
18700
  if (options?.blobFields) {
17307
18701
  this.blobFieldsRegistry.set(collectionName, options.blobFields);
17308
18702
  }
18703
+ if (options?.archive) {
18704
+ this.archiveRegistry.set(collectionName, options.archive);
18705
+ }
17309
18706
  if (options?.attestation !== void 0) {
17310
18707
  this.attestationRegistry.set(collectionName, options.attestation);
17311
18708
  }
@@ -17421,6 +18818,8 @@ var Vault = class {
17421
18818
  collOpts.onCrossTierAccess = (event) => this.emitCrossTier(event);
17422
18819
  if (this.syncAdapter !== void 0) collOpts.syncAdapter = this.syncAdapter;
17423
18820
  if (options?.i18nFields !== void 0) collOpts.i18nFields = options.i18nFields;
18821
+ if (options?.moneyFields !== void 0) collOpts.moneyFields = options.moneyFields;
18822
+ if (options?.computed !== void 0) collOpts.computed = options.computed;
17424
18823
  if (options?.dictKeyFields !== void 0) {
17425
18824
  collOpts.dictLabelResolver = async (dictName, key, locale, fallback) => {
17426
18825
  const handle = this.dictionary(dictName);
@@ -17840,6 +19239,33 @@ var Vault = class {
17840
19239
  * await vault.compact({ maxEvictions: 1000 }) // cap batch
17841
19240
  * ```
17842
19241
  */
19242
+ /**
19243
+ * Atomic, gap-free numbering. `vault.sequence('invoice-2026').next()`
19244
+ * returns 1, 2, 3, … with no gaps or duplicates under concurrency, via
19245
+ * an optimistic-CAS counter at `_sequences/<name>`. Each name is an
19246
+ * independent sequence.
19247
+ *
19248
+ * **Online-only:** `next()` throws `SequenceOfflineError` unless the
19249
+ * store advertises `capabilities.casAtomic` — gap-free numbering cannot
19250
+ * be serialized by an offline / non-CAS writer.
19251
+ *
19252
+ * ```ts
19253
+ * const n = await vault.sequence('invoice-2026').next() // 1, then 2, …
19254
+ * const cur = await vault.sequence('invoice-2026').peek() // current value, no allocation
19255
+ * ```
19256
+ */
19257
+ sequence(name) {
19258
+ if (!this.sequenceStore) {
19259
+ this.sequenceStore = new SequenceStore({
19260
+ adapter: this.adapter,
19261
+ vault: this.name,
19262
+ encrypted: this.encrypted,
19263
+ getDEK: this.getDEK,
19264
+ actor: this.keyring.userId
19265
+ });
19266
+ }
19267
+ return this.sequenceStore.handle(name);
19268
+ }
17843
19269
  async compact(options = {}) {
17844
19270
  return runCompaction({
17845
19271
  adapter: this.adapter,
@@ -17864,6 +19290,48 @@ var Vault = class {
17864
19290
  }
17865
19291
  }, options);
17866
19292
  }
19293
+ /**
19294
+ * Sweep records eligible by their collection's `archive` policy into the
19295
+ * cold archive store. Relocation is envelope-level (no re-encryption) and
19296
+ * bypasses guards + materialized-view dispatch, so issued/immutable
19297
+ * records over a sealed period can be archived without recomputing
19298
+ * finalized aggregates. A `legalHold` predicate blocks archival.
19299
+ * Requires `archiveStrategy: withArchive({ store })` in `createNoydb`.
19300
+ */
19301
+ async archive(options = {}) {
19302
+ return runArchive(this._archiveContext(), options);
19303
+ }
19304
+ /** Relocate one archived record back to the primary store. Returns false if it was not archived. */
19305
+ async restore(collection, id) {
19306
+ return runRestore(this._archiveContext(), collection, id);
19307
+ }
19308
+ /** List archived record ids for a collection (or all collections with an archive policy). */
19309
+ async listArchived(collection) {
19310
+ return runListArchived(this._archiveContext(), collection);
19311
+ }
19312
+ _archiveContext() {
19313
+ const strategy = this.archiveStrategy;
19314
+ if (!strategy) {
19315
+ throw new Error(
19316
+ "vault.archive/restore/listArchived require `archiveStrategy: withArchive({ store })` in createNoydb"
19317
+ );
19318
+ }
19319
+ const archiveStore = strategy.store;
19320
+ return {
19321
+ vaultId: this.name,
19322
+ archiveStore,
19323
+ collectionsWithPolicy: () => [...this.archiveRegistry.keys()],
19324
+ getPolicy: (c) => this.archiveRegistry.get(c) ?? null,
19325
+ listRecordIds: (c) => this.adapter.list(this.name, c),
19326
+ getRecord: async (c, id) => await this.collection(c).get(id, { locale: "raw" }),
19327
+ getEnvelope: (c, id) => this.adapter.get(this.name, c, id),
19328
+ removeFromPrimary: (c, id) => this.collection(c)._internalDelete(id),
19329
+ restoreToPrimary: async (c, id, env) => {
19330
+ await this.adapter.put(this.name, c, id, env);
19331
+ await this.collection(c)._invalidateCacheEntry(id);
19332
+ }
19333
+ };
19334
+ }
17867
19335
  exportBlobs(options = {}) {
17868
19336
  this.assertCanExport("plaintext", "blob");
17869
19337
  return createExportBlobsHandle(
@@ -18562,7 +20030,7 @@ var Vault = class {
18562
20030
  *
18563
20031
  * @internal
18564
20032
  */
18565
- async _logConsent(op, collection, recordId2) {
20033
+ async _logConsent(op, collection, recordId3) {
18566
20034
  const ctx = this.consentContext;
18567
20035
  if (!ctx) return;
18568
20036
  await this.consentStrategy.write(
@@ -18575,7 +20043,7 @@ var Vault = class {
18575
20043
  consentHash: ctx.consentHash,
18576
20044
  op,
18577
20045
  collection,
18578
- recordId: recordId2
20046
+ recordId: recordId3
18579
20047
  },
18580
20048
  this.getDEK
18581
20049
  );
@@ -18758,14 +20226,14 @@ var Vault = class {
18758
20226
  * the HKDF derivation, record-id composition, and batch logic so the
18759
20227
  * grantor doesn't touch this method directly.
18760
20228
  */
18761
- async writeMagicLinkGrant(contentKey, grantKek, recordId2, opts) {
20229
+ async writeMagicLinkGrant(contentKey, grantKek, recordId3, opts) {
18762
20230
  return writeMagicLinkGrant(
18763
20231
  this.adapter,
18764
20232
  this.name,
18765
20233
  this.keyring,
18766
20234
  contentKey,
18767
20235
  grantKek,
18768
- recordId2,
20236
+ recordId3,
18769
20237
  opts
18770
20238
  );
18771
20239
  }
@@ -19125,7 +20593,7 @@ var Vault = class {
19125
20593
  }
19126
20594
  }
19127
20595
  const internalSnapshot = {};
19128
- for (const internalName of [LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, SCHEMAS_COLLECTION]) {
20596
+ for (const internalName of [LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, SCHEMAS_COLLECTION, SEQUENCE_COLLECTION]) {
19129
20597
  const ids = await this.adapter.list(this.name, internalName);
19130
20598
  if (ids.length === 0) continue;
19131
20599
  const records = {};
@@ -20601,6 +22069,7 @@ var Noydb = class {
20601
22069
  writeRelay;
20602
22070
  /** Per-vault policy enforcers. */
20603
22071
  policyEnforcers = /* @__PURE__ */ new Map();
22072
+ vaultTemplates = /* @__PURE__ */ new Map();
20604
22073
  txStrategy;
20605
22074
  sessionStrategy;
20606
22075
  syncStrategy;
@@ -20670,7 +22139,7 @@ var Noydb = class {
20670
22139
  await registry.runChecks(e.collection, incoming, ctx);
20671
22140
  const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor(), executor_exports));
20672
22141
  for (const g of guards) {
20673
- await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming);
22142
+ await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming, e.computedFieldNames);
20674
22143
  }
20675
22144
  });
20676
22145
  this.subsystemBus.registerGate("beforeDelete", async (e) => {
@@ -20849,6 +22318,7 @@ var Noydb = class {
20849
22318
  syncAdapter: targets.length > 0 ? targets[0].store : void 0,
20850
22319
  historyConfig: this.options.history,
20851
22320
  ...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
22321
+ ...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
20852
22322
  ...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
20853
22323
  ...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
20854
22324
  ...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
@@ -20902,6 +22372,7 @@ var Noydb = class {
20902
22372
  emitter: this.emitter,
20903
22373
  historyConfig: this.options.history,
20904
22374
  ...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
22375
+ ...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
20905
22376
  ...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
20906
22377
  ...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
20907
22378
  ...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
@@ -20930,6 +22401,7 @@ var Noydb = class {
20930
22401
  encrypted: true,
20931
22402
  historyConfig: this.options.history,
20932
22403
  ...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
22404
+ ...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
20933
22405
  ...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
20934
22406
  ...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
20935
22407
  ...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
@@ -21244,6 +22716,32 @@ var Noydb = class {
21244
22716
  }
21245
22717
  return results;
21246
22718
  }
22719
+ /**
22720
+ * Register a shard schema blueprint. `createShard` / `openVaultGroup`
22721
+ * stamp shards from the named template. See the MVF design spec.
22722
+ */
22723
+ withVaultTemplate(name, template) {
22724
+ this.vaultTemplates.set(name, template);
22725
+ }
22726
+ /**
22727
+ * Open a VaultGroup — transparent routing over per-partition shard
22728
+ * vaults, with shard discovery backed by the supplied `vault-registry`
22729
+ * collection.
22730
+ */
22731
+ async openVaultGroup(name, opts) {
22732
+ if (this.closed) throw new ValidationError("Instance is closed");
22733
+ const template = this.vaultTemplates.get(opts.sharding.vaultTemplate);
22734
+ if (!template) throw new VaultTemplateNotFoundError(opts.sharding.vaultTemplate);
22735
+ const { VaultGroup: VaultGroup2 } = await Promise.resolve().then(() => (init_vault_group(), vault_group_exports));
22736
+ return new VaultGroup2(this, name, opts.registry, opts.sharding, template);
22737
+ }
22738
+ /**
22739
+ * @internal — true when an encrypted shard vault is provisioned
22740
+ * (its keyring exists in the store).
22741
+ */
22742
+ async _shardVaultProvisioned(vaultId) {
22743
+ return (await this.options.store.list(vaultId, "_keyring")).length > 0;
22744
+ }
21247
22745
  /**
21248
22746
  * Change the current user's passphrase for a vault.
21249
22747
  *
@@ -23506,6 +25004,50 @@ function withGuard(strategy) {
23506
25004
  };
23507
25005
  }
23508
25006
 
25007
+ // src/guards/immutable-guard.ts
25008
+ init_errors();
25009
+ function recordId2(record) {
25010
+ const id = record?.id;
25011
+ return typeof id === "string" ? id : "";
25012
+ }
25013
+ function immutableGuard(config) {
25014
+ const { collection, after, appendOnly, amendmentRoles } = config;
25015
+ if (appendOnly && after !== void 0) {
25016
+ throw new ValidationError("immutableGuard: `after` and `appendOnly` are mutually exclusive");
25017
+ }
25018
+ if (!appendOnly && after === void 0) {
25019
+ throw new ValidationError("immutableGuard: provide `after` or `appendOnly: true`");
25020
+ }
25021
+ const isImmutable = appendOnly ? () => true : after;
25022
+ const reason = appendOnly ? "append-only collection" : "record is immutable after issue";
25023
+ const spec = {
25024
+ collection,
25025
+ // Block updates to an already-immutable record. Inserts (existing
25026
+ // null) and the transition write that first makes the record
25027
+ // immutable are allowed — `after` reads the prior state.
25028
+ check: (incoming, ctx) => {
25029
+ if (ctx.existing !== null && isImmutable(ctx.existing)) {
25030
+ throw new RecordLockedError(collection, recordId2(incoming), reason);
25031
+ }
25032
+ },
25033
+ // Block deletes of an immutable record.
25034
+ onDelete: (existing) => {
25035
+ if (isImmutable(existing)) {
25036
+ throw new RecordLockedError(collection, recordId2(existing), reason);
25037
+ }
25038
+ },
25039
+ // The authorized override: inside an amendment transaction the
25040
+ // check/onDelete are skipped and the change is ledgered. No extra
25041
+ // invariant — the amendment itself is the sanctioned exception.
25042
+ amendment: {
25043
+ roles: amendmentRoles ?? ["admin", "owner"],
25044
+ invariant: () => {
25045
+ }
25046
+ }
25047
+ };
25048
+ return withGuard(spec);
25049
+ }
25050
+
23509
25051
  // src/derivations/with-derivation.ts
23510
25052
  init_errors();
23511
25053
  function withDerivation(spec) {
@@ -23663,6 +25205,10 @@ function withOverlayedView(spec) {
23663
25205
  init_errors();
23664
25206
  init_errors();
23665
25207
 
25208
+ // src/money/index.ts
25209
+ init_descriptor();
25210
+ init_iso4217();
25211
+
23666
25212
  // src/i18n/script.ts
23667
25213
  init_errors();
23668
25214
  var LATIN_BASE = /* @__PURE__ */ new Set([
@@ -24448,6 +25994,7 @@ function shortJSON(value) {
24448
25994
  CollectionFrame,
24449
25995
  CollectionIndexes,
24450
25996
  CollectionInstant,
25997
+ ComputedFieldError,
24451
25998
  ConflictError,
24452
25999
  CrossJoinSourceUnknownError,
24453
26000
  CrossJoinTooLargeError,
@@ -24511,6 +26058,9 @@ function shortJSON(value) {
24511
26058
  MemorySealingKeyProvider,
24512
26059
  MigrationRequiredError,
24513
26060
  MissingTranslationError,
26061
+ MoneyCurrencyError,
26062
+ MoneyPrecisionError,
26063
+ MoneyUnsupportedError,
24514
26064
  NOYDB_BACKUP_VERSION,
24515
26065
  NOYDB_BUNDLE_FORMAT_VERSION,
24516
26066
  NOYDB_BUNDLE_MAGIC,
@@ -24563,9 +26113,13 @@ function shortJSON(value) {
24563
26113
  SchemaUpdateError,
24564
26114
  SchemaValidationError,
24565
26115
  ScriptViolationError,
26116
+ SequenceContentionError,
26117
+ SequenceOfflineError,
26118
+ SequenceStore,
24566
26119
  SessionExpiredError,
24567
26120
  SessionNotFoundError,
24568
26121
  SessionPolicyError,
26122
+ ShardProvisioningError,
24569
26123
  SnapshotNotFoundError,
24570
26124
  StoreCapabilityError,
24571
26125
  SyncEngine,
@@ -24581,6 +26135,7 @@ function shortJSON(value) {
24581
26135
  USER_ENVELOPE_COLLECTION,
24582
26136
  USER_ENVELOPE_MAX_BYTES,
24583
26137
  UniqueConstraintError,
26138
+ UnknownShardError,
24584
26139
  UnsupportedIndexOptionError,
24585
26140
  UserApi,
24586
26141
  UserEnvelopeOversizedError,
@@ -24589,6 +26144,7 @@ function shortJSON(value) {
24589
26144
  Vault,
24590
26145
  VaultFrame,
24591
26146
  VaultInstant,
26147
+ VaultTemplateNotFoundError,
24592
26148
  WeakPassphraseError,
24593
26149
  activeSessionCount,
24594
26150
  additiveOnly,
@@ -24645,6 +26201,7 @@ function shortJSON(value) {
24645
26201
  envelopePayloadHash,
24646
26202
  estimateEntropy,
24647
26203
  estimateRecordBytes,
26204
+ evalComputedFields,
24648
26205
  evaluateClause,
24649
26206
  evaluateExportCapability,
24650
26207
  evaluateFieldClause,
@@ -24661,6 +26218,7 @@ function shortJSON(value) {
24661
26218
  hasRecoveryEnrolled,
24662
26219
  hashEntry,
24663
26220
  i18nText,
26221
+ immutableGuard,
24664
26222
  inferScripts,
24665
26223
  isDevUnlockActive,
24666
26224
  isDictCollectionName,
@@ -24668,6 +26226,7 @@ function shortJSON(value) {
24668
26226
  isDiscriminant,
24669
26227
  isI18nTextDescriptor,
24670
26228
  isMagicLinkGrantExpired,
26229
+ isMoneyDescriptor,
24671
26230
  isPreCompressed,
24672
26231
  isPublicEnvelope,
24673
26232
  isSessionAlive,
@@ -24699,6 +26258,7 @@ function shortJSON(value) {
24699
26258
  mintPaperRecoveryEntry,
24700
26259
  mintShamirRecoveryEntry,
24701
26260
  mintWrappedDeksBlob,
26261
+ money,
24702
26262
  paddedIndex,
24703
26263
  parseBytes,
24704
26264
  parseIndex,
@@ -24739,6 +26299,7 @@ function shortJSON(value) {
24739
26299
  saveShamirRecoveryEntries,
24740
26300
  saveUserEnvelope,
24741
26301
  saveVaultPolicy,
26302
+ scaleForCurrency,
24742
26303
  sha256Hex,
24743
26304
  sum,
24744
26305
  unwrapDeksFromBlob,
@@ -24752,6 +26313,7 @@ function shortJSON(value) {
24752
26313
  validateSchemaOutput,
24753
26314
  validateSessionPolicy,
24754
26315
  visibilityRecordId,
26316
+ withArchive,
24755
26317
  withCache,
24756
26318
  withCircuitBreaker,
24757
26319
  withDerivation,