@noy-db/hub 0.2.0-pre.10 → 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 (266) 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 +1468 -19
  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-DRXIZOFV.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-5IXJGFF2.js → chunk-7JJE3OMJ.js} +5 -5
  28. package/dist/{chunk-HHOO7HGH.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-PXTQPZO4.js → chunk-DLTU4M2I.js} +6 -6
  37. package/dist/{chunk-GAUEWM7D.js → chunk-EKNUBIIQ.js} +4 -4
  38. package/dist/{chunk-HQSQC2XL.js → chunk-GFPR7VJS.js} +17 -4
  39. package/dist/chunk-GFPR7VJS.js.map +1 -0
  40. package/dist/{chunk-6EOXTJS2.js → chunk-HBAJDI2N.js} +5 -5
  41. package/dist/{chunk-PVUUIWHY.js → chunk-HLGDYFWR.js} +10 -3
  42. package/dist/chunk-HLGDYFWR.js.map +1 -0
  43. package/dist/{chunk-RRNA5GKT.js → chunk-IEPT7HVP.js} +2 -2
  44. package/dist/{chunk-R233SLY3.js → chunk-IUBHXEPJ.js} +2 -2
  45. package/dist/{chunk-CH22FZHT.js → chunk-L6BYRCYB.js} +2 -2
  46. package/dist/{chunk-5OX6XVNS.js → chunk-LOA2VCMS.js} +5 -5
  47. package/dist/{chunk-BB27JMWB.js → chunk-LSEW3ZZ2.js} +3 -3
  48. package/dist/{chunk-Y26YV5R3.js → chunk-LWSD4QPT.js} +3 -3
  49. package/dist/{chunk-WIRRPTFH.js → chunk-LYNNZEQD.js} +1 -1
  50. package/dist/chunk-LYNNZEQD.js.map +1 -0
  51. package/dist/{chunk-26NK23DZ.js → chunk-M45IRXDM.js} +3 -3
  52. package/dist/{chunk-CXJG63MA.js → chunk-NP6EZT44.js} +20 -6
  53. package/dist/chunk-NP6EZT44.js.map +1 -0
  54. package/dist/{chunk-GNHAC43Q.js → chunk-O53RIZCC.js} +5 -5
  55. package/dist/chunk-OPDTLHFA.js +783 -0
  56. package/dist/chunk-OPDTLHFA.js.map +1 -0
  57. package/dist/{chunk-LSTBFLL2.js → chunk-P3Z5Y2TS.js} +2 -2
  58. package/dist/{chunk-QSOYKKMD.js → chunk-P4EDT5ZP.js} +2 -2
  59. package/dist/{chunk-PC6ZEDRL.js → chunk-RHQYVHFH.js} +2 -2
  60. package/dist/{chunk-3LPV6BXR.js → chunk-RRDWXNBQ.js} +3 -3
  61. package/dist/{chunk-4CLICFEY.js → chunk-SJJQKNMP.js} +4 -4
  62. package/dist/{chunk-TY32C732.js → chunk-SZ4N3IL5.js} +5 -5
  63. package/dist/{chunk-4USCAEDT.js → chunk-TMHJEYW7.js} +502 -60
  64. package/dist/chunk-TMHJEYW7.js.map +1 -0
  65. package/dist/{chunk-2N62W5YP.js → chunk-UA6G45ME.js} +3 -3
  66. package/dist/{chunk-6YLPHBKR.js → chunk-UOC7JMZO.js} +13 -4
  67. package/dist/chunk-UOC7JMZO.js.map +1 -0
  68. package/dist/{chunk-DAP2XL7Q.js → chunk-VOXMU6LB.js} +2 -2
  69. package/dist/chunk-WNRGOVLG.js +64 -0
  70. package/dist/chunk-WNRGOVLG.js.map +1 -0
  71. package/dist/{chunk-DJRWA3Q5.js → chunk-WUG3E423.js} +4 -4
  72. package/dist/{chunk-PM3QYWUU.js → chunk-XHM2SARW.js} +3 -3
  73. package/dist/{chunk-RC6SU5NO.js → chunk-XSIFXX54.js} +2 -2
  74. package/dist/{chunk-CXFOITNS.js → chunk-ZC7MNVYN.js} +2 -2
  75. package/dist/{chunk-6T2UDBKG.js → chunk-ZCFS7U4J.js} +2 -2
  76. package/dist/consent/index.cjs.map +1 -1
  77. package/dist/consent/index.d.cts +4 -4
  78. package/dist/consent/index.d.ts +4 -4
  79. package/dist/consent/index.js +3 -3
  80. package/dist/{crypto-2CRLG4F4.js → crypto-AJB72OKN.js} +3 -3
  81. package/dist/{delegation-ZTRT2PRV.js → delegation-6FCWDRUS.js} +5 -5
  82. package/dist/derivations/index.cjs.map +1 -1
  83. package/dist/derivations/index.d.cts +5 -5
  84. package/dist/derivations/index.d.ts +5 -5
  85. package/dist/derivations/index.js +4 -4
  86. package/dist/{dev-unlock-BOEYl1xl.d.ts → dev-unlock-D3mpVFRc.d.ts} +1 -1
  87. package/dist/{dev-unlock-AglVnkPY.d.cts → dev-unlock-ckqa_Nso.d.cts} +1 -1
  88. package/dist/executor-7KSCEIFA.js +8 -0
  89. package/dist/executor-D2QMNGRJ.js +8 -0
  90. package/dist/executor-O5AZK7UW.js +11 -0
  91. package/dist/{fanout-sidecar-OKPMMPLG.js → fanout-sidecar-ZSKEQ6NI.js} +2 -2
  92. package/dist/guards/index.cjs +53 -1
  93. package/dist/guards/index.cjs.map +1 -1
  94. package/dist/guards/index.d.cts +12 -6
  95. package/dist/guards/index.d.ts +12 -6
  96. package/dist/guards/index.js +5 -3
  97. package/dist/{hash-B9m3_fhj.d.ts → hash-CTZVkXLx.d.ts} +1 -1
  98. package/dist/{hash-RVqz2zi8.d.cts → hash-rDSSd_oW.d.cts} +1 -1
  99. package/dist/history/index.cjs.map +1 -1
  100. package/dist/history/index.d.cts +5 -5
  101. package/dist/history/index.d.ts +5 -5
  102. package/dist/history/index.js +5 -5
  103. package/dist/i18n/index.cjs.map +1 -1
  104. package/dist/i18n/index.d.cts +4 -4
  105. package/dist/i18n/index.d.ts +4 -4
  106. package/dist/i18n/index.js +6 -6
  107. package/dist/immutable-guard-C51vAHuh.d.cts +67 -0
  108. package/dist/immutable-guard-DyD0qg2k.d.ts +67 -0
  109. package/dist/index-CkFHr4OP.d.ts +1190 -0
  110. package/dist/index-Cmop06zJ.d.cts +1190 -0
  111. package/dist/index.cjs +1636 -61
  112. package/dist/index.cjs.map +1 -1
  113. package/dist/index.d.cts +46 -13
  114. package/dist/index.d.ts +46 -13
  115. package/dist/index.js +76 -44
  116. package/dist/index.js.map +1 -1
  117. package/dist/indexing/index.cjs.map +1 -1
  118. package/dist/indexing/index.js +2 -2
  119. package/dist/issue-YIYG4OW5.js +12 -0
  120. package/dist/{ledger-O7FXOG3D.js → ledger-5JMVF7PY.js} +5 -5
  121. package/dist/materialized-views/index.cjs.map +1 -1
  122. package/dist/materialized-views/index.d.cts +5 -6
  123. package/dist/materialized-views/index.d.ts +5 -6
  124. package/dist/materialized-views/index.js +6 -6
  125. package/dist/noydb-D5SLAJ6V.js +34 -0
  126. package/dist/overlay-views/index.cjs.map +1 -1
  127. package/dist/overlay-views/index.d.cts +5 -5
  128. package/dist/overlay-views/index.d.ts +5 -5
  129. package/dist/overlay-views/index.js +4 -4
  130. package/dist/periods/index.cjs.map +1 -1
  131. package/dist/periods/index.d.cts +4 -4
  132. package/dist/periods/index.d.ts +4 -4
  133. package/dist/periods/index.js +5 -5
  134. package/dist/{public-envelope-HMYHZIRH.js → public-envelope-PFLZI5MO.js} +4 -4
  135. package/dist/query/index.cjs +293 -10
  136. package/dist/query/index.cjs.map +1 -1
  137. package/dist/query/index.d.cts +2 -2
  138. package/dist/query/index.d.ts +2 -2
  139. package/dist/query/index.js +4 -4
  140. package/dist/registry-BVQ5ITMF.js +8 -0
  141. package/dist/registry-JLP3QOLD.js +8 -0
  142. package/dist/{registry-ST2VNFZC.js → registry-NCY445U5.js} +3 -3
  143. package/dist/{revoke-S6JMSLUN.js → revoke-7RLGQWZ7.js} +6 -6
  144. package/dist/session/index.cjs.map +1 -1
  145. package/dist/session/index.d.cts +5 -5
  146. package/dist/session/index.d.ts +5 -5
  147. package/dist/session/index.js +3 -3
  148. package/dist/shadow/index.cjs.map +1 -1
  149. package/dist/shadow/index.d.cts +4 -4
  150. package/dist/shadow/index.d.ts +4 -4
  151. package/dist/shadow/index.js +2 -2
  152. package/dist/{signer-7NPTB3SQ.js → signer-6JF44I4A.js} +5 -5
  153. package/dist/snapshots/index.cjs.map +1 -1
  154. package/dist/snapshots/index.d.cts +4 -4
  155. package/dist/snapshots/index.d.ts +4 -4
  156. package/dist/snapshots/index.js +4 -4
  157. package/dist/{stale-VKXSXJF4.js → stale-UBLP3RJ3.js} +2 -2
  158. package/dist/store/index.cjs.map +1 -1
  159. package/dist/store/index.d.cts +4 -4
  160. package/dist/store/index.d.ts +4 -4
  161. package/dist/store/index.js +2 -2
  162. package/dist/strategy-rtpKDfTC.d.cts +2029 -0
  163. package/dist/strategy-rtpKDfTC.d.ts +2029 -0
  164. package/dist/sync/index.cjs.map +1 -1
  165. package/dist/sync/index.d.cts +3 -3
  166. package/dist/sync/index.d.ts +3 -3
  167. package/dist/sync/index.js +4 -4
  168. package/dist/team/index.cjs.map +1 -1
  169. package/dist/team/index.d.cts +4 -4
  170. package/dist/team/index.d.ts +4 -4
  171. package/dist/team/index.js +8 -8
  172. package/dist/tx/index.cjs +8 -1
  173. package/dist/tx/index.cjs.map +1 -1
  174. package/dist/tx/index.d.cts +4 -4
  175. package/dist/tx/index.d.ts +4 -4
  176. package/dist/tx/index.js +3 -3
  177. package/dist/{types-n2_IfwlQ.d.cts → types-BGwjsDef.d.cts} +520 -6
  178. package/dist/{types-CaNQm4i8.d.ts → types-DRdfwgTG.d.ts} +520 -6
  179. package/dist/{ulid-CLMjmyhG.d.cts → ulid-D4d0Xto3.d.cts} +1 -1
  180. package/dist/{ulid-B9SMWj5i.d.ts → ulid-DOTPZ5_h.d.ts} +1 -1
  181. package/dist/util/index.cjs.map +1 -1
  182. package/dist/util/index.js +1 -1
  183. package/dist/vault-group-Z4KB75ZH.js +450 -0
  184. package/dist/vault-group-Z4KB75ZH.js.map +1 -0
  185. package/dist/{with-derivation-CVIOPTUf.d.ts → with-derivation-B082Y_WQ.d.ts} +1 -1
  186. package/dist/{with-derivation-aKrtS7Jj.d.cts → with-derivation-CB1EdcFF.d.cts} +1 -1
  187. package/dist/{with-materialized-view-C1eA1_T_.d.cts → with-materialized-view-CzRg1Dpr.d.cts} +1 -1
  188. package/dist/{with-materialized-view-DaYaE8-Q.d.ts → with-materialized-view-Dw4SwjKl.d.ts} +1 -1
  189. package/dist/{with-overlayed-view-DleJfKcV.d.cts → with-overlayed-view-C9YFKXzn.d.cts} +1 -1
  190. package/dist/{with-overlayed-view-DQsh2p8H.d.ts → with-overlayed-view-CaCXeW26.d.ts} +1 -1
  191. package/package.json +3 -3
  192. package/dist/chunk-2LPPNWF6.js +0 -340
  193. package/dist/chunk-2LPPNWF6.js.map +0 -1
  194. package/dist/chunk-4USCAEDT.js.map +0 -1
  195. package/dist/chunk-6YLPHBKR.js.map +0 -1
  196. package/dist/chunk-C3WE6UJY.js +0 -19
  197. package/dist/chunk-C3WE6UJY.js.map +0 -1
  198. package/dist/chunk-CXJG63MA.js.map +0 -1
  199. package/dist/chunk-DRXIZOFV.js.map +0 -1
  200. package/dist/chunk-HQSQC2XL.js.map +0 -1
  201. package/dist/chunk-O6EJ6WTI.js.map +0 -1
  202. package/dist/chunk-PVUUIWHY.js.map +0 -1
  203. package/dist/chunk-WIRRPTFH.js.map +0 -1
  204. package/dist/executor-S76VN45G.js +0 -8
  205. package/dist/executor-UCXLIGLW.js +0 -11
  206. package/dist/executor-ZCNZJMGR.js +0 -8
  207. package/dist/index-B8bjExET.d.cts +0 -2434
  208. package/dist/index-DfUbNad8.d.ts +0 -2434
  209. package/dist/issue-3W6IVLKH.js +0 -12
  210. package/dist/noydb-YAZNH5TI.js +0 -34
  211. package/dist/registry-UFIK7CSR.js +0 -8
  212. package/dist/registry-ZGYYSM5I.js +0 -8
  213. package/dist/strategy-CT2LCKAX.d.cts +0 -613
  214. package/dist/strategy-CT2LCKAX.d.ts +0 -613
  215. package/dist/with-guard-DZQbPzoP.d.cts +0 -18
  216. package/dist/with-guard-DseETUrF.d.ts +0 -18
  217. /package/dist/{chunk-7CEGU63S.js.map → chunk-4BHFNKTP.js.map} +0 -0
  218. /package/dist/{chunk-5OEJ6GOT.js.map → chunk-5ARRXIVR.js.map} +0 -0
  219. /package/dist/{chunk-YM7LFCG7.js.map → chunk-6BYBVRZU.js.map} +0 -0
  220. /package/dist/{chunk-5IXJGFF2.js.map → chunk-7JJE3OMJ.js.map} +0 -0
  221. /package/dist/{chunk-HHOO7HGH.js.map → chunk-7LVRIW4G.js.map} +0 -0
  222. /package/dist/{chunk-IMYKDWB4.js.map → chunk-B7GGYNKQ.js.map} +0 -0
  223. /package/dist/{chunk-BDV7INMP.js.map → chunk-BXOUVUES.js.map} +0 -0
  224. /package/dist/{chunk-FO3UEG4S.js.map → chunk-C2CIIQRG.js.map} +0 -0
  225. /package/dist/{chunk-ZROPXHJY.js.map → chunk-CHBXWJZQ.js.map} +0 -0
  226. /package/dist/{chunk-RYIL3PI2.js.map → chunk-CILT6V3V.js.map} +0 -0
  227. /package/dist/{chunk-PXTQPZO4.js.map → chunk-DLTU4M2I.js.map} +0 -0
  228. /package/dist/{chunk-GAUEWM7D.js.map → chunk-EKNUBIIQ.js.map} +0 -0
  229. /package/dist/{chunk-6EOXTJS2.js.map → chunk-HBAJDI2N.js.map} +0 -0
  230. /package/dist/{chunk-RRNA5GKT.js.map → chunk-IEPT7HVP.js.map} +0 -0
  231. /package/dist/{chunk-R233SLY3.js.map → chunk-IUBHXEPJ.js.map} +0 -0
  232. /package/dist/{chunk-CH22FZHT.js.map → chunk-L6BYRCYB.js.map} +0 -0
  233. /package/dist/{chunk-5OX6XVNS.js.map → chunk-LOA2VCMS.js.map} +0 -0
  234. /package/dist/{chunk-BB27JMWB.js.map → chunk-LSEW3ZZ2.js.map} +0 -0
  235. /package/dist/{chunk-Y26YV5R3.js.map → chunk-LWSD4QPT.js.map} +0 -0
  236. /package/dist/{chunk-26NK23DZ.js.map → chunk-M45IRXDM.js.map} +0 -0
  237. /package/dist/{chunk-GNHAC43Q.js.map → chunk-O53RIZCC.js.map} +0 -0
  238. /package/dist/{chunk-LSTBFLL2.js.map → chunk-P3Z5Y2TS.js.map} +0 -0
  239. /package/dist/{chunk-QSOYKKMD.js.map → chunk-P4EDT5ZP.js.map} +0 -0
  240. /package/dist/{chunk-PC6ZEDRL.js.map → chunk-RHQYVHFH.js.map} +0 -0
  241. /package/dist/{chunk-3LPV6BXR.js.map → chunk-RRDWXNBQ.js.map} +0 -0
  242. /package/dist/{chunk-4CLICFEY.js.map → chunk-SJJQKNMP.js.map} +0 -0
  243. /package/dist/{chunk-TY32C732.js.map → chunk-SZ4N3IL5.js.map} +0 -0
  244. /package/dist/{chunk-2N62W5YP.js.map → chunk-UA6G45ME.js.map} +0 -0
  245. /package/dist/{chunk-DAP2XL7Q.js.map → chunk-VOXMU6LB.js.map} +0 -0
  246. /package/dist/{chunk-DJRWA3Q5.js.map → chunk-WUG3E423.js.map} +0 -0
  247. /package/dist/{chunk-PM3QYWUU.js.map → chunk-XHM2SARW.js.map} +0 -0
  248. /package/dist/{chunk-RC6SU5NO.js.map → chunk-XSIFXX54.js.map} +0 -0
  249. /package/dist/{chunk-CXFOITNS.js.map → chunk-ZC7MNVYN.js.map} +0 -0
  250. /package/dist/{chunk-6T2UDBKG.js.map → chunk-ZCFS7U4J.js.map} +0 -0
  251. /package/dist/{crypto-2CRLG4F4.js.map → crypto-AJB72OKN.js.map} +0 -0
  252. /package/dist/{delegation-ZTRT2PRV.js.map → delegation-6FCWDRUS.js.map} +0 -0
  253. /package/dist/{executor-S76VN45G.js.map → executor-7KSCEIFA.js.map} +0 -0
  254. /package/dist/{executor-UCXLIGLW.js.map → executor-D2QMNGRJ.js.map} +0 -0
  255. /package/dist/{executor-ZCNZJMGR.js.map → executor-O5AZK7UW.js.map} +0 -0
  256. /package/dist/{fanout-sidecar-OKPMMPLG.js.map → fanout-sidecar-ZSKEQ6NI.js.map} +0 -0
  257. /package/dist/{issue-3W6IVLKH.js.map → issue-YIYG4OW5.js.map} +0 -0
  258. /package/dist/{ledger-O7FXOG3D.js.map → ledger-5JMVF7PY.js.map} +0 -0
  259. /package/dist/{noydb-YAZNH5TI.js.map → noydb-D5SLAJ6V.js.map} +0 -0
  260. /package/dist/{public-envelope-HMYHZIRH.js.map → public-envelope-PFLZI5MO.js.map} +0 -0
  261. /package/dist/{registry-ST2VNFZC.js.map → registry-BVQ5ITMF.js.map} +0 -0
  262. /package/dist/{registry-UFIK7CSR.js.map → registry-JLP3QOLD.js.map} +0 -0
  263. /package/dist/{registry-ZGYYSM5I.js.map → registry-NCY445U5.js.map} +0 -0
  264. /package/dist/{revoke-S6JMSLUN.js.map → revoke-7RLGQWZ7.js.map} +0 -0
  265. /package/dist/{signer-7NPTB3SQ.js.map → signer-6JF44I4A.js.map} +0 -0
  266. /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);
@@ -7940,6 +9115,18 @@ async function loadKeyring(adapter, vault, userId, passphrase) {
7940
9115
  ...keyringFile.policy !== void 0 && { policy: keyringFile.policy }
7941
9116
  };
7942
9117
  }
9118
+ async function assertKeyringOpenAllowed(store, vault, userId, create) {
9119
+ const keyringUsers = await store.list(vault, "_keyring");
9120
+ if (keyringUsers.includes(userId)) return;
9121
+ if (!create) {
9122
+ throw new NoAccessError(`Vault "${vault}" not opened: create disabled and no keyring for "${userId}".`);
9123
+ }
9124
+ if (keyringUsers.length > 0) {
9125
+ throw new NoAccessError(
9126
+ `No keyring for user "${userId}" in vault "${vault}" (held by other principals) \u2014 refusing to self-provision.`
9127
+ );
9128
+ }
9129
+ }
7943
9130
  async function createOwnerKeyring(adapter, vault, userId, passphrase, passphraseOpts) {
7944
9131
  if (passphraseOpts?.validate && !passphraseOpts.allowWeakPassphrase) {
7945
9132
  assertStrongPassphrase(passphrase, passphraseOpts);
@@ -10022,6 +11209,119 @@ function applyI18nLocale(record, i18nFields, locale, fallback, layer = "read") {
10022
11209
  return result;
10023
11210
  }
10024
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
+
10025
11325
  // src/i18n/strategy.ts
10026
11326
  function notEnabled(op) {
10027
11327
  return new Error(
@@ -10340,6 +11640,7 @@ var NO_AGGREGATE = {
10340
11640
  };
10341
11641
 
10342
11642
  // src/query/builder.ts
11643
+ init_money_reducer();
10343
11644
  var EMPTY_PLAN = {
10344
11645
  clauses: [],
10345
11646
  orderBy: [],
@@ -10785,6 +12086,10 @@ var Query = class _Query {
10785
12086
  * partition boundaries without an API break.
10786
12087
  */
10787
12088
  aggregate(spec) {
12089
+ const moneyFields = this.source.moneyFields;
12090
+ if (moneyFields) {
12091
+ spec = wrapMoneyReducers(spec, moneyFields);
12092
+ }
10788
12093
  const source = this.source;
10789
12094
  const clauses = this.plan.clauses;
10790
12095
  const joinCtx = this.joinContext;
@@ -10832,13 +12137,15 @@ var Query = class _Query {
10832
12137
  executeRecords,
10833
12138
  field,
10834
12139
  upstreams,
10835
- dictLabelResolver
12140
+ dictLabelResolver,
12141
+ this.source.moneyFields
10836
12142
  );
10837
12143
  }
10838
12144
  return this.aggregateStrategy.groupByN(
10839
12145
  executeRecords,
10840
12146
  fields,
10841
- upstreams
12147
+ upstreams,
12148
+ this.source.moneyFields
10842
12149
  );
10843
12150
  }
10844
12151
  /**
@@ -11379,7 +12686,8 @@ function count(opts) {
11379
12686
  init: () => 0,
11380
12687
  step: (state) => state + 1,
11381
12688
  remove: (state) => state - 1,
11382
- finalize: (state) => state
12689
+ finalize: (state) => state,
12690
+ merge: (a, b) => a + b
11383
12691
  };
11384
12692
  }
11385
12693
  function sum(field, opts) {
@@ -11388,10 +12696,15 @@ function sum(field, opts) {
11388
12696
  return {
11389
12697
  op: "sum",
11390
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 } : {},
11391
12703
  init: () => 0,
11392
12704
  step: (state, record) => state + readNumber(record, field),
11393
12705
  remove: (state, record) => state - readNumber(record, field),
11394
- finalize: (state) => state
12706
+ finalize: (state) => state,
12707
+ merge: (a, b) => a + b
11395
12708
  };
11396
12709
  }
11397
12710
  function avg(field, opts) {
@@ -11409,7 +12722,8 @@ function avg(field, opts) {
11409
12722
  sum: state.sum - readNumber(record, field),
11410
12723
  count: state.count - 1
11411
12724
  }),
11412
- 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 })
11413
12727
  };
11414
12728
  }
11415
12729
  function pushValue(state, value) {
@@ -11439,7 +12753,8 @@ function min(field, opts) {
11439
12753
  if (v < out) out = v;
11440
12754
  }
11441
12755
  return out;
11442
- }
12756
+ },
12757
+ merge: (a, b) => ({ values: [...a.values, ...b.values] })
11443
12758
  };
11444
12759
  }
11445
12760
  function max(field, opts) {
@@ -11459,11 +12774,17 @@ function max(field, opts) {
11459
12774
  if (v > out) out = v;
11460
12775
  }
11461
12776
  return out;
11462
- }
12777
+ },
12778
+ merge: (a, b) => ({ values: [...a.values, ...b.values] })
11463
12779
  };
11464
12780
  }
11465
12781
  function readNumber(record, field) {
11466
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
+ }
11467
12788
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
11468
12789
  }
11469
12790
 
@@ -11877,8 +13198,8 @@ function coerceRefKey2(value) {
11877
13198
 
11878
13199
  // src/indexing/persisted-indexes.ts
11879
13200
  var IDX_PREFIX = "_idx/";
11880
- function encodeIdxId(field, recordId2) {
11881
- return `${IDX_PREFIX}${field}/${recordId2}`;
13201
+ function encodeIdxId(field, recordId3) {
13202
+ return `${IDX_PREFIX}${field}/${recordId3}`;
11882
13203
  }
11883
13204
  function decodeIdxId(id) {
11884
13205
  if (!id.startsWith(IDX_PREFIX)) return null;
@@ -11886,9 +13207,9 @@ function decodeIdxId(id) {
11886
13207
  const firstSlash = rest.indexOf("/");
11887
13208
  if (firstSlash <= 0) return null;
11888
13209
  const field = rest.slice(0, firstSlash);
11889
- const recordId2 = rest.slice(firstSlash + 1);
11890
- if (recordId2.length === 0) return null;
11891
- return { field, recordId: recordId2 };
13210
+ const recordId3 = rest.slice(firstSlash + 1);
13211
+ if (recordId3.length === 0) return null;
13212
+ return { field, recordId: recordId3 };
11892
13213
  }
11893
13214
 
11894
13215
  // src/indexing/lazy-builder.ts
@@ -12874,6 +14195,18 @@ var Collection = class {
12874
14195
  * fields when a locale is requested.
12875
14196
  */
12876
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;
12877
14210
  /**
12878
14211
  * Async callback provided by the Vault that resolves a dict key
12879
14212
  * to its label for a given locale. Used by the locale-read path for
@@ -13015,6 +14348,8 @@ var Collection = class {
13015
14348
  this.joinResolver = opts.joinResolver;
13016
14349
  this.i18nFields = opts.i18nFields;
13017
14350
  this.dictKeyFields = opts.dictKeyFields;
14351
+ this.moneyFields = opts.moneyFields;
14352
+ this.computed = opts.computed;
13018
14353
  this.dictLabelResolver = opts.dictLabelResolver;
13019
14354
  this.i18nPutValidator = opts.i18nPutValidator;
13020
14355
  this.autoTranslateHook = opts.autoTranslateHook;
@@ -13139,6 +14474,19 @@ var Collection = class {
13139
14474
  getSchema() {
13140
14475
  return this.schema;
13141
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
+ }
13142
14490
  /**
13143
14491
  * Get a single record by ID.
13144
14492
  *
@@ -13310,7 +14658,7 @@ var Collection = class {
13310
14658
  existingRecord = null;
13311
14659
  }
13312
14660
  }
13313
- await this.subsystemBus.dispatchGate("beforePut", {
14661
+ const gateEvent = {
13314
14662
  op: existingEnv ? "update" : "create",
13315
14663
  vault: this.vault,
13316
14664
  collection: this.name,
@@ -13320,12 +14668,20 @@ var Collection = class {
13320
14668
  existingVersion: existingEnv?._v ?? 0,
13321
14669
  existingTs: existingEnv?._ts,
13322
14670
  userId: this.keyring.userId,
13323
- role: this.keyring.role
13324
- });
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);
13325
14678
  }
13326
14679
  if (this.schema !== void 0) {
13327
14680
  record = await validateSchemaInput(this.schema, record, `put(${id})`);
13328
14681
  }
14682
+ if (this.moneyFields) {
14683
+ record = quantizeMoneyFields(record, this.moneyFields);
14684
+ }
13329
14685
  if (this.i18nFields) {
13330
14686
  const obj = record;
13331
14687
  for (const [field, descriptor] of Object.entries(this.i18nFields)) {
@@ -14147,7 +15503,8 @@ var Collection = class {
14147
15503
  // fields. The Query builder consults these when present and falls
14148
15504
  // back to a linear scan otherwise.
14149
15505
  getIndexes: () => this.getIndexes(),
14150
- lookupById: (id) => this.cache.get(id)?.record
15506
+ lookupById: (id) => this.cache.get(id)?.record,
15507
+ ...this.moneyFields ? { moneyFields: this.moneyFields } : {}
14151
15508
  };
14152
15509
  const resolver = this.joinResolver;
14153
15510
  const leftCollection = this.name;
@@ -14649,11 +16006,11 @@ var Collection = class {
14649
16006
  }
14650
16007
  }
14651
16008
  persisted.clear();
14652
- for (const recordId2 of canonicalIds) {
14653
- 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);
14654
16011
  if (!envelope) continue;
14655
16012
  const record = await this.decryptRecord(envelope, { skipValidation: true });
14656
- await this.maintainPersistedIndexesOnPut(recordId2, record, null, envelope._v);
16013
+ await this.maintainPersistedIndexesOnPut(recordId3, record, null, envelope._v);
14657
16014
  }
14658
16015
  this.persistedIndexesLoaded = true;
14659
16016
  }
@@ -14829,10 +16186,14 @@ var Collection = class {
14829
16186
  async applyLocaleToRecord(record, localeOpts) {
14830
16187
  const hasI18n = this.i18nFields && Object.keys(this.i18nFields).length > 0;
14831
16188
  const hasDict = this.dictKeyFields && Object.keys(this.dictKeyFields).length > 0;
14832
- if (!hasI18n && !hasDict) return record;
16189
+ const hasMoney = this.moneyFields && Object.keys(this.moneyFields).length > 0;
16190
+ if (!hasI18n && !hasDict && !hasMoney) return record;
14833
16191
  const locale = localeOpts?.locale ?? this.defaultLocale;
14834
- if (!locale) return record;
14835
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;
14836
16197
  if (hasI18n && this.i18nFields) {
14837
16198
  result = this.i18nStrategy.applyI18nLocale(result, this.i18nFields, locale, localeOpts?.fallback);
14838
16199
  }
@@ -16231,6 +17592,7 @@ async function runCompaction(ctx, options = {}) {
16231
17592
  let evicted = 0;
16232
17593
  let records = 0;
16233
17594
  let auditEntries = 0;
17595
+ let held = 0;
16234
17596
  let collectionsWithPolicy = 0;
16235
17597
  outer: for (const collectionName of allCollections) {
16236
17598
  if (collectionName.startsWith("_")) continue;
@@ -16241,25 +17603,29 @@ async function runCompaction(ctx, options = {}) {
16241
17603
  collectionsWithPolicy += 1;
16242
17604
  byCollection[collectionName] = { records: 0, evicted: 0 };
16243
17605
  const ids = await ctx.listRecords(collectionName);
16244
- for (const recordId2 of ids) {
17606
+ for (const recordId3 of ids) {
16245
17607
  if (evicted >= maxEvictions) break outer;
16246
- const record = await ctx.getRecord(collectionName, recordId2).catch(() => null);
17608
+ const record = await ctx.getRecord(collectionName, recordId3).catch(() => null);
16247
17609
  if (record === null) continue;
16248
17610
  records += 1;
16249
17611
  byCollection[collectionName].records += 1;
16250
- const slots = await ctx.listSlots(collectionName, recordId2).catch(() => []);
17612
+ const slots = await ctx.listSlots(collectionName, recordId3).catch(() => []);
16251
17613
  for (const slot of slots) {
16252
17614
  if (evicted >= maxEvictions) break outer;
16253
17615
  const policy = config[slot.name];
16254
17616
  if (!policy) continue;
16255
17617
  const reason = evaluatePolicy(policy, record, slot, now);
16256
17618
  if (!reason) continue;
17619
+ if (isHeld2(policy, record, now)) {
17620
+ held += 1;
17621
+ continue;
17622
+ }
16257
17623
  if (!dryRun) {
16258
- await ctx.deleteSlot(collectionName, recordId2, slot.name);
17624
+ await ctx.deleteSlot(collectionName, recordId3, slot.name);
16259
17625
  await writeAuditEntry(ctx, {
16260
- id: generateEvictionId(collectionName, recordId2, slot.name),
17626
+ id: generateEvictionId(collectionName, recordId3, slot.name),
16261
17627
  collection: collectionName,
16262
- recordId: recordId2,
17628
+ recordId: recordId3,
16263
17629
  slotName: slot.name,
16264
17630
  blobHash: slot.eTag,
16265
17631
  reason,
@@ -16278,9 +17644,32 @@ async function runCompaction(ctx, options = {}) {
16278
17644
  records,
16279
17645
  collections: collectionsWithPolicy,
16280
17646
  auditEntries,
17647
+ held,
16281
17648
  byCollection
16282
17649
  };
16283
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
+ }
16284
17673
  function evaluatePolicy(policy, record, slot, now) {
16285
17674
  let ttlTriggered = false;
16286
17675
  let predicateTriggered = false;
@@ -16303,11 +17692,11 @@ function evaluatePolicy(policy, record, slot, now) {
16303
17692
  if (predicateTriggered) return "predicate";
16304
17693
  return null;
16305
17694
  }
16306
- function generateEvictionId(collection, recordId2, slotName) {
17695
+ function generateEvictionId(collection, recordId3, slotName) {
16307
17696
  const rand = globalThis.crypto.getRandomValues(new Uint8Array(8));
16308
17697
  let suffix = "";
16309
17698
  for (const b of rand) suffix += b.toString(16).padStart(2, "0");
16310
- return `${collection}__${recordId2}__${slotName}__${suffix}`;
17699
+ return `${collection}__${recordId3}__${slotName}__${suffix}`;
16311
17700
  }
16312
17701
  async function writeAuditEntry(ctx, entry) {
16313
17702
  const json = JSON.stringify(entry);
@@ -16358,7 +17747,7 @@ async function deriveMagicLinkContentKey(serverSecret, token, vault) {
16358
17747
  ["encrypt", "decrypt"]
16359
17748
  );
16360
17749
  }
16361
- async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek, recordId2, opts) {
17750
+ async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek, recordId3, opts) {
16362
17751
  const collectionName = opts.collection ?? null;
16363
17752
  const sourceKey = collectionName ? dekKey(collectionName, opts.tier) : `__any#${opts.tier}`;
16364
17753
  const sourceDek = grantor.deks.get(sourceKey);
@@ -16371,7 +17760,7 @@ async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek,
16371
17760
  const until = typeof opts.until === "string" ? opts.until : opts.until.toISOString();
16372
17761
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
16373
17762
  const payload = {
16374
- id: recordId2,
17763
+ id: recordId3,
16375
17764
  toUser: opts.toUser,
16376
17765
  fromUser: grantor.userId,
16377
17766
  tier: opts.tier,
@@ -16391,11 +17780,11 @@ async function writeMagicLinkGrant(store, vault, grantor, contentKey, grantKek,
16391
17780
  _data: data,
16392
17781
  _by: grantor.userId
16393
17782
  };
16394
- await store.put(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId2, envelope);
16395
- return { recordId: recordId2, payload };
17783
+ await store.put(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId3, envelope);
17784
+ return { recordId: recordId3, payload };
16396
17785
  }
16397
- async function readMagicLinkGrantRecord(store, vault, contentKey, recordId2) {
16398
- 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);
16399
17788
  if (!env) return null;
16400
17789
  try {
16401
17790
  const json = await decrypt(env._iv, env._data, contentKey);
@@ -16997,6 +18386,10 @@ var Vault = class {
16997
18386
  * call throws with a pointer at `@noy-db/hub/blobs`.
16998
18387
  */
16999
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();
17000
18393
  indexStrategy;
17001
18394
  aggregateStrategy;
17002
18395
  crdtStrategy;
@@ -17100,6 +18493,8 @@ var Vault = class {
17100
18493
  * docstring.
17101
18494
  */
17102
18495
  ledgerStore = null;
18496
+ /** Lazily-built atomic-sequence store. See {@link sequence}. */
18497
+ sequenceStore = null;
17103
18498
  /**
17104
18499
  * Background writes for persisted-schema envelopes (#schema-dump v0
17105
18500
  * slice 1). One promise per `collection({ persistJsonSchema: true })`
@@ -17201,6 +18596,7 @@ var Vault = class {
17201
18596
  this.onRegisterConflictResolver = opts.onRegisterConflictResolver;
17202
18597
  this.syncAdapter = opts.syncAdapter;
17203
18598
  this.blobStrategy = opts.blobStrategy;
18599
+ this.archiveStrategy = opts.archiveStrategy;
17204
18600
  this.indexStrategy = opts.indexStrategy;
17205
18601
  this.aggregateStrategy = opts.aggregateStrategy;
17206
18602
  this.crdtStrategy = opts.crdtStrategy;
@@ -17263,8 +18659,9 @@ var Vault = class {
17263
18659
  *. `put()` validates keys against the declared set; reads
17264
18660
  * with `{ locale }` add `<field>Label` virtual fields.
17265
18661
  *
17266
- * Throws `ReservedCollectionNameError` for names starting with `_dict_`.
17267
- * 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.
17268
18665
  *
17269
18666
  * Lazy mode + indexes is rejected at construction time — see the
17270
18667
  * Collection constructor for the rationale.
@@ -17283,7 +18680,16 @@ var Vault = class {
17283
18680
  if (isDictCollectionName(collectionName)) {
17284
18681
  throw new ReservedCollectionNameError(collectionName);
17285
18682
  }
18683
+ if (collectionName === SEQUENCE_COLLECTION) {
18684
+ throw new ReservedCollectionNameError(collectionName);
18685
+ }
17286
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
+ }
17287
18693
  if (!coll) {
17288
18694
  if (options?.refs) {
17289
18695
  this.refRegistry.register(collectionName, options.refs);
@@ -17294,6 +18700,9 @@ var Vault = class {
17294
18700
  if (options?.blobFields) {
17295
18701
  this.blobFieldsRegistry.set(collectionName, options.blobFields);
17296
18702
  }
18703
+ if (options?.archive) {
18704
+ this.archiveRegistry.set(collectionName, options.archive);
18705
+ }
17297
18706
  if (options?.attestation !== void 0) {
17298
18707
  this.attestationRegistry.set(collectionName, options.attestation);
17299
18708
  }
@@ -17409,6 +18818,8 @@ var Vault = class {
17409
18818
  collOpts.onCrossTierAccess = (event) => this.emitCrossTier(event);
17410
18819
  if (this.syncAdapter !== void 0) collOpts.syncAdapter = this.syncAdapter;
17411
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;
17412
18823
  if (options?.dictKeyFields !== void 0) {
17413
18824
  collOpts.dictLabelResolver = async (dictName, key, locale, fallback) => {
17414
18825
  const handle = this.dictionary(dictName);
@@ -17828,6 +19239,33 @@ var Vault = class {
17828
19239
  * await vault.compact({ maxEvictions: 1000 }) // cap batch
17829
19240
  * ```
17830
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
+ }
17831
19269
  async compact(options = {}) {
17832
19270
  return runCompaction({
17833
19271
  adapter: this.adapter,
@@ -17852,6 +19290,48 @@ var Vault = class {
17852
19290
  }
17853
19291
  }, options);
17854
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
+ }
17855
19335
  exportBlobs(options = {}) {
17856
19336
  this.assertCanExport("plaintext", "blob");
17857
19337
  return createExportBlobsHandle(
@@ -18550,7 +20030,7 @@ var Vault = class {
18550
20030
  *
18551
20031
  * @internal
18552
20032
  */
18553
- async _logConsent(op, collection, recordId2) {
20033
+ async _logConsent(op, collection, recordId3) {
18554
20034
  const ctx = this.consentContext;
18555
20035
  if (!ctx) return;
18556
20036
  await this.consentStrategy.write(
@@ -18563,7 +20043,7 @@ var Vault = class {
18563
20043
  consentHash: ctx.consentHash,
18564
20044
  op,
18565
20045
  collection,
18566
- recordId: recordId2
20046
+ recordId: recordId3
18567
20047
  },
18568
20048
  this.getDEK
18569
20049
  );
@@ -18746,14 +20226,14 @@ var Vault = class {
18746
20226
  * the HKDF derivation, record-id composition, and batch logic so the
18747
20227
  * grantor doesn't touch this method directly.
18748
20228
  */
18749
- async writeMagicLinkGrant(contentKey, grantKek, recordId2, opts) {
20229
+ async writeMagicLinkGrant(contentKey, grantKek, recordId3, opts) {
18750
20230
  return writeMagicLinkGrant(
18751
20231
  this.adapter,
18752
20232
  this.name,
18753
20233
  this.keyring,
18754
20234
  contentKey,
18755
20235
  grantKek,
18756
- recordId2,
20236
+ recordId3,
18757
20237
  opts
18758
20238
  );
18759
20239
  }
@@ -19113,7 +20593,7 @@ var Vault = class {
19113
20593
  }
19114
20594
  }
19115
20595
  const internalSnapshot = {};
19116
- 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]) {
19117
20597
  const ids = await this.adapter.list(this.name, internalName);
19118
20598
  if (ids.length === 0) continue;
19119
20599
  const records = {};
@@ -20589,6 +22069,7 @@ var Noydb = class {
20589
22069
  writeRelay;
20590
22070
  /** Per-vault policy enforcers. */
20591
22071
  policyEnforcers = /* @__PURE__ */ new Map();
22072
+ vaultTemplates = /* @__PURE__ */ new Map();
20592
22073
  txStrategy;
20593
22074
  sessionStrategy;
20594
22075
  syncStrategy;
@@ -20658,7 +22139,7 @@ var Noydb = class {
20658
22139
  await registry.runChecks(e.collection, incoming, ctx);
20659
22140
  const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor(), executor_exports));
20660
22141
  for (const g of guards) {
20661
- await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming);
22142
+ await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming, e.computedFieldNames);
20662
22143
  }
20663
22144
  });
20664
22145
  this.subsystemBus.registerGate("beforeDelete", async (e) => {
@@ -20779,7 +22260,7 @@ var Noydb = class {
20779
22260
  }
20780
22261
  return comp;
20781
22262
  }
20782
- const keyring = await this.getKeyringInternal(name);
22263
+ const keyring = await this.getKeyringInternal(name, { create: opts?.create !== false });
20783
22264
  if (!this.activeTier.has(name)) {
20784
22265
  this.activeTier.set(name, 1);
20785
22266
  }
@@ -20837,6 +22318,7 @@ var Noydb = class {
20837
22318
  syncAdapter: targets.length > 0 ? targets[0].store : void 0,
20838
22319
  historyConfig: this.options.history,
20839
22320
  ...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
22321
+ ...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
20840
22322
  ...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
20841
22323
  ...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
20842
22324
  ...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
@@ -20890,6 +22372,7 @@ var Noydb = class {
20890
22372
  emitter: this.emitter,
20891
22373
  historyConfig: this.options.history,
20892
22374
  ...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
22375
+ ...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
20893
22376
  ...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
20894
22377
  ...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
20895
22378
  ...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
@@ -20918,6 +22401,7 @@ var Noydb = class {
20918
22401
  encrypted: true,
20919
22402
  historyConfig: this.options.history,
20920
22403
  ...this.options.blobStrategy !== void 0 ? { blobStrategy: this.options.blobStrategy } : {},
22404
+ ...this.options.archiveStrategy !== void 0 ? { archiveStrategy: this.options.archiveStrategy } : {},
20921
22405
  ...this.options.indexStrategy !== void 0 ? { indexStrategy: this.options.indexStrategy } : {},
20922
22406
  ...this.options.aggregateStrategy !== void 0 ? { aggregateStrategy: this.options.aggregateStrategy } : {},
20923
22407
  ...this.options.crdtStrategy !== void 0 ? { crdtStrategy: this.options.crdtStrategy } : {},
@@ -21207,7 +22691,7 @@ var Noydb = class {
21207
22691
  const vaultId = vaultIds[idx];
21208
22692
  const task = (async () => {
21209
22693
  try {
21210
- const comp = await this.openVault(vaultId);
22694
+ const comp = await this.openVault(vaultId, { create: options.create !== false });
21211
22695
  const result = await fn(comp);
21212
22696
  results[idx] = { vault: vaultId, result };
21213
22697
  } catch (err) {
@@ -21232,6 +22716,32 @@ var Noydb = class {
21232
22716
  }
21233
22717
  return results;
21234
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
+ }
21235
22745
  /**
21236
22746
  * Change the current user's passphrase for a vault.
21237
22747
  *
@@ -22549,7 +24059,7 @@ var Noydb = class {
22549
24059
  * accesses see them. Not exposed publicly — callers outside hub
22550
24060
  * should use {@link getKeyring}, which returns a defensive copy.
22551
24061
  */
22552
- async getKeyringInternal(vault) {
24062
+ async getKeyringInternal(vault, opts = { create: true }) {
22553
24063
  if (this.options.encrypt === false) {
22554
24064
  return createPlaintextKeyring(this.options.user);
22555
24065
  }
@@ -22560,6 +24070,7 @@ var Noydb = class {
22560
24070
  this.keyringCache.set(vault, keyring2);
22561
24071
  return keyring2;
22562
24072
  }
24073
+ await assertKeyringOpenAllowed(this.options.store, vault, this.options.user, opts.create);
22563
24074
  let effectiveSecret;
22564
24075
  if (this.options.passphraseMode === "managed") {
22565
24076
  effectiveSecret = await resolveManagedSecret(
@@ -23493,6 +25004,50 @@ function withGuard(strategy) {
23493
25004
  };
23494
25005
  }
23495
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
+
23496
25051
  // src/derivations/with-derivation.ts
23497
25052
  init_errors();
23498
25053
  function withDerivation(spec) {
@@ -23650,6 +25205,10 @@ function withOverlayedView(spec) {
23650
25205
  init_errors();
23651
25206
  init_errors();
23652
25207
 
25208
+ // src/money/index.ts
25209
+ init_descriptor();
25210
+ init_iso4217();
25211
+
23653
25212
  // src/i18n/script.ts
23654
25213
  init_errors();
23655
25214
  var LATIN_BASE = /* @__PURE__ */ new Set([
@@ -24435,6 +25994,7 @@ function shortJSON(value) {
24435
25994
  CollectionFrame,
24436
25995
  CollectionIndexes,
24437
25996
  CollectionInstant,
25997
+ ComputedFieldError,
24438
25998
  ConflictError,
24439
25999
  CrossJoinSourceUnknownError,
24440
26000
  CrossJoinTooLargeError,
@@ -24498,6 +26058,9 @@ function shortJSON(value) {
24498
26058
  MemorySealingKeyProvider,
24499
26059
  MigrationRequiredError,
24500
26060
  MissingTranslationError,
26061
+ MoneyCurrencyError,
26062
+ MoneyPrecisionError,
26063
+ MoneyUnsupportedError,
24501
26064
  NOYDB_BACKUP_VERSION,
24502
26065
  NOYDB_BUNDLE_FORMAT_VERSION,
24503
26066
  NOYDB_BUNDLE_MAGIC,
@@ -24550,9 +26113,13 @@ function shortJSON(value) {
24550
26113
  SchemaUpdateError,
24551
26114
  SchemaValidationError,
24552
26115
  ScriptViolationError,
26116
+ SequenceContentionError,
26117
+ SequenceOfflineError,
26118
+ SequenceStore,
24553
26119
  SessionExpiredError,
24554
26120
  SessionNotFoundError,
24555
26121
  SessionPolicyError,
26122
+ ShardProvisioningError,
24556
26123
  SnapshotNotFoundError,
24557
26124
  StoreCapabilityError,
24558
26125
  SyncEngine,
@@ -24568,6 +26135,7 @@ function shortJSON(value) {
24568
26135
  USER_ENVELOPE_COLLECTION,
24569
26136
  USER_ENVELOPE_MAX_BYTES,
24570
26137
  UniqueConstraintError,
26138
+ UnknownShardError,
24571
26139
  UnsupportedIndexOptionError,
24572
26140
  UserApi,
24573
26141
  UserEnvelopeOversizedError,
@@ -24576,6 +26144,7 @@ function shortJSON(value) {
24576
26144
  Vault,
24577
26145
  VaultFrame,
24578
26146
  VaultInstant,
26147
+ VaultTemplateNotFoundError,
24579
26148
  WeakPassphraseError,
24580
26149
  activeSessionCount,
24581
26150
  additiveOnly,
@@ -24632,6 +26201,7 @@ function shortJSON(value) {
24632
26201
  envelopePayloadHash,
24633
26202
  estimateEntropy,
24634
26203
  estimateRecordBytes,
26204
+ evalComputedFields,
24635
26205
  evaluateClause,
24636
26206
  evaluateExportCapability,
24637
26207
  evaluateFieldClause,
@@ -24648,6 +26218,7 @@ function shortJSON(value) {
24648
26218
  hasRecoveryEnrolled,
24649
26219
  hashEntry,
24650
26220
  i18nText,
26221
+ immutableGuard,
24651
26222
  inferScripts,
24652
26223
  isDevUnlockActive,
24653
26224
  isDictCollectionName,
@@ -24655,6 +26226,7 @@ function shortJSON(value) {
24655
26226
  isDiscriminant,
24656
26227
  isI18nTextDescriptor,
24657
26228
  isMagicLinkGrantExpired,
26229
+ isMoneyDescriptor,
24658
26230
  isPreCompressed,
24659
26231
  isPublicEnvelope,
24660
26232
  isSessionAlive,
@@ -24686,6 +26258,7 @@ function shortJSON(value) {
24686
26258
  mintPaperRecoveryEntry,
24687
26259
  mintShamirRecoveryEntry,
24688
26260
  mintWrappedDeksBlob,
26261
+ money,
24689
26262
  paddedIndex,
24690
26263
  parseBytes,
24691
26264
  parseIndex,
@@ -24726,6 +26299,7 @@ function shortJSON(value) {
24726
26299
  saveShamirRecoveryEntries,
24727
26300
  saveUserEnvelope,
24728
26301
  saveVaultPolicy,
26302
+ scaleForCurrency,
24729
26303
  sha256Hex,
24730
26304
  sum,
24731
26305
  unwrapDeksFromBlob,
@@ -24739,6 +26313,7 @@ function shortJSON(value) {
24739
26313
  validateSchemaOutput,
24740
26314
  validateSessionPolicy,
24741
26315
  visibilityRecordId,
26316
+ withArchive,
24742
26317
  withCache,
24743
26318
  withCircuitBreaker,
24744
26319
  withDerivation,