@noy-db/hub 0.6.0 → 0.7.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +1 -1
  3. package/codemods/0.4.0-pre.json +24 -0
  4. package/codemods/0.6.0-pre.json +18 -0
  5. package/codemods/0.7.0-pre.json +258 -0
  6. package/dist/api-EQYRTZO2.js +20 -0
  7. package/dist/as/index.js +146 -0
  8. package/dist/as/index.js.map +1 -0
  9. package/dist/at/index.js +23 -0
  10. package/dist/attestation/index.js +18 -18
  11. package/dist/{backup-VL4GRIEW.js → backup-Z7ZBTAQ6.js} +12 -12
  12. package/dist/blobs/index.js +13 -13
  13. package/dist/broker/index.js +12 -12
  14. package/dist/by/index.js +11 -0
  15. package/dist/cargo/index.js +35 -31
  16. package/dist/{chunk-JKATP6OE.js → chunk-225O5CXQ.js} +2 -2
  17. package/dist/{chunk-XINT4VJA.js → chunk-2GXEQUEX.js} +2 -2
  18. package/dist/chunk-2GXEQUEX.js.map +1 -0
  19. package/dist/{chunk-CXPOG3ZY.js → chunk-2LPVI4PX.js} +8 -8
  20. package/dist/{chunk-2J5IT37K.js → chunk-3L2MVE36.js} +5 -5
  21. package/dist/{chunk-HOPNIR5R.js → chunk-3QARDAYW.js} +2 -2
  22. package/dist/{chunk-HSJBJIB2.js → chunk-467SKJ65.js} +4 -4
  23. package/dist/{chunk-YCGLAANM.js → chunk-4A4TYISP.js} +2 -2
  24. package/dist/{chunk-2CBFYXJX.js → chunk-4G25SDQL.js} +4 -4
  25. package/dist/{chunk-5LZZQMIC.js → chunk-4LPF226H.js} +5 -5
  26. package/dist/{chunk-GANFIM2M.js → chunk-4ZPE6SKS.js} +3 -3
  27. package/dist/{chunk-65JXA63J.js → chunk-5263OEM3.js} +2 -2
  28. package/dist/{chunk-FNYCT27B.js → chunk-57JTHWLQ.js} +4 -4
  29. package/dist/{chunk-5IHGJHWC.js → chunk-5ACAVJUI.js} +2 -2
  30. package/dist/{chunk-3OE2QNSM.js → chunk-5AFMP532.js} +6 -6
  31. package/dist/{chunk-J6I5VR6P.js → chunk-5W7YXFV5.js} +8 -8
  32. package/dist/chunk-5W7YXFV5.js.map +1 -0
  33. package/dist/{chunk-7ZGPQOFV.js → chunk-6EW2UCVF.js} +3 -3
  34. package/dist/{chunk-GCU42IG7.js → chunk-6FOGTKBY.js} +7 -7
  35. package/dist/chunk-6FOGTKBY.js.map +1 -0
  36. package/dist/{chunk-36CEG6CM.js → chunk-7LYUJNU3.js} +2 -2
  37. package/dist/{chunk-D6SXRMZZ.js → chunk-A2YW5EMU.js} +5 -5
  38. package/dist/{chunk-OQMCFHUX.js → chunk-AB5P32RE.js} +2 -2
  39. package/dist/{chunk-JMJSCI7U.js → chunk-ALB64CZM.js} +7 -7
  40. package/dist/{chunk-RPRASGEY.js → chunk-ALUNQEBY.js} +2 -2
  41. package/dist/{chunk-ZYZJLYNS.js → chunk-ATV637J7.js} +7 -7
  42. package/dist/{chunk-LC6NE4IQ.js → chunk-CBRS2NEK.js} +2 -11
  43. package/dist/chunk-CBRS2NEK.js.map +1 -0
  44. package/dist/{chunk-W3HAIN6U.js → chunk-CN6B5PEN.js} +5 -5
  45. package/dist/{chunk-JIHWBKMQ.js → chunk-DBNYPLWY.js} +3 -3
  46. package/dist/{chunk-ZJUZOJVW.js → chunk-DFAUMLD7.js} +5 -5
  47. package/dist/{chunk-S6PJ6ZKE.js → chunk-DKWPJOYH.js} +2 -2
  48. package/dist/{chunk-MPKP6L5E.js → chunk-E6YA6UAP.js} +2 -2
  49. package/dist/{chunk-LIR2DJWP.js → chunk-F23WQK3G.js} +9 -9
  50. package/dist/chunk-F23WQK3G.js.map +1 -0
  51. package/dist/{chunk-S7PM7Z4K.js → chunk-F7ZFRPGQ.js} +2 -2
  52. package/dist/{chunk-CKKN25P2.js → chunk-FOG7YEJP.js} +3 -3
  53. package/dist/{chunk-AJGGBXWA.js → chunk-GK4DRVUD.js} +6 -6
  54. package/dist/chunk-GK4DRVUD.js.map +1 -0
  55. package/dist/{chunk-4ATF4BLF.js → chunk-H36KSN6X.js} +7 -7
  56. package/dist/{chunk-YTUFV7U7.js → chunk-H4M7ADXY.js} +10 -10
  57. package/dist/chunk-H4M7ADXY.js.map +1 -0
  58. package/dist/{chunk-QQRRKO7W.js → chunk-H7HP56F6.js} +5 -5
  59. package/dist/{chunk-RJZQ2PVD.js → chunk-HM4JSXX2.js} +3 -3
  60. package/dist/{chunk-3YGUHUJA.js → chunk-HTGRQZFB.js} +3 -3
  61. package/dist/{chunk-JXHNUYQO.js → chunk-IESNYJXZ.js} +6 -6
  62. package/dist/{chunk-OCJ3JA4O.js → chunk-J4MN6XIU.js} +2 -2
  63. package/dist/{chunk-HWI7GHJZ.js → chunk-J4UJ5FRV.js} +4 -4
  64. package/dist/{chunk-Z3BZXPRO.js → chunk-JBIV4FLR.js} +1 -1
  65. package/dist/chunk-JBIV4FLR.js.map +1 -0
  66. package/dist/{chunk-QVXZFJBU.js → chunk-JVLSU7S2.js} +2 -2
  67. package/dist/{chunk-7Q3RHOA5.js → chunk-KDJGJR7F.js} +2 -2
  68. package/dist/{chunk-PJX72ICF.js → chunk-KN4W2OQ4.js} +7 -7
  69. package/dist/chunk-KN4W2OQ4.js.map +1 -0
  70. package/dist/{chunk-M7WBE3OR.js → chunk-L6GTLZRK.js} +5 -5
  71. package/dist/{chunk-2ZT4INZ4.js → chunk-LF6DMX4C.js} +3 -3
  72. package/dist/chunk-LFUID7OW.js +163 -0
  73. package/dist/chunk-LFUID7OW.js.map +1 -0
  74. package/dist/{chunk-4K6BIMLF.js → chunk-LKFKXS5K.js} +2 -2
  75. package/dist/{chunk-POFYIGQ2.js → chunk-LR4Z47QD.js} +3 -3
  76. package/dist/{chunk-LUYPYCHY.js → chunk-MTBXJAS4.js} +4 -4
  77. package/dist/{chunk-FX3UIDCS.js → chunk-N4CNVSUF.js} +2 -2
  78. package/dist/{chunk-FU5VYEYK.js → chunk-NLKLXXTO.js} +2 -2
  79. package/dist/{chunk-YYV53EHO.js → chunk-OPXR4PB2.js} +2 -2
  80. package/dist/{chunk-5F7BZAGV.js → chunk-OWLVBOWO.js} +10 -10
  81. package/dist/{chunk-6NR36UNB.js → chunk-P6ASQOZR.js} +2 -2
  82. package/dist/{chunk-5ATC6UT4.js → chunk-PCQDY6QN.js} +6 -6
  83. package/dist/{chunk-EQOTJL76.js → chunk-PGFKIOR3.js} +3 -3
  84. package/dist/{chunk-D2YFQ7XG.js → chunk-POS53FG6.js} +2 -2
  85. package/dist/{chunk-ZY6P7M52.js → chunk-PQ6Z2TAP.js} +2 -2
  86. package/dist/{chunk-5OT4G5N5.js → chunk-PUJTKLPL.js} +3 -3
  87. package/dist/{chunk-64WHKBMP.js → chunk-QGJ4XIA7.js} +8 -8
  88. package/dist/{chunk-DECNV4QK.js → chunk-QHJKK77B.js} +6 -6
  89. package/dist/{chunk-JHYKSMVX.js → chunk-QI5NWLVW.js} +7 -7
  90. package/dist/{chunk-BZ3WMOYX.js → chunk-RC4BROYR.js} +3 -3
  91. package/dist/{chunk-IWQPYT6S.js → chunk-RDHUZ674.js} +4 -4
  92. package/dist/{chunk-UJ4Z67UQ.js → chunk-RK7N4OQ5.js} +1 -1
  93. package/dist/chunk-RK7N4OQ5.js.map +1 -0
  94. package/dist/{chunk-EHW7NLGD.js → chunk-S2VG3ABP.js} +3 -3
  95. package/dist/chunk-S2VG3ABP.js.map +1 -0
  96. package/dist/{chunk-7OBIBPCV.js → chunk-S35S77YU.js} +10 -10
  97. package/dist/{chunk-22GR3O4J.js → chunk-SHILAQH5.js} +2 -2
  98. package/dist/{chunk-WPZ5FVAS.js → chunk-T3PHCEN4.js} +3 -3
  99. package/dist/{chunk-GKTW3KLV.js → chunk-TOV6VMHF.js} +5 -5
  100. package/dist/chunk-TOV6VMHF.js.map +1 -0
  101. package/dist/{chunk-6RH7PRTL.js → chunk-TVIFAHZR.js} +6 -6
  102. package/dist/chunk-UBSFPGVJ.js +1 -0
  103. package/dist/{chunk-323MN67Z.js → chunk-UCUALOSM.js} +2 -2
  104. package/dist/{chunk-B4CRUZPR.js → chunk-VAG47QHV.js} +2 -2
  105. package/dist/{chunk-QH47SQLD.js → chunk-VFIWDPXZ.js} +3 -3
  106. package/dist/{chunk-K6CPIK3R.js → chunk-VTG7327U.js} +4 -4
  107. package/dist/chunk-VTG7327U.js.map +1 -0
  108. package/dist/{chunk-ONPVHX2Y.js → chunk-W5LHQXZU.js} +2 -2
  109. package/dist/{chunk-ZIMQDXTQ.js → chunk-WK5FSW7T.js} +8 -8
  110. package/dist/{chunk-PFWE7FJG.js → chunk-WLLUM35R.js} +2 -2
  111. package/dist/{chunk-IWKCJCLP.js → chunk-X62MEF2O.js} +5 -5
  112. package/dist/chunk-X62MEF2O.js.map +1 -0
  113. package/dist/{chunk-XPEPMK4Y.js → chunk-X67FCULK.js} +5 -5
  114. package/dist/chunk-X67FCULK.js.map +1 -0
  115. package/dist/{chunk-C3XWJCG7.js → chunk-XDVVBETV.js} +2 -2
  116. package/dist/chunk-XDVVBETV.js.map +1 -0
  117. package/dist/chunk-XJ3FEKEY.js +15 -0
  118. package/dist/chunk-XJ3FEKEY.js.map +1 -0
  119. package/dist/{chunk-EV2TDHQZ.js → chunk-XSN63FUS.js} +2 -2
  120. package/dist/{chunk-OR2FYRYR.js → chunk-XVU7J43V.js} +2 -2
  121. package/dist/{chunk-JYPRZWCZ.js → chunk-Y3YLJORD.js} +167 -133
  122. package/dist/chunk-Y3YLJORD.js.map +1 -0
  123. package/dist/{chunk-DNKVKO7K.js → chunk-Y6GZ2P4M.js} +2 -2
  124. package/dist/{chunk-ATY3ZASK.js → chunk-YCSM3GZU.js} +2 -2
  125. package/dist/{chunk-BMU2DS3S.js → chunk-YKPGQJFU.js} +12 -171
  126. package/dist/chunk-YKPGQJFU.js.map +1 -0
  127. package/dist/{chunk-RDB66EKD.js → chunk-YMLQBZFJ.js} +6 -6
  128. package/dist/{chunk-EHRIDNZX.js → chunk-YPSOHDNC.js} +2 -2
  129. package/dist/{chunk-KM6ZXG7U.js → chunk-YXA2TCTZ.js} +4 -4
  130. package/dist/{chunk-TKBB62DN.js → chunk-Z2PWOSCJ.js} +2 -2
  131. package/dist/{chunk-S2I5MYSA.js → chunk-Z2WK53N6.js} +42 -15
  132. package/dist/chunk-Z2WK53N6.js.map +1 -0
  133. package/dist/{chunk-5O5ZSW7N.js → chunk-Z7ZXZL6Y.js} +2 -2
  134. package/dist/{chunk-2ZCWHCLF.js → chunk-ZO252Q5S.js} +6 -6
  135. package/dist/classified/index.js +5 -5
  136. package/dist/{classified-marker-AZDSDG6V.js → classified-marker-BSQHU4JT.js} +3 -3
  137. package/dist/collection-facade-LG3NYZVB.js +45 -0
  138. package/dist/{computed-VG2PCM4L.js → computed-ASBNTLOK.js} +3 -3
  139. package/dist/consent/index.js +9 -9
  140. package/dist/cover/index.js +9 -9
  141. package/dist/custody/index.js +12 -12
  142. package/dist/{dead-filter-JNQNB3TA.js → dead-filter-QKU56XTQ.js} +2 -2
  143. package/dist/debug/index.js +1 -1
  144. package/dist/{delegation-5F36DRDH.js → delegation-XWRPYODW.js} +11 -11
  145. package/dist/derivations/index.js +12 -12
  146. package/dist/derive-CXHCY46K.js +22 -0
  147. package/dist/directory/index.js +10 -10
  148. package/dist/{dispatch-3ZA733EY.js → dispatch-4AP4PUZU.js} +18 -18
  149. package/dist/{dispatch-TNM5CRLZ.js → dispatch-VR4LRXZ7.js} +5 -5
  150. package/dist/{enclave-Y2ALNDRD.js → enclave-NYPT7WA7.js} +9 -9
  151. package/dist/executor-3KFRQETS.js +29 -0
  152. package/dist/executor-AFHQYRAU.js +9 -0
  153. package/dist/executor-YYDW6XQI.js +9 -0
  154. package/dist/export-accessible-BX2ILB4C.js +23 -0
  155. package/dist/extract-partition-UPOVITFI.js +38 -0
  156. package/dist/{fanout-sidecar-3QZU7ZZX.js → fanout-sidecar-IWP3R4RC.js} +9 -9
  157. package/dist/{fence-watcher-DHQ775JB.js → fence-watcher-ZVSMLKQD.js} +1 -1
  158. package/dist/fence-watcher-ZVSMLKQD.js.map +1 -0
  159. package/dist/find-SZBL2HPB.js +11 -0
  160. package/dist/forget/index.js +9 -9
  161. package/dist/guards/index.js +7 -7
  162. package/dist/history/index.js +13 -13
  163. package/dist/i18n/index.js +14 -14
  164. package/dist/index.d.ts +5 -5
  165. package/dist/index.js +134 -131
  166. package/dist/index.js.map +1 -1
  167. package/dist/indexing/index.js +2 -2
  168. package/dist/introspection/index.js +12 -11
  169. package/dist/issue-G53NB6EW.js +19 -0
  170. package/dist/kernel/collection-config.d.ts +9 -9
  171. package/dist/kernel/collection.d.ts +1 -1
  172. package/dist/kernel/errors.d.ts +2 -2
  173. package/dist/kernel/noydb.d.ts +6 -6
  174. package/dist/kernel/secret-mode.d.ts +1 -1
  175. package/dist/kernel/types.d.ts +34 -15
  176. package/dist/kernel/vault.d.ts +10 -1
  177. package/dist/kernel/via/compose.d.ts +3 -3
  178. package/dist/kernel/via/index.d.ts +4 -4
  179. package/dist/kernel/via/pipeline.d.ts +6 -6
  180. package/dist/kernel/via/reconcile.d.ts +2 -2
  181. package/dist/kernel/via/taint-binding.d.ts +3 -3
  182. package/dist/{ledger-MQIIOTZB.js → ledger-T5K4P2D7.js} +11 -11
  183. package/dist/liberate-VEOZRESE.js +25 -0
  184. package/dist/{link-set-5PJBBR63.js → link-set-76JZ6YB4.js} +11 -11
  185. package/dist/{managed-secret-ETDG6AAE.js → managed-secret-6YC32VXG.js} +12 -12
  186. package/dist/materialized-views/index.js +25 -25
  187. package/dist/{migrate-cek-V3VDRV24.js → migrate-cek-LBRKYUZZ.js} +2 -2
  188. package/dist/money/index.js +4 -4
  189. package/dist/noydb-EDJA4JNJ.js +86 -0
  190. package/dist/on/index.js +1 -0
  191. package/dist/overlay-views/index.js +4 -4
  192. package/dist/periods/index.js +12 -12
  193. package/dist/pod/index.js +58 -57
  194. package/dist/{pod-handle-AZ3CXY5Y.js → pod-handle-P6DPBM5U.js} +9 -9
  195. package/dist/policy/index.js +10 -10
  196. package/dist/port/as/active.d.ts +10 -0
  197. package/dist/port/as/index.d.ts +32 -0
  198. package/dist/port/as/types.d.ts +113 -0
  199. package/dist/port/at/index.d.ts +37 -0
  200. package/dist/port/by/default-provider.d.ts +3 -3
  201. package/dist/port/by/index.d.ts +32 -10
  202. package/dist/port/by/types.d.ts +29 -11
  203. package/dist/port/on/index.d.ts +50 -0
  204. package/dist/port/with/computed-strategy.d.ts +1 -1
  205. package/dist/port/with/device-seal-strategy.d.ts +2 -2
  206. package/dist/port/with/formats-strategy.d.ts +76 -0
  207. package/dist/port/with/strategies.d.ts +11 -5
  208. package/dist/portability/index.js +8 -8
  209. package/dist/{post-register-QCJYRMIK.js → post-register-53O7AJRZ.js} +6 -6
  210. package/dist/query/index.js +3 -3
  211. package/dist/reduce/index.js +4 -4
  212. package/dist/register-6TCBR6XD.js +32 -0
  213. package/dist/registry-DH2L4O2V.js +18 -0
  214. package/dist/registry-IWFMXCHX.js +9 -0
  215. package/dist/registry-Q3FG3OAX.js +9 -0
  216. package/dist/registry-YC3IFG3A.js +19 -0
  217. package/dist/request-withdrawal-NM5VYR6F.js +31 -0
  218. package/dist/{reveal-W7BKZSUP.js → reveal-XBIMHAXR.js} +6 -6
  219. package/dist/revoke-MJNYWO72.js +24 -0
  220. package/dist/satellites/index.js +1 -1
  221. package/dist/schema-update/index.js +2 -2
  222. package/dist/sealed-record/index.js +12 -12
  223. package/dist/search/index.js +4 -4
  224. package/dist/{seed-55FPOMYP.js → seed-NSNIGYXE.js} +12 -12
  225. package/dist/sequence/index.js +9 -9
  226. package/dist/session/index.js +9 -9
  227. package/dist/shadow/index.js +2 -2
  228. package/dist/signer-6XVS2AU3.js +25 -0
  229. package/dist/snapshots/index.js +10 -10
  230. package/dist/{stale-TH7SSVRV.js → stale-MCS46TFO.js} +10 -10
  231. package/dist/stale-MCS46TFO.js.map +1 -0
  232. package/dist/storage-ZXUT476I.js +23 -0
  233. package/dist/storage-ZXUT476I.js.map +1 -0
  234. package/dist/{store-coordination-provider-R27VRD6K.js → store-coordination-provider-6FM26VQM.js} +14 -13
  235. package/dist/store-coordination-provider-6FM26VQM.js.map +1 -0
  236. package/dist/sync/index.js +10 -10
  237. package/dist/team/index.js +17 -17
  238. package/dist/tiers/index.js +11 -11
  239. package/dist/to/index.js +1 -1
  240. package/dist/transactions/index.js +3 -3
  241. package/dist/util/index.js +1 -1
  242. package/dist/vault-diff-FE5J35NJ.js +9 -0
  243. package/dist/vault-diff-FE5J35NJ.js.map +1 -0
  244. package/dist/vault-head/index.js +8 -8
  245. package/dist/{verify-M5LQH43C.js → verify-WYOFLSNA.js} +5 -5
  246. package/dist/via/blob/binding.d.ts +6 -6
  247. package/dist/via/classified/binding.d.ts +9 -9
  248. package/dist/via/computed/binding.d.ts +2 -2
  249. package/dist/via/i18n/binding.d.ts +4 -4
  250. package/dist/via/lookup/binding.d.ts +5 -5
  251. package/dist/via/lookup/index.d.ts +1 -1
  252. package/dist/via/lookup/snapshot.d.ts +1 -1
  253. package/dist/via/money/binding.d.ts +3 -3
  254. package/dist/via/money/normalize.d.ts +1 -1
  255. package/dist/via/money/where.d.ts +3 -3
  256. package/dist/walk-JNEOYCAU.js +21 -0
  257. package/dist/walk-JNEOYCAU.js.map +1 -0
  258. package/dist/with-cargo/adopt-partition.d.ts +6 -6
  259. package/dist/with-cargo/floor.d.ts +1 -1
  260. package/dist/with-cargo/index.d.ts +1 -1
  261. package/dist/with-party/team/deed.d.ts +4 -4
  262. package/dist/with-party/team/device-seal.d.ts +5 -5
  263. package/dist/with-party/team/echo-ceremony.d.ts +2 -2
  264. package/dist/with-party/team/echo-secret.d.ts +4 -4
  265. package/dist/with-party/team/index.d.ts +2 -2
  266. package/dist/with-party/team/keyring.d.ts +2 -2
  267. package/dist/with-party/team/managed-secret.d.ts +21 -11
  268. package/dist/with-party/team/noydb-facade.d.ts +2 -2
  269. package/dist/with-party/team/{shamir-recovery-provider.d.ts → noydb-shamir.d.ts} +1 -1
  270. package/dist/with-party/team/recovery.d.ts +3 -3
  271. package/dist/with-party/team/rotate-recover.d.ts +5 -5
  272. package/dist/with-pod/format.d.ts +1 -1
  273. package/dist/with-pod/pod.d.ts +6 -6
  274. package/dist/with-shape/introspection/describe.d.ts +2 -2
  275. package/dist/with-shape/schema-update/fence-controller.d.ts +4 -4
  276. package/dist/with-shape/schema-update/fence-watcher.d.ts +4 -4
  277. package/dist/with-shape/schema-update/store-coordination-provider.d.ts +7 -6
  278. package/dist/withdraw-accessible-ILTSXTIE.js +28 -0
  279. package/dist/withdraw-accessible-ILTSXTIE.js.map +1 -0
  280. package/package.json +23 -5
  281. package/dist/api-6X2VLZ7J.js +0 -20
  282. package/dist/chunk-AJGGBXWA.js.map +0 -1
  283. package/dist/chunk-BMU2DS3S.js.map +0 -1
  284. package/dist/chunk-C3XWJCG7.js.map +0 -1
  285. package/dist/chunk-EHW7NLGD.js.map +0 -1
  286. package/dist/chunk-GCU42IG7.js.map +0 -1
  287. package/dist/chunk-GKTW3KLV.js.map +0 -1
  288. package/dist/chunk-IWKCJCLP.js.map +0 -1
  289. package/dist/chunk-J6I5VR6P.js.map +0 -1
  290. package/dist/chunk-JYPRZWCZ.js.map +0 -1
  291. package/dist/chunk-K6CPIK3R.js.map +0 -1
  292. package/dist/chunk-LC6NE4IQ.js.map +0 -1
  293. package/dist/chunk-LIR2DJWP.js.map +0 -1
  294. package/dist/chunk-PJX72ICF.js.map +0 -1
  295. package/dist/chunk-S2I5MYSA.js.map +0 -1
  296. package/dist/chunk-UJ4Z67UQ.js.map +0 -1
  297. package/dist/chunk-XINT4VJA.js.map +0 -1
  298. package/dist/chunk-XPEPMK4Y.js.map +0 -1
  299. package/dist/chunk-YTUFV7U7.js.map +0 -1
  300. package/dist/chunk-Z3BZXPRO.js.map +0 -1
  301. package/dist/collection-facade-WSUAYFJ4.js +0 -45
  302. package/dist/derive-MPOGZ4NR.js +0 -22
  303. package/dist/executor-BBEEUFO3.js +0 -9
  304. package/dist/executor-G6G3YEWR.js +0 -9
  305. package/dist/executor-ZUTUMTPA.js +0 -29
  306. package/dist/export-accessible-S3KEB763.js +0 -23
  307. package/dist/extract-partition-KVCTSUVE.js +0 -38
  308. package/dist/fence-watcher-DHQ775JB.js.map +0 -1
  309. package/dist/find-V4FN5ITT.js +0 -11
  310. package/dist/issue-5CFBWBRX.js +0 -19
  311. package/dist/liberate-LW7OKVBH.js +0 -25
  312. package/dist/noydb-LFDRMYZ5.js +0 -85
  313. package/dist/register-JBOEHTVX.js +0 -32
  314. package/dist/registry-5DYHQFJS.js +0 -9
  315. package/dist/registry-EANV44W2.js +0 -9
  316. package/dist/registry-J25H6VB4.js +0 -19
  317. package/dist/registry-R23SQUQH.js +0 -18
  318. package/dist/request-withdrawal-KQ4W4UT5.js +0 -31
  319. package/dist/revoke-H3VSTAIE.js +0 -24
  320. package/dist/signer-53AQ3BU2.js +0 -25
  321. package/dist/storage-WSF7ZKTL.js +0 -23
  322. package/dist/store-coordination-provider-R27VRD6K.js.map +0 -1
  323. package/dist/walk-NABCHDZN.js +0 -21
  324. package/dist/withdraw-accessible-VCINIG6V.js +0 -28
  325. /package/dist/{api-6X2VLZ7J.js.map → api-EQYRTZO2.js.map} +0 -0
  326. /package/dist/{collection-facade-WSUAYFJ4.js.map → at/index.js.map} +0 -0
  327. /package/dist/{backup-VL4GRIEW.js.map → backup-Z7ZBTAQ6.js.map} +0 -0
  328. /package/dist/{computed-VG2PCM4L.js.map → by/index.js.map} +0 -0
  329. /package/dist/{chunk-JKATP6OE.js.map → chunk-225O5CXQ.js.map} +0 -0
  330. /package/dist/{chunk-CXPOG3ZY.js.map → chunk-2LPVI4PX.js.map} +0 -0
  331. /package/dist/{chunk-2J5IT37K.js.map → chunk-3L2MVE36.js.map} +0 -0
  332. /package/dist/{chunk-HOPNIR5R.js.map → chunk-3QARDAYW.js.map} +0 -0
  333. /package/dist/{chunk-HSJBJIB2.js.map → chunk-467SKJ65.js.map} +0 -0
  334. /package/dist/{chunk-YCGLAANM.js.map → chunk-4A4TYISP.js.map} +0 -0
  335. /package/dist/{chunk-2CBFYXJX.js.map → chunk-4G25SDQL.js.map} +0 -0
  336. /package/dist/{chunk-5LZZQMIC.js.map → chunk-4LPF226H.js.map} +0 -0
  337. /package/dist/{chunk-GANFIM2M.js.map → chunk-4ZPE6SKS.js.map} +0 -0
  338. /package/dist/{chunk-65JXA63J.js.map → chunk-5263OEM3.js.map} +0 -0
  339. /package/dist/{chunk-FNYCT27B.js.map → chunk-57JTHWLQ.js.map} +0 -0
  340. /package/dist/{chunk-5IHGJHWC.js.map → chunk-5ACAVJUI.js.map} +0 -0
  341. /package/dist/{chunk-3OE2QNSM.js.map → chunk-5AFMP532.js.map} +0 -0
  342. /package/dist/{chunk-7ZGPQOFV.js.map → chunk-6EW2UCVF.js.map} +0 -0
  343. /package/dist/{chunk-36CEG6CM.js.map → chunk-7LYUJNU3.js.map} +0 -0
  344. /package/dist/{chunk-D6SXRMZZ.js.map → chunk-A2YW5EMU.js.map} +0 -0
  345. /package/dist/{chunk-OQMCFHUX.js.map → chunk-AB5P32RE.js.map} +0 -0
  346. /package/dist/{chunk-JMJSCI7U.js.map → chunk-ALB64CZM.js.map} +0 -0
  347. /package/dist/{chunk-RPRASGEY.js.map → chunk-ALUNQEBY.js.map} +0 -0
  348. /package/dist/{chunk-ZYZJLYNS.js.map → chunk-ATV637J7.js.map} +0 -0
  349. /package/dist/{chunk-W3HAIN6U.js.map → chunk-CN6B5PEN.js.map} +0 -0
  350. /package/dist/{chunk-JIHWBKMQ.js.map → chunk-DBNYPLWY.js.map} +0 -0
  351. /package/dist/{chunk-ZJUZOJVW.js.map → chunk-DFAUMLD7.js.map} +0 -0
  352. /package/dist/{chunk-S6PJ6ZKE.js.map → chunk-DKWPJOYH.js.map} +0 -0
  353. /package/dist/{chunk-MPKP6L5E.js.map → chunk-E6YA6UAP.js.map} +0 -0
  354. /package/dist/{chunk-S7PM7Z4K.js.map → chunk-F7ZFRPGQ.js.map} +0 -0
  355. /package/dist/{chunk-CKKN25P2.js.map → chunk-FOG7YEJP.js.map} +0 -0
  356. /package/dist/{chunk-4ATF4BLF.js.map → chunk-H36KSN6X.js.map} +0 -0
  357. /package/dist/{chunk-QQRRKO7W.js.map → chunk-H7HP56F6.js.map} +0 -0
  358. /package/dist/{chunk-RJZQ2PVD.js.map → chunk-HM4JSXX2.js.map} +0 -0
  359. /package/dist/{chunk-3YGUHUJA.js.map → chunk-HTGRQZFB.js.map} +0 -0
  360. /package/dist/{chunk-JXHNUYQO.js.map → chunk-IESNYJXZ.js.map} +0 -0
  361. /package/dist/{chunk-OCJ3JA4O.js.map → chunk-J4MN6XIU.js.map} +0 -0
  362. /package/dist/{chunk-HWI7GHJZ.js.map → chunk-J4UJ5FRV.js.map} +0 -0
  363. /package/dist/{chunk-QVXZFJBU.js.map → chunk-JVLSU7S2.js.map} +0 -0
  364. /package/dist/{chunk-7Q3RHOA5.js.map → chunk-KDJGJR7F.js.map} +0 -0
  365. /package/dist/{chunk-M7WBE3OR.js.map → chunk-L6GTLZRK.js.map} +0 -0
  366. /package/dist/{chunk-2ZT4INZ4.js.map → chunk-LF6DMX4C.js.map} +0 -0
  367. /package/dist/{chunk-4K6BIMLF.js.map → chunk-LKFKXS5K.js.map} +0 -0
  368. /package/dist/{chunk-POFYIGQ2.js.map → chunk-LR4Z47QD.js.map} +0 -0
  369. /package/dist/{chunk-LUYPYCHY.js.map → chunk-MTBXJAS4.js.map} +0 -0
  370. /package/dist/{chunk-FX3UIDCS.js.map → chunk-N4CNVSUF.js.map} +0 -0
  371. /package/dist/{chunk-FU5VYEYK.js.map → chunk-NLKLXXTO.js.map} +0 -0
  372. /package/dist/{chunk-YYV53EHO.js.map → chunk-OPXR4PB2.js.map} +0 -0
  373. /package/dist/{chunk-5F7BZAGV.js.map → chunk-OWLVBOWO.js.map} +0 -0
  374. /package/dist/{chunk-6NR36UNB.js.map → chunk-P6ASQOZR.js.map} +0 -0
  375. /package/dist/{chunk-5ATC6UT4.js.map → chunk-PCQDY6QN.js.map} +0 -0
  376. /package/dist/{chunk-EQOTJL76.js.map → chunk-PGFKIOR3.js.map} +0 -0
  377. /package/dist/{chunk-D2YFQ7XG.js.map → chunk-POS53FG6.js.map} +0 -0
  378. /package/dist/{chunk-ZY6P7M52.js.map → chunk-PQ6Z2TAP.js.map} +0 -0
  379. /package/dist/{chunk-5OT4G5N5.js.map → chunk-PUJTKLPL.js.map} +0 -0
  380. /package/dist/{chunk-64WHKBMP.js.map → chunk-QGJ4XIA7.js.map} +0 -0
  381. /package/dist/{chunk-DECNV4QK.js.map → chunk-QHJKK77B.js.map} +0 -0
  382. /package/dist/{chunk-JHYKSMVX.js.map → chunk-QI5NWLVW.js.map} +0 -0
  383. /package/dist/{chunk-BZ3WMOYX.js.map → chunk-RC4BROYR.js.map} +0 -0
  384. /package/dist/{chunk-IWQPYT6S.js.map → chunk-RDHUZ674.js.map} +0 -0
  385. /package/dist/{chunk-7OBIBPCV.js.map → chunk-S35S77YU.js.map} +0 -0
  386. /package/dist/{chunk-22GR3O4J.js.map → chunk-SHILAQH5.js.map} +0 -0
  387. /package/dist/{chunk-WPZ5FVAS.js.map → chunk-T3PHCEN4.js.map} +0 -0
  388. /package/dist/{chunk-6RH7PRTL.js.map → chunk-TVIFAHZR.js.map} +0 -0
  389. /package/dist/{derive-MPOGZ4NR.js.map → chunk-UBSFPGVJ.js.map} +0 -0
  390. /package/dist/{chunk-323MN67Z.js.map → chunk-UCUALOSM.js.map} +0 -0
  391. /package/dist/{chunk-B4CRUZPR.js.map → chunk-VAG47QHV.js.map} +0 -0
  392. /package/dist/{chunk-QH47SQLD.js.map → chunk-VFIWDPXZ.js.map} +0 -0
  393. /package/dist/{chunk-ONPVHX2Y.js.map → chunk-W5LHQXZU.js.map} +0 -0
  394. /package/dist/{chunk-ZIMQDXTQ.js.map → chunk-WK5FSW7T.js.map} +0 -0
  395. /package/dist/{chunk-PFWE7FJG.js.map → chunk-WLLUM35R.js.map} +0 -0
  396. /package/dist/{chunk-EV2TDHQZ.js.map → chunk-XSN63FUS.js.map} +0 -0
  397. /package/dist/{chunk-OR2FYRYR.js.map → chunk-XVU7J43V.js.map} +0 -0
  398. /package/dist/{chunk-DNKVKO7K.js.map → chunk-Y6GZ2P4M.js.map} +0 -0
  399. /package/dist/{chunk-ATY3ZASK.js.map → chunk-YCSM3GZU.js.map} +0 -0
  400. /package/dist/{chunk-RDB66EKD.js.map → chunk-YMLQBZFJ.js.map} +0 -0
  401. /package/dist/{chunk-EHRIDNZX.js.map → chunk-YPSOHDNC.js.map} +0 -0
  402. /package/dist/{chunk-KM6ZXG7U.js.map → chunk-YXA2TCTZ.js.map} +0 -0
  403. /package/dist/{chunk-TKBB62DN.js.map → chunk-Z2PWOSCJ.js.map} +0 -0
  404. /package/dist/{chunk-5O5ZSW7N.js.map → chunk-Z7ZXZL6Y.js.map} +0 -0
  405. /package/dist/{chunk-2ZCWHCLF.js.map → chunk-ZO252Q5S.js.map} +0 -0
  406. /package/dist/{classified-marker-AZDSDG6V.js.map → classified-marker-BSQHU4JT.js.map} +0 -0
  407. /package/dist/{enclave-Y2ALNDRD.js.map → collection-facade-LG3NYZVB.js.map} +0 -0
  408. /package/dist/{executor-BBEEUFO3.js.map → computed-ASBNTLOK.js.map} +0 -0
  409. /package/dist/{dead-filter-JNQNB3TA.js.map → dead-filter-QKU56XTQ.js.map} +0 -0
  410. /package/dist/{delegation-5F36DRDH.js.map → delegation-XWRPYODW.js.map} +0 -0
  411. /package/dist/{executor-G6G3YEWR.js.map → derive-CXHCY46K.js.map} +0 -0
  412. /package/dist/{dispatch-3ZA733EY.js.map → dispatch-4AP4PUZU.js.map} +0 -0
  413. /package/dist/{dispatch-TNM5CRLZ.js.map → dispatch-VR4LRXZ7.js.map} +0 -0
  414. /package/dist/{executor-ZUTUMTPA.js.map → enclave-NYPT7WA7.js.map} +0 -0
  415. /package/dist/{export-accessible-S3KEB763.js.map → executor-3KFRQETS.js.map} +0 -0
  416. /package/dist/{extract-partition-KVCTSUVE.js.map → executor-AFHQYRAU.js.map} +0 -0
  417. /package/dist/{find-V4FN5ITT.js.map → executor-YYDW6XQI.js.map} +0 -0
  418. /package/dist/{issue-5CFBWBRX.js.map → export-accessible-BX2ILB4C.js.map} +0 -0
  419. /package/dist/{ledger-MQIIOTZB.js.map → extract-partition-UPOVITFI.js.map} +0 -0
  420. /package/dist/{fanout-sidecar-3QZU7ZZX.js.map → fanout-sidecar-IWP3R4RC.js.map} +0 -0
  421. /package/dist/{liberate-LW7OKVBH.js.map → find-SZBL2HPB.js.map} +0 -0
  422. /package/dist/{link-set-5PJBBR63.js.map → issue-G53NB6EW.js.map} +0 -0
  423. /package/dist/{managed-secret-ETDG6AAE.js.map → ledger-T5K4P2D7.js.map} +0 -0
  424. /package/dist/{noydb-LFDRMYZ5.js.map → liberate-VEOZRESE.js.map} +0 -0
  425. /package/dist/{register-JBOEHTVX.js.map → link-set-76JZ6YB4.js.map} +0 -0
  426. /package/dist/{registry-5DYHQFJS.js.map → managed-secret-6YC32VXG.js.map} +0 -0
  427. /package/dist/{migrate-cek-V3VDRV24.js.map → migrate-cek-LBRKYUZZ.js.map} +0 -0
  428. /package/dist/{registry-EANV44W2.js.map → noydb-EDJA4JNJ.js.map} +0 -0
  429. /package/dist/{registry-J25H6VB4.js.map → on/index.js.map} +0 -0
  430. /package/dist/{pod-handle-AZ3CXY5Y.js.map → pod-handle-P6DPBM5U.js.map} +0 -0
  431. /package/dist/{post-register-QCJYRMIK.js.map → post-register-53O7AJRZ.js.map} +0 -0
  432. /package/dist/{registry-R23SQUQH.js.map → register-6TCBR6XD.js.map} +0 -0
  433. /package/dist/{request-withdrawal-KQ4W4UT5.js.map → registry-DH2L4O2V.js.map} +0 -0
  434. /package/dist/{revoke-H3VSTAIE.js.map → registry-IWFMXCHX.js.map} +0 -0
  435. /package/dist/{signer-53AQ3BU2.js.map → registry-Q3FG3OAX.js.map} +0 -0
  436. /package/dist/{stale-TH7SSVRV.js.map → registry-YC3IFG3A.js.map} +0 -0
  437. /package/dist/{storage-WSF7ZKTL.js.map → request-withdrawal-NM5VYR6F.js.map} +0 -0
  438. /package/dist/{reveal-W7BKZSUP.js.map → reveal-XBIMHAXR.js.map} +0 -0
  439. /package/dist/{walk-NABCHDZN.js.map → revoke-MJNYWO72.js.map} +0 -0
  440. /package/dist/{seed-55FPOMYP.js.map → seed-NSNIGYXE.js.map} +0 -0
  441. /package/dist/{withdraw-accessible-VCINIG6V.js.map → signer-6XVS2AU3.js.map} +0 -0
  442. /package/dist/{verify-M5LQH43C.js.map → verify-WYOFLSNA.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-pod/format.ts","../src/with-pod/signature.ts","../src/with-pod/pod.ts"],"sourcesContent":["/**\n * `.noydb` container format — byte layout, header schema, validators.\n *\n *. Wraps a `vault.dump()` JSON string in a thin\n * binary container with a magic-byte prefix, a minimum-disclosure\n * unencrypted header, and a compressed body.\n *\n * **Byte layout** (read in order from offset 0):\n *\n * ```\n * +--------+--------+--------+--------+\n * | N=78 | D=68 | B=66 | 1=49 | Magic 'NDB1' (4 bytes)\n * +--------+--------+--------+--------+\n * | flags | compr | header_length (uint32 BE) |\n * +--------+--------+--------+--------+--------+--------+--------+\n * | header_length bytes of UTF-8 JSON header ...\n * +--------+--------+\n * | compressed body bytes ...\n * ```\n *\n * Total fixed prefix before the header JSON is **10 bytes**:\n * - 4 bytes magic\n * - 1 byte flags\n * - 1 byte compression algorithm\n * - 4 bytes header length (uint32 big-endian)\n *\n * **Why a binary container** at all? `vault.dump()` already\n * produces a JSON string with encrypted records inside. Wrapping it\n * again seems redundant — but the wrap is what makes the file safe\n * to drop into cloud storage (Drive, Dropbox, iCloud) without\n * leaking the vault name and exporter identity through the\n * cloud's metadata API. The minimum-disclosure header is the only\n * thing visible without downloading and decompressing the body.\n * The dump JSON inside the body still contains the original\n * metadata, but that's only readable by someone who already has the\n * file bytes — the same person who could read the encrypted records\n * with the right secret.\n *\n * **Why minimum disclosure** in the header? Because consumers will\n * inevitably store these in services where the filename, file size,\n * and any unencrypted metadata are indexed for search. A field like\n * `vault: \"Acme Corp\"` would let an attacker (or a curious\n * cloud admin) enumerate which compartments exist and who exported\n * them, even with zero access to the encrypted body. The header\n * carries only what's needed to identify the file as a NOYDB\n * bundle and verify its integrity — nothing about the contents.\n */\n\nimport type { Cover } from '../with-party/directory/cover/types.js'\nimport type { Redirect } from './redirect.js'\n\n/** Magic bytes 'NDB1' (ASCII) — `NoyDB 1` — identifying a NOYDB pod (`.noydb` container). */\nexport const NOYDB_POD_MAGIC = new Uint8Array([0x4e, 0x44, 0x42, 0x31])\n\n/** Total fixed prefix before the header JSON: 4+1+1+4 bytes. */\nexport const NOYDB_POD_PREFIX_BYTES = 10\n\n/** Current bundle format version. Bumped on layout changes. Default for unsigned pods. */\nexport const NOYDB_POD_FORMAT_VERSION = 1\n\n/** Format version for pods carrying a header signature (`sig`/`keyId`/`sigAlg`). */\nexport const NOYDB_POD_FORMAT_VERSION_SIGNED = 2\n\n/**\n * Bitfield interpretation of the flags byte.\n *\n * Bit 0 — body is compressed (0 = raw, 1 = compressed)\n * Bit 1 — header carries an integrity hash over the body bytes\n * Bits 2-7 — reserved, must be 0 in\n */\nexport const FLAG_COMPRESSED = 0b0000_0001\nexport const FLAG_HAS_INTEGRITY_HASH = 0b0000_0010\n\n/**\n * Compression algorithm encoding for the byte at offset 5.\n *\n * `none` is admitted for round-trip testing and for callers that\n * want to bundle without compression (e.g. when piping into a\n * separately compressed transport). `gzip` is the universally\n * available baseline (Node 18+, all modern browsers). `brotli` is\n * preferred when the runtime supports it — typically 30-50% smaller\n * for JSON payloads — but Node 22+ / Chrome 124+ / Firefox 122+\n * are required, so the writer feature-detects at runtime and falls\n * back to gzip. The reader must handle all three.\n */\nexport const COMPRESSION_NONE = 0\nexport const COMPRESSION_GZIP = 1\nexport const COMPRESSION_BROTLI = 2\n\nexport type CompressionAlgo = 0 | 1 | 2\n\n/**\n * The unencrypted header carried in every `.noydb` bundle.\n *\n * **Minimum-disclosure rules:** these are the ONLY allowed keys.\n * Any other key in a parsed header causes\n * `validatePodHeaderFields` to throw. The set is kept short to\n * minimize attack surface from cloud-storage metadata indexing —\n * see the file-level doc comment for the rationale.\n *\n * Forbidden in particular:\n * - `vault` / `_compartment` — would leak the tenant name\n * - `exporter` / `_exported_by` — would leak user identity\n * - `timestamp` / `_exported_at` — would leak activity timing\n * - `kdfParams` / salt fields — would leak crypto config that\n * could narrow brute-force search space\n * - any field starting with `_` (reserved by the dump format)\n */\nexport interface NoydbPodHeader {\n /** Bundle format version — bumped on layout changes. */\n readonly formatVersion: number\n /**\n * Opaque ULID identifier — generated once per vault and\n * stable across re-exports of the same vault. Does not\n * leak any information about contents (the timestamp prefix is\n * just monotonicity for sortability, not exporter activity —\n * see `bundle/ulid.ts` for the design notes).\n */\n readonly handle: string\n /** Compressed body length in bytes. Lets readers verify completeness without decompressing. */\n readonly bodyBytes: number\n /** SHA-256 of the compressed body bytes (lowercase hex). Lets readers verify integrity without decompressing. */\n readonly bodySha256: string\n /**\n * Owner-curated cover (`https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/public-envelope.md`).\n * Optional — present only when the source vault has a\n * `_meta/public-envelope` document AND the writer's hub is opted\n * into the feature. Treat as **untrusted hint**; the body's\n * encrypted contents remain the source of truth.\n *\n * The JSON key keeps its frozen wire name `publicEnvelope` (#799\n * renamed only the developer surface — existing bundles stay\n * readable byte-for-byte).\n *\n * The cover deliberately widens the minimum-disclosure rule\n * for explicit, owner-curated label fields (name, icon, …). Every\n * other unknown header key still rejects at parse time.\n */\n readonly publicEnvelope?: Cover\n /**\n * Auto-unlock material indicator. When present, the bundle\n * body wraps the dump JSON in a structure carrying per-user\n * secrets — either plaintext (`'unsealed'`, public-by-design)\n * or sealed under a `SealingKeyProvider` (`'sealed'`, requires\n * matching provider on the recipient side).\n *\n * Visible pre-decompression so cloud listing UIs can warn before\n * download: \"this bundle opens itself for anyone holding the file\"\n * (unsealed) or \"this bundle is sealed for a specific provider\"\n * (sealed).\n *\n * Absent → the body is a raw `vault.dump()` JSON string (the\n * the legacy shape; back-compatible).\n */\n readonly autoUnlock?: 'unsealed' | 'sealed'\n /**\n * Bundle's role in the source → destination lifecycle.\n * - omitted / 'snapshot' (default): backup/copy of an existing vault.\n * - 'extracted-partition': re-keyed projection awaiting adoption.\n */\n readonly bundleKind?: 'snapshot' | 'extracted-partition'\n /**\n * Transfer-seal INDICATOR — metadata only, no payload (the\n * sealed DEKs live in the body). Present iff\n * bundleKind === 'extracted-partition'.\n */\n readonly transferSeal?: {\n readonly v: 1\n readonly alg: 'aes-256-gcm-pre-shared'\n readonly sealId: string\n }\n /**\n * Non-secret 16-hex-char fingerprint identifying the signing key\n * used for `sig`. A hash prefix, not the key itself — discloses no\n * crypto config, safe under minimum-disclosure. Present iff `sig`\n * and `sigAlg` are also present (all-or-nothing 3-tuple), and only\n * on `formatVersion === 2` headers.\n */\n readonly keyId?: string\n /**\n * Signature authenticating the rest of the header (base64url,\n * no padding). Verifies the header wasn't tampered with in\n * transit/storage — see `with-pod/signature.ts`. Part of the\n * sig/keyId/sigAlg 3-tuple.\n */\n readonly sig?: string\n /**\n * Signature algorithm for `sig`. Named `sigAlg` (not `alg`) to\n * avoid confusion with `transferSeal.alg`. Part of the\n * sig/keyId/sigAlg 3-tuple.\n */\n readonly sigAlg?: 'ed25519'\n /**\n * Engine version range that wrote (and can read) this pod, e.g.\n * `'^0.5.0'`. Discloses only a semver range — no timestamps, no\n * identities — so a version-skew triage step (open a decade-old\n * pod → route to a version-matched read-only player instead of a\n * hard failure) or a landing page can branch before any library\n * code runs. Safe under minimum-disclosure: a range says nothing\n * about who wrote the pod or when.\n */\n readonly engineRange?: string\n /**\n * Declared unlock method(s), e.g. `['password', 'webauthn']`. Lets\n * a landing/player present the right unlock UI pre-library, and is\n * ALL a shared connection pod may disclose about its bootstrap\n * (auth method + endpoint class). Safe under minimum-disclosure: it\n * names a method family, not a credential, key, or identity.\n *\n * The member set mirrors the `@noy-db/on-*` package family\n * (`on-password`, `on-webauthn`, `on-oidc`, `on-totp`,\n * `on-email-otp`, `on-magic-link`, `on-pin`, `on-recovery`,\n * `on-shamir`) — the top-level \"which on-* package to load\" method\n * name, not the finer-grained `FactorKind` used by policy gates\n * (which splits WebAuthn into `webauthn-roaming` /\n * `webauthn-platform` for factor-proof freshness). `on-threat` is\n * excluded — it's a lockout/duress helper, not an unlock method a\n * landing page would present.\n */\n readonly unlockMethods?: readonly UnlockMethod[]\n /**\n * App-manifest presence flag. Lets a landing fork between\n * \"orphan pod → read-only browse\" and \"linked app → launch flow\"\n * without reading the body. Safe under minimum-disclosure: a\n * boolean, not the app's identity or contents.\n */\n readonly hasApp?: boolean\n /**\n * Artifact species hint for dispatcher branching, cloud tooling,\n * and the share-flow guard that warns when a data-bearing pod is\n * shared where a pointer was intended. An \"orphan\" pod is derived\n * (`species: 'full'` + `hasApp: false`), not a separate species.\n * Safe under minimum-disclosure: a coarse artifact-shape label, not\n * contents or identity.\n */\n readonly species?: 'full' | 'connection' | 'snapshot' | 'redirect' | 'group'\n /**\n * Whether the app pointer (if any) is disclosed pre-auth. Default\n * is `'private'` by product decision (recorded in #942) — absence\n * means private by convention; this field is never auto-injected,\n * only an author's explicit opt-in writes `'public'`. Safe under\n * minimum-disclosure: a disclosure-policy flag, not the pointer\n * itself.\n */\n readonly pointerMode?: 'public' | 'private'\n /**\n * Signed \"this moved, go there\" pointer (#944). Carried in the\n * PLAINTEXT header (not the encrypted body) so a dispatcher can follow\n * it pre-auth — no secret, no decompression. Structurally validated\n * here (shape only); the record's OWN signature is a separate\n * verification step (`verifyRedirect`) since a parser has no\n * `trustedKeys`. `readPodRedirect` returns this field UNVERIFIED.\n */\n readonly redirect?: Redirect\n}\n\n/**\n * Declared unlock method names — mirrors the `@noy-db/on-*` package\n * family (see `unlockMethods` doc comment on `NoydbPodHeader`).\n */\nexport type UnlockMethod =\n | 'password'\n | 'webauthn'\n | 'oidc'\n | 'totp'\n | 'email-otp'\n | 'magic-link'\n | 'pin'\n | 'recovery'\n | 'shamir'\n\n\n/**\n * Allowlist of header keys. Any key not in this set is forbidden\n * and causes `validatePodHeaderFields` to throw. Kept as a Set for\n * O(1) lookup; the validator iterates over the parsed header and\n * checks each key against this set.\n */\nconst ALLOWED_HEADER_KEYS: ReadonlySet<string> = new Set([\n 'formatVersion',\n 'handle',\n 'bodyBytes',\n 'bodySha256',\n 'publicEnvelope',\n 'autoUnlock',\n 'bundleKind',\n 'transferSeal',\n 'sig',\n 'keyId',\n 'sigAlg',\n 'engineRange',\n 'unlockMethods',\n 'hasApp',\n 'species',\n 'pointerMode',\n 'redirect',\n])\n\n/** Valid `unlockMethods` member values — see `UnlockMethod`. */\nconst UNLOCK_METHODS: ReadonlySet<string> = new Set([\n 'password',\n 'webauthn',\n 'oidc',\n 'totp',\n 'email-otp',\n 'magic-link',\n 'pin',\n 'recovery',\n 'shamir',\n])\n\n/** Valid `species` values — see `NoydbPodHeader.species`. */\nconst POD_SPECIES: ReadonlySet<string> = new Set([\n 'full',\n 'connection',\n 'snapshot',\n 'redirect',\n 'group',\n])\n\n/** Valid `redirect.reason` values — see `Redirect['reason']`. */\nconst REDIRECT_REASONS: ReadonlySet<string> = new Set([\n 'moved',\n 'release',\n 'tombstone',\n 'repoint',\n])\n\n/**\n * Validate a parsed bundle header. Throws on any deviation from\n * the minimum-disclosure schema:\n *\n * - Missing required field\n * - Wrong type for any field\n * - Any extra key not in `ALLOWED_HEADER_KEYS`\n * - Unsupported `formatVersion`\n * - Negative or non-integer `bodyBytes`\n * - Malformed `handle` (must be 26-char Crockford base32)\n * - Malformed `bodySha256` (must be 64-char lowercase hex)\n *\n * The error messages name the offending field so consumers can\n * fix the producer rather than the reader.\n */\nexport function validatePodHeaderFields(\n parsed: unknown,\n): asserts parsed is NoydbPodHeader {\n if (parsed === null || typeof parsed !== 'object') {\n throw new Error(\n `.noydb bundle header must be a JSON object, got ${parsed === null ? 'null' : typeof parsed}`,\n )\n }\n // Disallow any unknown key — minimum disclosure means we reject\n // forward-compat extension keys at the format layer; new fields\n // require a format version bump and a new validator.\n for (const key of Object.keys(parsed)) {\n if (!ALLOWED_HEADER_KEYS.has(key)) {\n throw new Error(\n `.noydb bundle header contains forbidden key \"${key}\". ` +\n `Only minimum-disclosure fields are allowed: ` +\n `${[...ALLOWED_HEADER_KEYS].join(', ')}.`,\n )\n }\n }\n const h = parsed as Record<string, unknown>\n if (\n typeof h['formatVersion'] !== 'number' ||\n (h['formatVersion'] !== NOYDB_POD_FORMAT_VERSION && h['formatVersion'] !== NOYDB_POD_FORMAT_VERSION_SIGNED)\n ) {\n throw new Error(\n `.noydb bundle header.formatVersion must be ${NOYDB_POD_FORMAT_VERSION} or ${NOYDB_POD_FORMAT_VERSION_SIGNED}, ` +\n `got ${String(h['formatVersion'])}. The reader does not support ` +\n `forward-compat versions; upgrade the reader to handle newer bundles.`,\n )\n }\n if (typeof h['handle'] !== 'string' || !/^[0-9A-HJKMNP-TV-Z]{26}$/.test(h['handle'])) {\n throw new Error(\n `.noydb bundle header.handle must be a 26-character Crockford base32 ULID, ` +\n `got ${typeof h['handle'] === 'string' ? `\"${h['handle']}\"` : String(h['handle'])}.`,\n )\n }\n if (typeof h['bodyBytes'] !== 'number' || !Number.isInteger(h['bodyBytes']) || h['bodyBytes'] < 0) {\n throw new Error(\n `.noydb bundle header.bodyBytes must be a non-negative integer, ` +\n `got ${String(h['bodyBytes'])}.`,\n )\n }\n if (typeof h['bodySha256'] !== 'string' || !/^[0-9a-f]{64}$/.test(h['bodySha256'])) {\n throw new Error(\n `.noydb bundle header.bodySha256 must be a 64-character lowercase hex string, ` +\n `got ${typeof h['bodySha256'] === 'string' ? `\"${h['bodySha256']}\"` : String(h['bodySha256'])}.`,\n )\n }\n if (h['publicEnvelope'] !== undefined) {\n const env = h['publicEnvelope']\n if (env === null || typeof env !== 'object' || Array.isArray(env)) {\n throw new Error(\n `.noydb bundle header.publicEnvelope must be a JSON object when present, got ${typeof env}.`,\n )\n }\n const e = env as Record<string, unknown>\n if (e['_noydb_public'] !== 1) {\n throw new Error(\n `.noydb bundle header.publicEnvelope._noydb_public must be 1, got ${String(e['_noydb_public'])}.`,\n )\n }\n if (typeof e['version'] !== 'number' || !Number.isInteger(e['version']) || e['version'] < 1) {\n throw new Error(\n `.noydb bundle header.publicEnvelope.version must be a positive integer, got ${String(e['version'])}.`,\n )\n }\n }\n if (h['autoUnlock'] !== undefined) {\n if (h['autoUnlock'] !== 'unsealed' && h['autoUnlock'] !== 'sealed') {\n const got = typeof h['autoUnlock'] === 'string' ? `\"${h['autoUnlock']}\"` : typeof h['autoUnlock']\n throw new Error(\n `.noydb bundle header.autoUnlock must be 'unsealed' or 'sealed' when present, got ${got}.`,\n )\n }\n }\n if (h['bundleKind'] !== undefined) {\n if (h['bundleKind'] !== 'snapshot' && h['bundleKind'] !== 'extracted-partition') {\n const got = typeof h['bundleKind'] === 'string' ? `\"${h['bundleKind']}\"` : typeof h['bundleKind']\n throw new Error(\n `.noydb bundle header.bundleKind must be 'snapshot' or 'extracted-partition' when present, got ${got}.`,\n )\n }\n }\n if (h['transferSeal'] !== undefined) {\n const ts = h['transferSeal']\n if (ts === null || typeof ts !== 'object' || Array.isArray(ts)) {\n throw new Error(`.noydb bundle header.transferSeal must be a JSON object when present, got ${typeof ts}.`)\n }\n const t = ts as Record<string, unknown>\n if (t['v'] !== 1) {\n throw new Error(`.noydb bundle header.transferSeal.v must be 1, got ${String(t['v'])}.`)\n }\n if (t['alg'] !== 'aes-256-gcm-pre-shared') {\n throw new Error(`.noydb bundle header.transferSeal.alg must be 'aes-256-gcm-pre-shared', got ${String(t['alg'])}.`)\n }\n if (typeof t['sealId'] !== 'string' || t['sealId'].length === 0) {\n throw new Error(`.noydb bundle header.transferSeal.sealId must be a non-empty string, got ${String(t['sealId'])}.`)\n }\n }\n if (h['keyId'] !== undefined) {\n if (typeof h['keyId'] !== 'string' || !/^[0-9a-f]{16}$/.test(h['keyId'])) {\n throw new Error(\n `.noydb bundle header.keyId must be a 16-character lowercase hex fingerprint, ` +\n `got ${typeof h['keyId'] === 'string' ? `\"${h['keyId']}\"` : typeof h['keyId']}.`,\n )\n }\n }\n if (h['sig'] !== undefined) {\n if (typeof h['sig'] !== 'string' || h['sig'].length === 0 || !/^[A-Za-z0-9_-]+$/.test(h['sig'])) {\n throw new Error(\n `.noydb bundle header.sig must be a non-empty base64url string, ` +\n `got ${typeof h['sig'] === 'string' ? `\"${h['sig']}\"` : typeof h['sig']}.`,\n )\n }\n }\n if (h['sigAlg'] !== undefined) {\n if (h['sigAlg'] !== 'ed25519') {\n const got = typeof h['sigAlg'] === 'string' ? `\"${h['sigAlg']}\"` : typeof h['sigAlg']\n throw new Error(`.noydb bundle header.sigAlg must be 'ed25519' when present, got ${got}.`)\n }\n }\n if (h['engineRange'] !== undefined) {\n if (typeof h['engineRange'] !== 'string' || h['engineRange'].length === 0) {\n throw new Error(\n `.noydb bundle header.engineRange must be a string when present, ` +\n `got ${typeof h['engineRange']}.`,\n )\n }\n }\n if (h['unlockMethods'] !== undefined) {\n const methods = h['unlockMethods']\n if (!Array.isArray(methods)) {\n throw new Error(\n `.noydb bundle header.unlockMethods must be an array when present, got ${typeof methods}.`,\n )\n }\n for (let i = 0; i < methods.length; i++) {\n if (!UNLOCK_METHODS.has(methods[i])) {\n throw new Error(\n `.noydb bundle header.unlockMethods[${i}] must be one of: ` +\n `${[...UNLOCK_METHODS].join(', ')}, got ${String(methods[i])}.`,\n )\n }\n }\n }\n if (h['hasApp'] !== undefined) {\n if (typeof h['hasApp'] !== 'boolean') {\n throw new Error(\n `.noydb bundle header.hasApp must be a boolean when present, got ${typeof h['hasApp']}.`,\n )\n }\n }\n if (h['species'] !== undefined) {\n if (!POD_SPECIES.has(h['species'] as string)) {\n const got = typeof h['species'] === 'string' ? `\"${h['species']}\"` : typeof h['species']\n throw new Error(\n `.noydb bundle header.species must be one of: ${[...POD_SPECIES].join(', ')}, got ${got}.`,\n )\n }\n }\n if (h['pointerMode'] !== undefined) {\n if (h['pointerMode'] !== 'public' && h['pointerMode'] !== 'private') {\n const got = typeof h['pointerMode'] === 'string' ? `\"${h['pointerMode']}\"` : typeof h['pointerMode']\n throw new Error(`.noydb bundle header.pointerMode must be 'public' or 'private' when present, got ${got}.`)\n }\n }\n if (h['redirect'] !== undefined) {\n const r = h['redirect']\n if (r === null || typeof r !== 'object' || Array.isArray(r)) {\n throw new Error(`.noydb bundle header.redirect must be a JSON object when present, got ${typeof r}.`)\n }\n const rec = r as Record<string, unknown>\n if (rec['v'] !== 1) {\n throw new Error(`.noydb bundle header.redirect.v must be 1, got ${String(rec['v'])}.`)\n }\n if (typeof rec['target'] !== 'string' || rec['target'].length === 0) {\n throw new Error(\n `.noydb bundle header.redirect.target must be a non-empty string, got ${String(rec['target'])}.`,\n )\n }\n if (!REDIRECT_REASONS.has(rec['reason'] as string)) {\n throw new Error(\n `.noydb bundle header.redirect.reason must be one of: ${[...REDIRECT_REASONS].join(', ')}, got ${String(rec['reason'])}.`,\n )\n }\n if (typeof rec['issuedBy'] !== 'string' || !/^[0-9a-f]{16}$/.test(rec['issuedBy'])) {\n throw new Error(\n `.noydb bundle header.redirect.issuedBy must be a 16-character lowercase hex fingerprint, ` +\n `got ${typeof rec['issuedBy'] === 'string' ? `\"${rec['issuedBy']}\"` : typeof rec['issuedBy']}.`,\n )\n }\n if (typeof rec['sig'] !== 'string' || rec['sig'].length === 0 || !/^[A-Za-z0-9_-]+$/.test(rec['sig'])) {\n throw new Error(\n `.noydb bundle header.redirect.sig must be a non-empty base64url string, ` +\n `got ${typeof rec['sig'] === 'string' ? `\"${rec['sig']}\"` : typeof rec['sig']}.`,\n )\n }\n }\n // Cross-field invariant: the seal indicator and the extracted-partition\n // kind imply each other. An extracted partition is unlocked via its\n // transfer seal; a seal without the kind is a malformed header.\n const isExtracted = h['bundleKind'] === 'extracted-partition'\n const hasSeal = h['transferSeal'] !== undefined\n if (hasSeal && !isExtracted) {\n throw new Error(\n `.noydb bundle header.transferSeal requires bundleKind === 'extracted-partition'.`,\n )\n }\n if (isExtracted && !hasSeal) {\n throw new Error(\n `.noydb bundle header with bundleKind === 'extracted-partition' must carry a transferSeal indicator.`,\n )\n }\n // An extracted partition's unlock path IS the transfer seal. A parallel\n // autoUnlock credential would create two unlock paths and weaken the\n // one-time-seal guarantee. Reject the combination.\n if (isExtracted && h['autoUnlock'] !== undefined) {\n throw new Error(\n `.noydb bundle header cannot carry both autoUnlock and bundleKind === 'extracted-partition' — `\n + `an extracted partition is unlocked via its transfer seal, not an auto-credential.`,\n )\n }\n // Cross-field invariant: sig/keyId/sigAlg authenticate the header as a\n // unit — a partial tuple (e.g. sig without keyId) is a malformed or\n // truncated signature, not a valid unsigned header. Signed headers also\n // require formatVersion 2 so older readers refuse them instead of\n // silently ignoring the signature.\n const sigFieldsPresent = [h['sig'], h['keyId'], h['sigAlg']].filter((v) => v !== undefined).length\n if (sigFieldsPresent > 0 && sigFieldsPresent < 3) {\n throw new Error(\n `.noydb bundle header.sig, .keyId, and .sigAlg must be present together or not at all ` +\n `(got ${sigFieldsPresent} of 3).`,\n )\n }\n if (sigFieldsPresent === 3 && h['formatVersion'] !== NOYDB_POD_FORMAT_VERSION_SIGNED) {\n throw new Error(\n `.noydb bundle header with sig/keyId/sigAlg must have formatVersion === ${NOYDB_POD_FORMAT_VERSION_SIGNED}, ` +\n `got ${String(h['formatVersion'])}.`,\n )\n }\n}\n\n/**\n * Encode a header object to UTF-8 JSON bytes after validating\n * minimum disclosure. Used by the writer to serialize the header\n * region of the container.\n */\nexport function encodePodHeader(header: NoydbPodHeader): Uint8Array {\n validatePodHeaderFields(header)\n // Stable key ordering — JSON.stringify with no replacer uses\n // insertion order, which is fine here because we control the\n // object construction. Stable ordering means two bundles with\n // identical contents produce byte-identical headers.\n const json = JSON.stringify({\n formatVersion: header.formatVersion,\n handle: header.handle,\n bodyBytes: header.bodyBytes,\n bodySha256: header.bodySha256,\n ...(header.publicEnvelope !== undefined ? { publicEnvelope: header.publicEnvelope } : {}),\n ...(header.autoUnlock !== undefined ? { autoUnlock: header.autoUnlock } : {}),\n ...(header.bundleKind !== undefined ? { bundleKind: header.bundleKind } : {}),\n ...(header.transferSeal !== undefined ? { transferSeal: header.transferSeal } : {}),\n // Encode order here is the WIRE-byte contract only — it does NOT\n // affect the signature. The signer (Task 4/5) signs the header\n // object via canonicalJson, which re-sorts keys independent of\n // this insertion order.\n ...(header.sig !== undefined ? { sig: header.sig } : {}),\n ...(header.keyId !== undefined ? { keyId: header.keyId } : {}),\n ...(header.sigAlg !== undefined ? { sigAlg: header.sigAlg } : {}),\n ...(header.engineRange !== undefined ? { engineRange: header.engineRange } : {}),\n ...(header.unlockMethods !== undefined ? { unlockMethods: header.unlockMethods } : {}),\n ...(header.hasApp !== undefined ? { hasApp: header.hasApp } : {}),\n ...(header.species !== undefined ? { species: header.species } : {}),\n ...(header.pointerMode !== undefined ? { pointerMode: header.pointerMode } : {}),\n ...(header.redirect !== undefined ? { redirect: header.redirect } : {}),\n })\n return new TextEncoder().encode(json)\n}\n\n/**\n * Parse a bundle header from its UTF-8 JSON bytes. Throws on\n * invalid JSON or any minimum-disclosure violation.\n */\nexport function decodePodHeader(bytes: Uint8Array): NoydbPodHeader {\n const json = new TextDecoder('utf-8', { fatal: true }).decode(bytes)\n let parsed: unknown\n try {\n parsed = JSON.parse(json)\n } catch (err) {\n throw new Error(\n `.noydb bundle header is not valid JSON: ${(err as Error).message}`,\n )\n }\n validatePodHeaderFields(parsed)\n return parsed\n}\n\n/**\n * Read a uint32 from `bytes` at `offset` in big-endian byte order.\n * No bounds check — callers must guarantee `offset + 4 <= bytes.length`.\n * Used to decode the header length field; kept inline so the parser\n * doesn't depend on DataView allocation per call.\n */\nexport function readUint32BE(bytes: Uint8Array, offset: number): number {\n return (\n (bytes[offset]! << 24 >>> 0) +\n (bytes[offset + 1]! << 16) +\n (bytes[offset + 2]! << 8) +\n bytes[offset + 3]!\n )\n}\n\n/**\n * Write a uint32 to `bytes` at `offset` in big-endian byte order.\n * No bounds check — callers must guarantee `offset + 4 <= bytes.length`.\n */\nexport function writeUint32BE(bytes: Uint8Array, offset: number, value: number): void {\n bytes[offset] = (value >>> 24) & 0xff\n bytes[offset + 1] = (value >>> 16) & 0xff\n bytes[offset + 2] = (value >>> 8) & 0xff\n bytes[offset + 3] = value & 0xff\n}\n\n/**\n * Verify the magic prefix of a bundle. Returns true if the first\n * 4 bytes match `NDB1`. Used by readers as a fast file-type check\n * before any further parsing.\n */\nexport function hasNoydbPodMagic(bytes: Uint8Array): boolean {\n if (bytes.length < NOYDB_POD_MAGIC.length) return false\n for (let i = 0; i < NOYDB_POD_MAGIC.length; i++) {\n if (bytes[i] !== NOYDB_POD_MAGIC[i]) return false\n }\n return true\n}\n","/**\n * The ONE signing convention for the noy-db family — pod header, the\n * Redirect record (#944), and manifest writes (#941) all sign through\n * this module rather than inventing their own scheme.\n *\n * Convention: `canonicalJson(payload minus sig)` → `utf8` → Ed25519, over\n * the published `@noy-db/attestation` primitives. The caller is\n * responsible for excluding the `sig` field from `payload` before\n * signing/verifying — this module never strips it for you, so a payload\n * that still carries a stray field (or `undefined`) fails loudly via\n * `canonicalJson` rather than silently signing the wrong bytes.\n *\n * No pod/vault imports here — this is a family-wide primitive.\n */\nimport { canonicalJson, ed25519Sign, ed25519Verify, utf8 } from '@noy-db/attestation'\n\nexport const POD_SIG_ALG = 'ed25519' as const\n\n/** The exact bytes a signature covers: utf8(canonicalJson(payload)). Exported for cross-checking. */\nexport function signedBytes(payload: Record<string, unknown>): Uint8Array {\n return utf8(canonicalJson(payload))\n}\n\n/**\n * Sign an arbitrary canonical record. `payload` MUST already exclude any\n * `sig` field.\n */\nexport async function signRecord(privateKeyPkcs8B64: string, payload: Record<string, unknown>): Promise<string> {\n return ed25519Sign(privateKeyPkcs8B64, signedBytes(payload))\n}\n\n/**\n * Verify. `payload` is the record WITHOUT `sig`. Fails closed (false) on\n * any malformed input.\n */\nexport async function verifyRecord(publicKeyB64: string, sig: string, payload: Record<string, unknown>): Promise<boolean> {\n return ed25519Verify(publicKeyB64, sig, signedBytes(payload))\n}\n","/**\n * `.noydb` container primitives — write, read, header-only read.\n *\n *. Wraps a `vault.dump()` JSON string in the\n * binary container described in `format.ts`.\n *\n * **Three primitives:**\n *\n * - `writePod(vault, opts?)` — produces the\n * full container bytes ready to write to disk or upload\n * - `readPodHeader(bytes)` — parses just the header\n * without decompressing the body, fast file-type and\n * metadata read for cloud listing UIs\n * - `readPod(bytes)` — full read: validates magic,\n * header, integrity hash, and decompresses the body to\n * return the original `dump()` JSON string for use with\n * `vault.load()`\n *\n * **Compression strategy:** brotli when available (Node 22+,\n * Chrome 124+, Firefox 122+), gzip fallback elsewhere. The\n * algorithm choice is encoded in the format byte at offset 5,\n * so readers handle either transparently. Brotli wins ~30-50%\n * on JSON payloads with repeated keys (which vault dumps\n * are).\n *\n * **Why split read/load?** `readPod` returns the\n * *unwrapped JSON string*, not a Vault object. The caller\n * is responsible for piping that JSON into\n * `vault.load(json, secret)`. Splitting the layers\n * keeps the bundle module free of any crypto/secret\n * concerns — it's purely a format layer. The same `readPod`\n * call can also feed verification tools, format inspectors, or\n * archive utilities that don't care about decryption.\n */\n\nimport {\n COMPRESSION_BROTLI,\n COMPRESSION_GZIP,\n COMPRESSION_NONE,\n FLAG_COMPRESSED,\n FLAG_HAS_INTEGRITY_HASH,\n NOYDB_POD_FORMAT_VERSION,\n NOYDB_POD_FORMAT_VERSION_SIGNED,\n NOYDB_POD_MAGIC,\n NOYDB_POD_PREFIX_BYTES,\n decodePodHeader,\n encodePodHeader,\n hasNoydbPodMagic,\n readUint32BE,\n writeUint32BE,\n type CompressionAlgo,\n type NoydbPodHeader,\n type UnlockMethod,\n} from './format.js'\nimport { signRecord, verifyRecord } from './signature.js'\nimport type { Redirect } from './redirect.js'\nimport type { DocSigner } from '../with-audit/attestation/signer.js'\nimport { sha256Hex as sha256HexBytes } from '../kernel/enclave/index.js'\nimport { PodIntegrityError, PodSealMismatchError, ValidationError } from '../kernel/errors.js'\nimport type { Vault } from '../kernel/vault.js'\nimport type { PodRecipient } from '../with-party/team/keyring.js'\nimport { pickLocale } from '../with-party/directory/cover/storage.js'\nimport type { Cover } from '../with-party/directory/cover/types.js'\nimport type { SealingKeyProvider, RecipientSealer, RecipientHint } from '../with-party/team/managed-secret.js'\n\n// ─── Auto-credential types ────────────────────────────────────────────────────\n\n/**\n * The credential kinds that can be bundled for auto-unlock.\n * WebAuthn is intentionally excluded — it is hardware-bound and\n * cannot be embedded as a portable credential.\n */\nexport type AutoCredentialKind = 'secret' | 'password' | 'pin'\n\n/**\n * A typed credential for auto-unlock. Carries the credential `kind`\n * alongside the plaintext `value`, so consumers can dispatch the\n * correct login/prefill path rather than treating all credentials\n * as secrets.\n *\n * `bundle.ts` is a pure format layer — it carries the credential\n * without interpreting it. The consumer is responsible for\n * dispatching on `kind`.\n */\nexport interface AutoCredential {\n readonly kind: AutoCredentialKind\n readonly value: string\n}\n\n/**\n * Options accepted by `writePod`.\n *\n * - `compression: 'auto'` (default) — try brotli, fall back to gzip\n * - `compression: 'brotli'` — force brotli, throw if unsupported\n * - `compression: 'gzip'` — force gzip\n * - `compression: 'none'` — no compression (round-trip testing only)\n *\n * **Slice filtering:**\n * - `collections` — allowlist of collection names to include. Internal\n * collections (keyrings, ledger) and excluded user collections are\n * dropped from the bundle. Records inside included collections are\n * carried through verbatim.\n * - `since` — only records whose envelope `_ts` is on/after the given\n * instant survive. Operates on the unencrypted envelope timestamp,\n * so plaintext access to records is not required.\n *\n * Both filters intersect (AND). When neither is provided the bundle is\n * a whole-vault snapshot, identical to today's behaviour.\n */\nexport interface WritePodOptions {\n readonly compression?: 'auto' | 'brotli' | 'gzip' | 'none'\n /** Allowlist of user-collection names to include. */\n readonly collections?: readonly string[]\n /**\n * Drop records whose envelope `_ts` is strictly older than this\n * instant. Accepts a `Date` or any ISO-8601 string parseable by\n * `new Date()`.\n */\n readonly since?: Date | string\n /**\n * Plaintext-pipeline record predicate. Decrypts each record\n * with the vault's per-collection DEK, runs the predicate, and\n * keeps the original ciphertext for survivors (no re-encrypt —\n * preserves zero-knowledge cleanly). Records the predicate returns\n * `false` for are dropped from the bundle.\n *\n * Async predicates are supported. Mutating the record from inside\n * the predicate is undefined behaviour.\n */\n readonly where?: (\n record: unknown,\n ctx: { collection: string; id: string },\n ) => boolean | Promise<boolean>\n /**\n * Hierarchical-tier ceiling. Records whose envelope `_tier`\n * is strictly greater than this number are dropped. Operates on the\n * envelope `_tier` (no decryption needed) — vault.exportStream is\n * referenced in the issue body for symmetry, but the tier value\n * lives on the unencrypted envelope. Vault without tiers is a no-op.\n */\n readonly tierAtMost?: number\n /**\n * Single-recipient re-keying shorthand. When set, the\n * bundle's keyring is replaced with one freshly-derived entry sealed\n * with this secret. The recipient inherits the source keyring's\n * userId, role, and permissions. Mutually exclusive with `recipients`.\n */\n readonly exportSecret?: string\n /**\n * Multi-recipient re-keying. Replaces the bundle's keyring\n * map with one slot per recipient, each sealed with its own\n * secret. DEKs are unwrapped from the source keyring once and\n * re-wrapped per recipient — record ciphertext is unchanged.\n *\n * Mutually exclusive with `exportSecret`. When neither is set,\n * the bundle inherits the source keyring as-is (today's behaviour,\n * suited to personal backup-and-restore).\n */\n readonly recipients?: readonly PodRecipient[]\n /**\n * Auto-unlock — unsealed per-user credentials.\n *\n * Generalises `autoSecrets` to support any bundleable credential\n * kind (`secret` | `password` | `pin`).\n *\n * Public-by-design: anyone holding the bundle bytes can read these\n * plaintext credentials. Use for demo data, sample vaults,\n * prospect onboarding.\n *\n * The `policy: 'public-by-design'` discriminant is mandatory. A\n * bare `{ perUser }` without it is rejected at write time — the\n * safety net against a careless call against a production vault.\n *\n * Mutually exclusive with `sealedCredentials`, `autoSecrets`,\n * and `sealedSecrets`.\n */\n readonly autoCredentials?: {\n readonly policy: 'public-by-design'\n readonly perUser: Record<string, AutoCredential>\n }\n /**\n * Auto-unlock — per-user credentials sealed under a\n * {@link SealingKeyProvider}.\n *\n * Generalises `sealedSecrets` to support any bundleable\n * credential kind (`secret` | `password` | `pin`).\n *\n * The hub seals each user's plaintext credential under `provider`\n * and embeds the resulting sealed envelopes in the bundle. The\n * recipient must hold a provider with a matching `pid` (i.e.,\n * `provider.id`) to auto-unseal on import.\n *\n * `mode: 'self-target'` — sender and recipient share the same\n * provider identity (same iCloud Keychain entry, same\n * MDM-provisioned bundle id, same KMS account, etc.).\n *\n * `mode: 'recipient-target'` — asymmetric sealing via a\n * {@link RecipientSealer}. Each user entry carries a\n * `credential` and a `hint` (the recipient's public material).\n * The bundle can only be unsealed by the holder of the matching\n * private key.\n *\n * Mutually exclusive with `autoCredentials`, `autoSecrets`,\n * and `sealedSecrets`.\n */\n readonly sealedCredentials?:\n | {\n readonly mode: 'self-target'\n readonly provider: SealingKeyProvider\n readonly perUser: Record<string, AutoCredential>\n }\n | {\n readonly mode: 'recipient-target'\n readonly provider: RecipientSealer\n readonly perUser: Record<string, { readonly credential: AutoCredential; readonly hint: RecipientHint }>\n }\n /**\n * @deprecated Use `autoCredentials` instead.\n *\n * Auto-unlock — unsealed per-user secrets.\n *\n * Public-by-design: anyone holding the bundle bytes can read these\n * plaintext credentials. Use for demo data, sample vaults,\n * prospect onboarding.\n *\n * The `policy: 'public-by-design'` discriminant is mandatory. A\n * bare `{ perUser }` without it is rejected at write time — the\n * safety net against a careless call against a production vault.\n *\n * Mutually exclusive with `autoCredentials`, `sealedCredentials`,\n * and `sealedSecrets`.\n */\n readonly autoSecrets?: {\n readonly policy: 'public-by-design'\n readonly perUser: Record<string, string>\n }\n /**\n * @deprecated Use `sealedCredentials` instead.\n *\n * Auto-unlock — per-user secrets sealed under a\n * {@link SealingKeyProvider} (self-target only).\n *\n * The hub seals each user's plaintext secret under `provider`\n * and embeds the resulting sealed envelopes in the bundle. The\n * recipient must hold a provider with a matching `pid` (i.e.,\n * `provider.id`) to auto-unseal on import.\n *\n * `mode: 'self-target'` is the only mode for `sealedSecrets` — sender\n * and recipient share the same provider identity (same iCloud Keychain\n * entry, same MDM-provisioned bundle id, same KMS account, etc.).\n * For recipient-target sealing via the `RecipientSealer` interface,\n * use `sealedCredentials` with `mode: 'recipient-target'` (§11.4).\n *\n * Mutually exclusive with `autoCredentials`, `sealedCredentials`,\n * and `autoSecrets`.\n */\n readonly sealedSecrets?: {\n readonly mode: 'self-target'\n readonly provider: SealingKeyProvider\n readonly perUser: Record<string, string>\n }\n /**\n * Pod-header signing control (#943).\n *\n * - omitted (default) — sign the header iff the source vault has a\n * persisted document signer (`vault._loadPodSigner()` returns\n * non-null). A vault that never minted a signer produces an\n * unsigned `formatVersion: 1` header, exactly as before.\n * - `false` — never sign, even if a signer is persisted.\n * - an explicit {@link DocSigner} — sign with the supplied keypair\n * without touching the vault's persisted signer (test / advanced\n * injection). Does not mint or persist anything.\n *\n * Signing NEVER mints a signer as a side effect of export: an absent\n * signer means an unsigned pod, not an on-the-fly key generation.\n */\n readonly sign?: false | DocSigner\n /**\n * Header L2 fields (#942) — pre-auth dispatch metadata written verbatim\n * into the pod header. See `NoydbPodHeader` for the disclosure rationale\n * of each. All optional; absence produces a legacy-shaped header exactly\n * as before.\n */\n readonly engineRange?: string\n readonly unlockMethods?: readonly UnlockMethod[]\n readonly hasApp?: boolean\n readonly species?: 'full' | 'connection' | 'snapshot' | 'redirect' | 'group'\n readonly pointerMode?: 'public' | 'private'\n /**\n * Signed \"this moved, go there\" pointer (#944), written verbatim into\n * the plaintext header. Pass an already-signed {@link Redirect}\n * (mint one via `signRedirect`) — `writePod` does not sign it for you.\n */\n readonly redirect?: Redirect\n}\n\n\n/**\n * Every key `writePod` reads, in declaration order (#991).\n *\n * A pod is a wire artifact, so an option this list doesn't name is refused\n * rather than ignored: a dropped write option produces a structurally valid\n * bundle that is missing something, and the defect surfaces later — in\n * whoever imports it — with nothing left to trace it back to.\n *\n * `bundle-option-keys.test.ts` parses `WritePodOptions` out of this file and\n * fails if the two ever drift, so adding an option is enough; you do not have\n * to remember to update this list, only to keep the interface shape parseable.\n *\n * @internal\n */\nexport const WRITE_POD_OPTION_KEYS = [\n 'compression',\n 'collections',\n 'since',\n 'where',\n 'tierAtMost',\n 'exportSecret',\n 'recipients',\n 'autoCredentials',\n 'sealedCredentials',\n 'autoSecrets',\n 'sealedSecrets',\n 'sign',\n 'engineRange',\n 'unlockMethods',\n 'hasApp',\n 'species',\n 'pointerMode',\n 'redirect',\n] as const satisfies readonly (keyof WritePodOptions)[]\n\n/**\n * Write options that were renamed away, mapped to what replaced them.\n *\n * These get a better message than a bare unknown key: `autoPassphrases` →\n * `autoSecrets` (§3) → `autoCredentials` (#215) is two hops, and a caller\n * arriving from `0.3.0` has no way to guess the far end.\n */\nconst RETIRED_WRITE_POD_OPTIONS: Readonly<Record<string, string>> = {\n autoPassphrases: 'autoCredentials',\n sealedPassphrases: 'sealedCredentials',\n exportPassphrase: 'exportSecret',\n}\n\n/**\n * Refuse any option key `writePod` does not read.\n *\n * Keys explicitly set to `undefined` pass — spreading a partially-built\n * options object is a normal call shape and carries no intent.\n */\nfunction assertKnownWritePodOptions(opts: WritePodOptions): void {\n const known = new Set<string>(WRITE_POD_OPTION_KEYS)\n for (const [key, value] of Object.entries(opts)) {\n if (value === undefined || known.has(key)) continue\n const replacement = RETIRED_WRITE_POD_OPTIONS[key]\n throw new Error(\n replacement !== undefined\n ? `writePod: \\`${key}\\` was renamed to \\`${replacement}\\`. `\n + 'Passing the old key would have written a pod without that '\n + 'slot — see the 0.4.0-pre migration note.'\n : `writePod: unknown option \\`${key}\\`. Options are not ignored — a `\n + 'dropped write option produces a pod that is silently missing '\n + `something. Known keys: ${WRITE_POD_OPTION_KEYS.join(', ')}.`,\n )\n }\n}\n\n/**\n * Result returned by `readPod`. The caller is expected to\n * pass `dumpJson` into `vault.load(json, secret)` to\n * actually restore a vault. Splitting the layers keeps the\n * bundle module free of crypto concerns — see file-level docs.\n */\nexport interface PodReadResult {\n readonly header: NoydbPodHeader\n readonly dumpJson: string\n /**\n * Auto-unlock material. Present only when\n * the header's `autoUnlock` flag is set AND the body's wrapped\n * structure survived parsing. Values are typed credentials — either\n * delivered plain (`kind: 'unsealed'`) or unsealed at read time\n * using one of the supplied `sealingProviders` (`kind: 'sealed'`).\n *\n * Consumers dispatch on `cred.kind` to choose the correct login /\n * prefill path. Pre-0.2 bundles (bare string entries) are coerced\n * to `{ kind: 'secret', value }` on read for back-compat.\n *\n * For `kind: 'sealed'` bundles read without `sealingProviders`, the\n * `value` field is the raw base64 sealed bytes — opaque to the\n * consumer until unsealed elsewhere.\n */\n readonly autoUnlock?: {\n readonly kind: 'unsealed' | 'sealed'\n readonly perUser: Record<string, AutoCredential>\n }\n}\n\n/**\n * Sealed credential entry as it appears in the bundle body's\n * `_autoUnlock.perUser` map when the bundle was written with\n * `sealedCredentials` (or the deprecated `sealedSecrets`).\n * Provider's sealed output is base64-encoded; the `pid` is the\n * dispatch key matched against recipient-supplied\n * `SealingKeyProvider.id`. The `kind` carries the plaintext-tier\n * metadata so the consumer can dispatch on credential type without\n * unsealing first.\n *\n * Back-compat: `kind` is absent in older bundles — readers must\n * default to `'secret'` when not present.\n */\ninterface SealedAutoUnlockEntry {\n readonly pid: string\n readonly sealed: string\n readonly alg: 'aes-256-gcm'\n readonly kind?: AutoCredentialKind\n /**\n * Recipient-target only: the RecipientHint the sender used to seal.\n * Carried for recipient verifiability (\"yes this was sealed against\n * my published hint\"). Self-target entries omit it. Pre-0.2 readers\n * ignore unknown fields, so this is back-compatible.\n */\n readonly hint?: RecipientHint\n}\n\n/**\n * Discriminated wrapper carried in the bundle body when the header's\n * `autoUnlock` flag is set. Without the flag, the body is the raw\n * `vault.dump()` JSON string.\n *\n * Back-compat: older bundles carry bare `string` values in the\n * unsealed `perUser` map. Readers must coerce those to\n * `{ kind: 'secret', value }`.\n */\ninterface AutoUnlockBody {\n readonly _noydb_bundle_body: 1\n readonly dump: string\n readonly _autoUnlock:\n | { readonly kind: 'unsealed'; readonly perUser: Record<string, AutoCredential | string> }\n | { readonly kind: 'sealed'; readonly perUser: Record<string, SealedAutoUnlockEntry> }\n}\n\n/**\n * Options accepted by {@link readPod} for the\n * auto-unlock paths. Without these the reader behaves exactly as before\n * (header parsed; body returned as `dumpJson`).\n */\nexport interface ReadPodOptions {\n /**\n * Recipient-side sealing providers used to unseal entries from\n * `sealedSecrets`. The reader picks the one whose `.id`\n * matches each entry's `pid`. Multiple providers may be supplied\n * (different users may seal under different identities).\n *\n * When unset and the bundle carries sealed envelopes, the\n * `autoUnlock.perUser` map remains the SEALED entries unmodified\n * — callers can inspect them or unseal elsewhere.\n */\n readonly sealingProviders?: readonly SealingKeyProvider[]\n /**\n * Opt-in trial mode for unsealing — when an entry's `pid` doesn't\n * match a registered provider, try each provider whose alg\n * matches. Default `false` (strict-pid dispatch per foundation\n * §11.9.2). Surfaces extra credential prompts; use deliberately.\n */\n readonly attemptUnsealAcrossProviders?: boolean\n}\n\n// ─── Auto-unlock helpers ──────────────────────────────────────────────────────\n\n/**\n * Internal normalized form of the auto-unlock options, computed once\n * from the four public-facing fields (autoCredentials, sealedCredentials,\n * autoSecrets, sealedSecrets). Callers work against this shape\n * so the build + validate paths share a single normalizer.\n */\ninterface NormalizedAutoUnlock {\n readonly mode: 'unsealed' | 'sealed-self' | 'sealed-recipient'\n readonly provider?: SealingKeyProvider | RecipientSealer\n readonly perUser: Record<string, AutoCredential>\n /** Present only for `sealed-recipient`. Same key set as `perUser`. */\n readonly hints?: Record<string, RecipientHint>\n}\n\n/**\n * Coerce a `Record<string, string>` (legacy secret-only map) into\n * a `Record<string, AutoCredential>` by tagging each entry as\n * `kind: 'secret'`. Used by the normalizer to promote the deprecated\n * `autoSecrets`/`sealedSecrets` sugar.\n */\nfunction toAutoCredentials(m: Record<string, string>): Record<string, AutoCredential> {\n return Object.fromEntries(\n Object.entries(m).map(([u, value]) => [u, { kind: 'secret' as const, value }]),\n )\n}\n\n/**\n * Normalize the four auto-unlock option fields into a single\n * `NormalizedAutoUnlock` (or `null` when none is set). Enforces mutual\n * exclusion — exactly one of the four may be present. Promotes the\n * deprecated sugar fields to `AutoCredential` shape.\n *\n * Does NOT validate field-level constraints (policy marker, perUser\n * length, mode, provider presence, kind allowlist) — those are checked\n * in `validateAutoUnlockOptions` after normalization.\n */\nfunction normalizeAutoUnlock(opts: WritePodOptions): NormalizedAutoUnlock | null {\n const set = [\n opts.autoCredentials,\n opts.sealedCredentials,\n opts.autoSecrets,\n opts.sealedSecrets,\n ].filter(v => v !== undefined).length\n if (set === 0) return null\n if (set > 1) {\n throw new ValidationError(\n 'writePod: only one of autoCredentials / sealedCredentials / '\n + 'autoSecrets / sealedSecrets may be set.',\n )\n }\n if (opts.autoCredentials !== undefined) {\n return { mode: 'unsealed', perUser: opts.autoCredentials.perUser }\n }\n if (opts.autoSecrets !== undefined) {\n return { mode: 'unsealed', perUser: toAutoCredentials(opts.autoSecrets.perUser) }\n }\n if (opts.sealedCredentials !== undefined) {\n if (opts.sealedCredentials.mode === 'recipient-target') {\n const perUser: Record<string, AutoCredential> = {}\n const hints: Record<string, RecipientHint> = {}\n for (const [userId, entry] of Object.entries(opts.sealedCredentials.perUser)) {\n perUser[userId] = entry.credential\n hints[userId] = entry.hint\n }\n return { mode: 'sealed-recipient', provider: opts.sealedCredentials.provider, perUser, hints }\n }\n return { mode: 'sealed-self', provider: opts.sealedCredentials.provider, perUser: opts.sealedCredentials.perUser }\n }\n // sealedSecrets — only remaining option\n return {\n mode: 'sealed-self',\n provider: opts.sealedSecrets!.provider,\n perUser: toAutoCredentials(opts.sealedSecrets!.perUser),\n }\n}\n\n/**\n * Validate the auto-unlock options and return the resulting header\n * `autoUnlock` value (or null when no auto-unlock requested).\n *\n * Takes the pre-computed `NormalizedAutoUnlock` so the caller (i.e.\n * `writePod`) can pass the same object to `buildAutoUnlockWrapper`\n * without a second `normalizeAutoUnlock` call.\n *\n * Validation per spec (§3):\n * - (mutual exclusion already enforced by normalizeAutoUnlock)\n * - unsealed path: `policy: 'public-by-design'` marker required\n * - non-empty `perUser` maps\n * - sealed path: provider present; both `mode: 'self-target'` and `mode: 'recipient-target'` accepted; recipient-target requires a `RecipientSealer` provider and per-user `hint` (§11.4)\n * - every AutoCredential.kind ∈ {secret, password, pin}\n * (WebAuthn is hardware-bound and cannot be bundled)\n *\n * Throws {@link ValidationError} on any violation.\n */\nfunction validateAutoUnlockOptions(\n opts: WritePodOptions,\n normalized: NormalizedAutoUnlock | null,\n): 'unsealed' | 'sealed' | null {\n if (normalized === null) return null\n\n const VALID_KINDS: ReadonlySet<string> = new Set(['secret', 'password', 'pin'])\n\n // Validate every credential kind before any further checks.\n for (const [userId, cred] of Object.entries(normalized.perUser)) {\n if (!VALID_KINDS.has(cred.kind)) {\n throw new ValidationError(\n `writePod: credential for user '${userId}' has unsupported kind '${cred.kind}'. `\n + 'auto-unlock supports secret/password/pin only; WebAuthn is hardware-bound '\n + 'and cannot be bundled.',\n )\n }\n }\n\n if (normalized.mode === 'unsealed') {\n // Read the policy marker from whichever active option carries it.\n const policy = opts.autoCredentials?.policy ?? opts.autoSecrets?.policy\n if (policy !== 'public-by-design') {\n throw new ValidationError(\n 'writePod: `autoCredentials` (or `autoSecrets`) requires '\n + '`policy: \"public-by-design\"`. '\n + 'This is an explicit opt-in marker — bundling plaintext credentials is '\n + 'safe only when those credentials are intended to be public (demo data, '\n + 'sample vaults). For production credentials, use `sealedCredentials` instead.',\n )\n }\n const userCount = Object.keys(normalized.perUser).length\n if (userCount === 0) {\n throw new ValidationError(\n 'writePod: `autoCredentials.perUser` (or `autoSecrets.perUser`) '\n + 'must have at least one entry.',\n )\n }\n return 'unsealed'\n }\n\n // Sealed path — branch on mode.\n if (normalized.mode === 'sealed-recipient') {\n const provider = normalized.provider\n if (provider === undefined || typeof (provider as RecipientSealer).publishRecipientHint !== 'function'\n || typeof (provider as RecipientSealer).sealForRecipient !== 'function') {\n throw new ValidationError(\n 'writePod: `sealedCredentials.provider` for mode \\'recipient-target\\' must be a '\n + 'RecipientSealer (publishRecipientHint + sealForRecipient). Self-only providers '\n + '(MemorySealingKeyProvider, at-macos-keychain, etc.) do not satisfy this contract.',\n )\n }\n const hints = normalized.hints\n if (hints === undefined) {\n throw new Error('unreachable — sealed-recipient normalization must populate hints')\n }\n for (const userId of Object.keys(normalized.perUser)) {\n const hint = hints[userId]\n if (hint === undefined) {\n throw new ValidationError(\n `writePod: \\`sealedCredentials.perUser['${userId}']\\` missing required \\`hint\\` for mode 'recipient-target'.`,\n )\n }\n if (hint.v !== 1) {\n throw new ValidationError(\n `writePod: \\`sealedCredentials.perUser['${userId}'].hint.v\\` must be 1 (got ${String(hint.v)}).`,\n )\n }\n if (typeof hint.pid !== 'string' || hint.pid.length === 0) {\n throw new ValidationError(\n `writePod: \\`sealedCredentials.perUser['${userId}'].hint.pid\\` must be a non-empty string identifying the recipient.`,\n )\n }\n if (hint.alg !== 'rsa-oaep-sha256') {\n throw new ValidationError(\n `writePod: \\`sealedCredentials.perUser['${userId}'].hint.alg\\` must be 'rsa-oaep-sha256' in slice 1 (got '${String(hint.alg)}').`,\n )\n }\n // Note: hint.pid identifies the recipient, not the sender — no pid===sender.id check here.\n // The sender holds a RecipientSealer that calls sealForRecipient(plaintext, hint);\n // the hint's pid is the dispatch key on the reader side (matched against recipient providers).\n }\n const userCount = Object.keys(normalized.perUser).length\n if (userCount === 0) {\n throw new ValidationError(\n 'writePod: `sealedCredentials.perUser` must have at least one entry.',\n )\n }\n return 'sealed'\n }\n\n // mode === 'sealed-self'\n const selfTargetMode = opts.sealedCredentials?.mode ?? opts.sealedSecrets?.mode\n if (selfTargetMode !== 'self-target') {\n throw new ValidationError(\n `writePod: \\`sealedCredentials.mode\\` (or \\`sealedSecrets.mode\\`) must be `\n + `'self-target' or 'recipient-target' (got '${String(selfTargetMode)}').`,\n )\n }\n if (normalized.provider === undefined) {\n throw new ValidationError(\n 'writePod: `sealedCredentials.provider` (or `sealedSecrets.provider`) '\n + 'is required (a `SealingKeyProvider`).',\n )\n }\n const userCount = Object.keys(normalized.perUser).length\n if (userCount === 0) {\n throw new ValidationError(\n 'writePod: `sealedCredentials.perUser` (or `sealedSecrets.perUser`) '\n + 'must have at least one entry.',\n )\n }\n return 'sealed'\n}\n\n/**\n * Build the body wrapper carrying the dump + `_autoUnlock` blob.\n * Takes the pre-computed `NormalizedAutoUnlock` so both validate and\n * build work off the same normalized form (no double-normalize).\n */\nasync function buildAutoUnlockWrapper(\n dumpJson: string,\n normalized: NormalizedAutoUnlock,\n): Promise<AutoUnlockBody> {\n if (normalized.mode === 'unsealed') {\n return {\n _noydb_bundle_body: 1,\n dump: dumpJson,\n _autoUnlock: {\n kind: 'unsealed',\n perUser: { ...normalized.perUser },\n },\n }\n }\n // Sealed path — branch on mode.\n const provider = normalized.provider\n if (provider === undefined) {\n throw new Error('unreachable — validation should have caught this')\n }\n const sealedPerUser: Record<string, SealedAutoUnlockEntry> = {}\n const encoder = new TextEncoder()\n\n if (normalized.mode === 'sealed-recipient') {\n const recipientSealer = provider as RecipientSealer\n const hints = normalized.hints\n if (hints === undefined) {\n throw new Error('unreachable — sealed-recipient normalization must populate hints')\n }\n for (const [userId, cred] of Object.entries(normalized.perUser)) {\n const hint = hints[userId]!\n const sealed = await recipientSealer.sealForRecipient(encoder.encode(cred.value), hint)\n sealedPerUser[userId] = {\n pid: hint.pid, // use the recipient's pid, not the sender's\n sealed: bytesToBase64(sealed),\n alg: 'aes-256-gcm',\n kind: cred.kind,\n hint,\n }\n }\n } else {\n // mode === 'sealed-self'\n const selfSealer = provider as SealingKeyProvider\n for (const [userId, cred] of Object.entries(normalized.perUser)) {\n const sealed = await selfSealer.seal(encoder.encode(cred.value))\n sealedPerUser[userId] = {\n pid: selfSealer.id,\n sealed: bytesToBase64(sealed),\n alg: 'aes-256-gcm',\n kind: cred.kind,\n }\n }\n }\n\n return {\n _noydb_bundle_body: 1,\n dump: dumpJson,\n _autoUnlock: { kind: 'sealed', perUser: sealedPerUser },\n }\n}\n\n/**\n * Parse the body bytes when the header signaled an auto-unlock.\n * Returns the inner `dump` JSON string + the `_autoUnlock` blob;\n * throws if the wrapper structure is malformed.\n */\nfunction parseAutoUnlockBody(bodyString: string): { dump: string; blob: AutoUnlockBody['_autoUnlock'] } {\n let parsed: unknown\n try {\n parsed = JSON.parse(bodyString)\n } catch (err) {\n throw new PodIntegrityError(\n 'header declared autoUnlock but body could not be parsed as JSON wrapper: '\n + (err instanceof Error ? err.message : String(err)),\n )\n }\n if (typeof parsed !== 'object' || parsed === null) {\n throw new PodIntegrityError('autoUnlock body is not a JSON object')\n }\n const obj = parsed as Record<string, unknown>\n if (obj['_noydb_bundle_body'] !== 1) {\n throw new PodIntegrityError(\n 'autoUnlock body missing `_noydb_bundle_body: 1` discriminator',\n )\n }\n if (typeof obj['dump'] !== 'string') {\n throw new PodIntegrityError('autoUnlock body must carry a string `dump` field')\n }\n const blob = obj['_autoUnlock']\n if (typeof blob !== 'object' || blob === null) {\n throw new PodIntegrityError('autoUnlock body missing `_autoUnlock` blob')\n }\n const blobObj = blob as Record<string, unknown>\n const kind = blobObj['kind']\n if (kind !== 'unsealed' && kind !== 'sealed') {\n throw new PodIntegrityError(\n `autoUnlock blob has invalid kind ${String(kind)}; expected 'unsealed' or 'sealed'`,\n )\n }\n return {\n dump: obj['dump'],\n blob: blob as AutoUnlockBody['_autoUnlock'],\n }\n}\n\n/**\n * Transfer-seal payload. The destination DEKs, exported to raw\n * bytes and AES-256-GCM-sealed *as a set* under the one-time transfer\n * key. `adoptPartition` unseals this; `createOwnerOnAdoptedPartition`\n * re-wraps the raw DEKs under the recipient's KEK.\n */\nexport interface TransferSealPayload {\n readonly v: 1\n readonly alg: 'aes-256-gcm-pre-shared'\n readonly sealId: string\n /** base64(AES-256-GCM(transferKey, JSON of { collection: base64(rawDEK) })) — iv ‖ ct ‖ tag. */\n readonly payload: string\n}\n\n/**\n * Body wrapper for an extracted, transfer-sealed partition.\n * Sibling to {@link AutoUnlockBody}; selected by `header.bundleKind ===\n * 'extracted-partition'`. The inner `dump` is a re-keyed projection with\n * an empty `keyrings` map.\n */\nexport interface ExtractedPartitionBody {\n readonly _noydb_bundle_body: 1\n readonly dump: string\n readonly _transferSeal: TransferSealPayload\n}\n\nexport function buildExtractedPartitionWrapper(\n dumpJson: string,\n seal: TransferSealPayload,\n): ExtractedPartitionBody {\n return { _noydb_bundle_body: 1, dump: dumpJson, _transferSeal: seal }\n}\n\nexport function parseExtractedPartitionBody(\n bodyString: string,\n): { dump: string; seal: TransferSealPayload } {\n let parsed: unknown\n try {\n parsed = JSON.parse(bodyString)\n } catch (err) {\n throw new PodIntegrityError(\n 'header declared extracted-partition but body could not be parsed as JSON wrapper: '\n + (err instanceof Error ? err.message : String(err)),\n )\n }\n if (typeof parsed !== 'object' || parsed === null) {\n throw new PodIntegrityError('extracted-partition body is not a JSON object')\n }\n const obj = parsed as Record<string, unknown>\n if (obj['_noydb_bundle_body'] !== 1) {\n throw new PodIntegrityError(\n 'extracted-partition body missing `_noydb_bundle_body: 1` discriminator',\n )\n }\n if (typeof obj['dump'] !== 'string') {\n throw new PodIntegrityError('extracted-partition body must carry a string `dump` field')\n }\n const seal = obj['_transferSeal']\n if (typeof seal !== 'object' || seal === null) {\n throw new PodIntegrityError('extracted-partition body missing `_transferSeal` blob')\n }\n const s = seal as Record<string, unknown>\n if (s['v'] !== 1 || s['alg'] !== 'aes-256-gcm-pre-shared'\n || typeof s['sealId'] !== 'string' || typeof s['payload'] !== 'string') {\n throw new PodIntegrityError('extracted-partition `_transferSeal` blob is malformed')\n }\n return { dump: obj['dump'], seal: seal as TransferSealPayload }\n}\n\n/**\n * Coerce an unsealed perUser entry to `AutoCredential`. Older bundles\n * store bare strings; newer bundles store `{ kind, value }` objects.\n */\nfunction coerceUnsealed(entry: AutoCredential | string): AutoCredential {\n if (typeof entry === 'string') return { kind: 'secret', value: entry }\n return entry\n}\n\n/**\n * Resolve the `_autoUnlock` blob into a typed per-user credential map.\n *\n * - For `kind: 'unsealed'`: pass through, coercing pre-0.2 bare strings\n * to `{ kind: 'secret', value }`.\n * - For `kind: 'sealed'`: pick a `SealingKeyProvider` from\n * `opts.sealingProviders` whose `.id` matches each entry's `pid`;\n * unseal to `AutoCredential`. When no provider matches AND strict mode\n * (default), throw `PodSealMismatchError`. With\n * `attemptUnsealAcrossProviders: true`, try each provider whose\n * `alg` matches the envelope.\n * Exception: if an unmatched entry carries a `hint` field (recipient-target\n * entries), it passes through as `{ kind, value: base64sealed }` rather than\n * throwing — multi-recipient bundles have N-1 unmatched entries from each\n * recipient's perspective, and the consumer is expected to ignore entries\n * not addressed to them.\n * - When `sealingProviders` is unset entirely on a `'sealed'` bundle,\n * pass through the SEALED entries as `{ kind, value: base64sealed }` —\n * the caller can inspect or unseal elsewhere.\n *\n * Pre-0.2 sealed entries missing `kind` default to `'secret'`.\n */\nasync function resolveAutoUnlock(\n blob: AutoUnlockBody['_autoUnlock'],\n opts: ReadPodOptions,\n): Promise<{ kind: 'unsealed' | 'sealed'; perUser: Record<string, AutoCredential> }> {\n if (blob.kind === 'unsealed') {\n const resolved: Record<string, AutoCredential> = {}\n for (const [userId, entry] of Object.entries(blob.perUser)) {\n resolved[userId] = coerceUnsealed(entry)\n }\n return { kind: 'unsealed', perUser: resolved }\n }\n // Sealed path.\n if (opts.sealingProviders === undefined || opts.sealingProviders.length === 0) {\n // Inspection mode — pass the sealed payload through as a typed\n // credential whose `value` is the opaque base64 sealed bytes.\n // The caller is signalled by `kind: 'sealed'` on the outer result.\n const passthrough: Record<string, AutoCredential> = {}\n for (const [userId, entry] of Object.entries(blob.perUser)) {\n passthrough[userId] = { kind: entry.kind ?? 'secret', value: entry.sealed }\n }\n return { kind: 'sealed', perUser: passthrough }\n }\n const providersByPid = new Map<string, SealingKeyProvider>()\n for (const p of opts.sealingProviders) providersByPid.set(p.id, p)\n\n const decoder = new TextDecoder()\n const unsealedMap: Record<string, AutoCredential> = {}\n\n for (const [userId, entry] of Object.entries(blob.perUser)) {\n const credKind: AutoCredentialKind = entry.kind ?? 'secret'\n const provider = providersByPid.get(entry.pid)\n if (provider === undefined) {\n if (opts.attemptUnsealAcrossProviders === true) {\n // Try each provider; first that succeeds wins.\n let opened: string | null = null\n for (const candidate of opts.sealingProviders) {\n try {\n const plaintextBytes = await candidate.unseal(base64ToBytes(entry.sealed))\n opened = decoder.decode(plaintextBytes)\n break\n } catch {\n // try next\n }\n }\n if (opened === null) {\n if (entry.hint !== undefined) {\n // Recipient-target entry not addressed to any held key — pass through sealed.\n // Other recipients' entries in a multi-recipient bundle are opaque to us.\n unsealedMap[userId] = { kind: credKind, value: entry.sealed }\n continue\n }\n throw new PodSealMismatchError(userId, entry.pid)\n }\n unsealedMap[userId] = { kind: credKind, value: opened }\n continue\n }\n if (entry.hint !== undefined) {\n // Recipient-target entry not addressed to any held key — pass through sealed.\n // Multi-recipient bundles deliberately seal each user's entry under their own\n // public key; a reader holding only alice's key will not match bob's pid.\n unsealedMap[userId] = { kind: credKind, value: entry.sealed }\n continue\n }\n throw new PodSealMismatchError(userId, entry.pid)\n }\n const plaintextBytes = await provider.unseal(base64ToBytes(entry.sealed))\n unsealedMap[userId] = { kind: credKind, value: decoder.decode(plaintextBytes) }\n }\n return { kind: 'sealed', perUser: unsealedMap }\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n let binary = ''\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]!)\n return btoa(binary)\n}\n\nfunction base64ToBytes(b64: string): Uint8Array {\n const binary = atob(b64)\n const out = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i)\n return out\n}\n\n/**\n * Detect whether the runtime's `CompressionStream` supports brotli.\n *\n * Brotli requires Node 22+ / Chrome 124+ / Firefox 122+. The\n * detection runs the `CompressionStream` constructor in a\n * try/catch — unsupported formats throw `TypeError` synchronously,\n * making this a safe one-shot check that we cache for the\n * lifetime of the process.\n */\nlet cachedBrotliSupport: boolean | null = null\nfunction supportsBrotliCompression(): boolean {\n if (cachedBrotliSupport !== null) return cachedBrotliSupport\n try {\n new CompressionStream('br' as CompressionFormat)\n cachedBrotliSupport = true\n } catch {\n cachedBrotliSupport = false\n }\n return cachedBrotliSupport\n}\n\n/** Test-only: reset the brotli detection cache between tests. */\nexport function resetBrotliSupportCache(): void {\n cachedBrotliSupport = null\n}\n\n/**\n * Pick the compression algorithm and the corresponding format byte\n * from a user option. Throws if the user explicitly requests brotli\n * on a runtime that doesn't support it — a silent fallback would\n * make the produced bundle smaller-than-expected and confuse\n * size-bound tests.\n */\nfunction selectCompression(option: WritePodOptions['compression']): {\n format: CompressionAlgo\n streamFormat: CompressionFormat | null\n} {\n const choice = option ?? 'auto'\n if (choice === 'none') return { format: COMPRESSION_NONE, streamFormat: null }\n if (choice === 'gzip') return { format: COMPRESSION_GZIP, streamFormat: 'gzip' }\n if (choice === 'brotli') {\n if (!supportsBrotliCompression()) {\n throw new Error(\n `writePod({ compression: 'brotli' }) is not supported on this ` +\n `runtime. Brotli requires Node 22+, Chrome 124+, or Firefox 122+. ` +\n `Use { compression: 'auto' } to fall back to gzip silently, or ` +\n `{ compression: 'gzip' } to be explicit.`,\n )\n }\n return { format: COMPRESSION_BROTLI, streamFormat: 'br' as CompressionFormat }\n }\n // 'auto' — prefer brotli, fall back to gzip\n if (supportsBrotliCompression()) {\n return { format: COMPRESSION_BROTLI, streamFormat: 'br' as CompressionFormat }\n }\n return { format: COMPRESSION_GZIP, streamFormat: 'gzip' }\n}\n\n/**\n * Pump a Uint8Array through a CompressionStream / DecompressionStream\n * and collect the output. Both APIs are universally available in\n * Node 18+ and modern browsers; the only variance is which\n * formats they support, handled by `selectCompression` above.\n *\n * Implementation: build a single-chunk ReadableStream from the\n * input, pipe through the transform, then drain the resulting\n * ReadableStream into a single concatenated Uint8Array. This is\n * O(N) memory in the input + output sizes, which is fine for the\n * dump-sized payloads (typically <50MB) targets.\n */\nasync function pumpThroughStream(\n input: Uint8Array,\n stream: CompressionStream | DecompressionStream,\n): Promise<Uint8Array> {\n const readable = new Blob([input as BlobPart]).stream().pipeThrough(stream)\n const reader = readable.getReader()\n const chunks: Uint8Array[] = []\n let total = 0\n for (;;) {\n const { value, done } = await reader.read()\n if (done) break\n if (value) {\n chunks.push(value as Uint8Array)\n total += value.length\n }\n }\n const out = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n out.set(chunk, offset)\n offset += chunk.length\n }\n return out\n}\n\n/**\n * SHA-256 hex digest of `bytes`. Used for the bundle integrity\n * hash carried in the header. Web Crypto API only — no Node\n * crypto module, no third-party hash library.\n *\n * The output format is lowercase hex (64 chars for SHA-256). The\n * format validator pins this — uppercase or mixed-case digests\n * are rejected, so the writer and reader agree on canonicalization.\n */\nasync function sha256Hex(bytes: Uint8Array): Promise<string> {\n // Copy into a fresh ArrayBuffer-backed Uint8Array. The\n // underlying buffer of `bytes` may be SharedArrayBuffer (e.g.\n // from a worker), which `subtle.digest` rejects via TypeScript's\n // BufferSource type. Allocating a fresh ArrayBuffer-backed view\n // sidesteps the type narrowing and is portable across all\n // runtimes — the copy cost is O(N) but bundle bodies are\n // typically <50MB, well below the threshold where the copy\n // matters.\n const copy = new Uint8Array(bytes.length)\n copy.set(bytes)\n return sha256HexBytes(copy)\n}\n\n/**\n * Concatenate any number of Uint8Arrays into a single new buffer.\n * Used to assemble the final bundle from its prefix + header +\n * body parts.\n */\nfunction concatBytes(parts: readonly Uint8Array[]): Uint8Array {\n let total = 0\n for (const p of parts) total += p.length\n const out = new Uint8Array(total)\n let offset = 0\n for (const p of parts) {\n out.set(p, offset)\n offset += p.length\n }\n return out\n}\n\n/**\n * Replace the bundle's keyrings with freshly built recipient slots,\n * one per supplied recipient. No-op when neither `exportSecret`\n * nor `recipients` is set — the source keyring is inherited as-is.\n *\n * The single-secret shorthand creates a one-recipient list whose\n * id, role, and permissions inherit from the source vault — useful\n * for \"back up to a different secret\" without changing role\n * semantics. The multi-recipient form wraps each slot independently\n * with its declared role + permissions.\n *\n * @internal\n */\nasync function applyRecipientRewrap(\n vault: Vault,\n dumpJson: string,\n opts: WritePodOptions,\n): Promise<string> {\n if (opts.exportSecret === undefined && opts.recipients === undefined) {\n return dumpJson\n }\n\n const recipients: readonly PodRecipient[] =\n opts.recipients ?? [\n {\n id: vault.userId,\n secret: opts.exportSecret as string,\n role: vault.role,\n },\n ]\n\n const recipientKeyrings = await vault.buildBundleRecipientKeyrings(recipients)\n\n const backup = JSON.parse(dumpJson) as { keyrings: unknown; [k: string]: unknown }\n backup.keyrings = recipientKeyrings\n return JSON.stringify(backup)\n}\n\n/**\n * Apply opt-in slice filters to a vault dump JSON string. Filters that\n * narrow the bundle without crossing the encryption boundary — both\n * operate on metadata (collection name, envelope `_ts`) and never need\n * to decrypt records. When neither filter is set, the dump is returned\n * unchanged so the no-arg path stays a pure passthrough.\n *\n * Internal-collection filtering: when a `collections` allowlist is\n * provided, the bundle still carries `_internal` (ledger entries) and\n * the keyrings — they're necessary for the receiver to verify and\n * unlock the bundle. The allowlist applies to the user-collection\n * map only.\n *\n * @internal\n */\nfunction applySliceFilters(\n dumpJson: string,\n opts: WritePodOptions,\n): string {\n const collectionsFilter = opts.collections\n ? new Set(opts.collections)\n : null\n const sinceMs =\n opts.since !== undefined ? new Date(opts.since).getTime() : null\n if (collectionsFilter === null && sinceMs === null) return dumpJson\n\n // Parse, prune, re-serialize. The dump shape is stable\n // (VaultBackup) so this is a one-off allocation; for vaults beyond\n // the documented 1K–50K target a streaming variant would be a\n // follow-up, but the simple parse path keeps the slice path\n // type-safe and trivially auditable.\n const backup = JSON.parse(dumpJson) as {\n collections?: Record<string, Record<string, { _ts?: string }>>\n [k: string]: unknown\n }\n\n if (backup.collections && typeof backup.collections === 'object') {\n const next: Record<string, Record<string, unknown>> = {}\n for (const [name, records] of Object.entries(backup.collections)) {\n if (collectionsFilter && !collectionsFilter.has(name)) continue\n if (sinceMs === null) {\n next[name] = records\n continue\n }\n const kept: Record<string, unknown> = {}\n for (const [id, env] of Object.entries(records)) {\n const envTs = env._ts ? new Date(env._ts).getTime() : NaN\n if (Number.isFinite(envTs) && envTs >= sinceMs) {\n kept[id] = env\n }\n }\n next[name] = kept\n }\n backup.collections = next as typeof backup.collections\n }\n\n return JSON.stringify(backup)\n}\n\n/**\n * Unconditionally drop satellite-collection records whose base row is dead\n * (absent or tombstoned — existence.ts rule 1) from a vault dump. Unlike\n * `applySliceFilters` / `applyPlaintextFilters`, this isn't opt-in: a\n * satellite envelope that outlived its base is dead ciphertext (plus its\n * wrapped keys) that should never leave the vault in a `.noydb` backup\n * (#591 Task 10 — closes the last enumerated existence-authority surface).\n *\n * Pairing info comes from the persisted `_schemas/<name>` marker rather\n * than the in-memory `SatelliteRegistry` — that registry is private to\n * `Vault` and the bundle-export path only has `Vault._introspectState()`\n * (adapter + vault name + per-collection DEK accessor), so it reads the\n * marker instead of widening the kernel's surface. See\n * `with-shape/satellites/dead-filter.ts`.\n *\n * No-ops (returns `dumpJson` unchanged) when the vault has no detectable\n * satellite collections at all, so vaults that never use satellites pay\n * no extra parse/reserialize cost. Also no-ops when `vault` doesn't\n * implement `_introspectState()` at all — some call sites pass a minimal\n * duck-typed vault-like object for unit tests (e.g.\n * `snapshots.test.ts`'s `makeMockVault`, which implements only\n * `getPodHandle`/`dump`/`load`/`getCover`); a real `Vault`\n * always implements it.\n *\n * @internal\n */\nasync function applySatelliteLivenessFilter(\n vault: Vault,\n dumpJson: string,\n): Promise<string> {\n const backup = JSON.parse(dumpJson) as {\n collections?: Record<string, Record<string, unknown>>\n [k: string]: unknown\n }\n if (!backup.collections || typeof backup.collections !== 'object') return dumpJson\n if (typeof vault._introspectState !== 'function') return dumpJson\n\n const { adapter, name, getDEK } = vault._introspectState()\n const { liveBaseIdSetsForBundle } = await import('../with-shape/satellites/dead-filter.js')\n const satLive = await liveBaseIdSetsForBundle(adapter, name, Object.keys(backup.collections), getDEK)\n if (satLive.size === 0) return dumpJson\n\n const next: Record<string, Record<string, unknown>> = {}\n for (const [collName, records] of Object.entries(backup.collections)) {\n const live = satLive.get(collName)\n if (!live) { next[collName] = records; continue }\n const kept: Record<string, unknown> = {}\n for (const [id, env] of Object.entries(records)) {\n if (live.has(id)) kept[id] = env\n }\n next[collName] = kept\n }\n backup.collections = next\n return JSON.stringify(backup)\n}\n\n/**\n * Apply opt-in plaintext-tier filters\n * to a vault dump. Operates BEFORE `applySliceFilters` so the metadata\n * pass sees the trimmed record set.\n *\n * The filter never re-encrypts: surviving records carry their original\n * envelope unchanged. Failing records are dropped from the\n * `collections` map. Internal collections (ledger, deltas) and the\n * keyrings map are untouched.\n *\n * @internal\n */\nasync function applyPlaintextFilters(\n vault: Vault,\n dumpJson: string,\n opts: WritePodOptions,\n): Promise<string> {\n if (opts.where === undefined && opts.tierAtMost === undefined) {\n return dumpJson\n }\n\n type Env = { _ts?: string; _tier?: number; _iv: string; _data: string }\n const backup = JSON.parse(dumpJson) as {\n collections?: Record<string, Record<string, Env>>\n [k: string]: unknown\n }\n if (!backup.collections || typeof backup.collections !== 'object') {\n return dumpJson\n }\n\n const tierCeiling = opts.tierAtMost\n const where = opts.where\n\n const next: Record<string, Record<string, Env>> = {}\n for (const [collName, records] of Object.entries(backup.collections)) {\n const kept: Record<string, Env> = {}\n for (const [id, env] of Object.entries(records)) {\n // Tier ceiling — runs FIRST so we don't waste a decrypt on\n // records about to be dropped anyway. Envelope tier defaults to\n // 0 when absent (matches Vault's tier-0 conventions).\n if (tierCeiling !== undefined) {\n const tier = env._tier ?? 0\n if (tier > tierCeiling) continue\n }\n // Plaintext predicate — decrypt, run, keep on truthy. Errors\n // from inside the predicate propagate (callers want to see why\n // their filter blew up rather than getting a silent passthrough).\n if (where !== undefined) {\n const record = await vault._decryptEnvelopeForBundleFilter(\n env as never,\n collName,\n id,\n )\n const ok = await where(record, { collection: collName, id })\n if (!ok) continue\n }\n kept[id] = env\n }\n next[collName] = kept\n }\n backup.collections = next\n return JSON.stringify(backup)\n}\n\n/**\n * Write a `.noydb` bundle for the given vault.\n *\n * Pipeline:\n * 1. Resolve or create the compartment's stable bundle handle\n * via `vault.getPodHandle()` — same handle on\n * every export from the same vault instance, so cloud\n * adapters can use it as a primary key.\n * 2. `vault.dump()` → JSON string with encrypted records\n * inside.\n * 3. UTF-8 encode the dump string.\n * 4. Compress (brotli if available, gzip fallback by default).\n * 5. Compute SHA-256 of the compressed body for integrity.\n * 6. Build the minimum-disclosure header from format version,\n * handle, body length, body sha.\n * 7. Serialize: magic (4) + flags (1) + algo (1) + headerLen (4)\n * + header JSON (N) + compressed body (M).\n *\n * The output is a single `Uint8Array`. Consumers writing to disk\n * pass it to `fs.writeFile`; consumers uploading to cloud storage\n * pass it as the request body. The `@noy-db/file` adapter wraps\n * this with a `saveBundle(path, vault)` helper.\n */\n/**\n * Assemble the final `.noydb` container bytes from a body JSON string +\n * header extras. Shared by `writePod` and `extractPartition`\n * so both producers go through one compress/hash/prefix path.\n *\n * @internal\n */\nexport async function assemblePodContainer(opts: {\n handle: string\n bodyJsonStr: string\n compression: WritePodOptions['compression']\n /** Header fields beyond the always-present four. */\n headerExtras?: Partial<Pick<NoydbPodHeader,\n | 'publicEnvelope' | 'autoUnlock' | 'bundleKind' | 'transferSeal'\n | 'engineRange' | 'unlockMethods' | 'hasApp' | 'species' | 'pointerMode' | 'redirect'\n >>\n /**\n * When present, the assembled header is signed (#943): the header is\n * bumped to `formatVersion: 2` and carries the sig/keyId/sigAlg tuple.\n * Absent → an unsigned `formatVersion: 1` header (partitions, and pods\n * from vaults without a signer, stay unsigned).\n */\n signer?: DocSigner\n}): Promise<Uint8Array> {\n const dumpBytes = new TextEncoder().encode(opts.bodyJsonStr)\n const { format, streamFormat } = selectCompression(opts.compression)\n const body = streamFormat === null\n ? dumpBytes\n : await pumpThroughStream(dumpBytes, new CompressionStream(streamFormat))\n const bodySha256 = await sha256Hex(body)\n\n const header: NoydbPodHeader = {\n formatVersion: NOYDB_POD_FORMAT_VERSION,\n handle: opts.handle,\n bodyBytes: body.length,\n bodySha256,\n ...(opts.headerExtras?.publicEnvelope !== undefined ? { publicEnvelope: opts.headerExtras.publicEnvelope } : {}),\n ...(opts.headerExtras?.autoUnlock !== undefined ? { autoUnlock: opts.headerExtras.autoUnlock } : {}),\n ...(opts.headerExtras?.bundleKind !== undefined ? { bundleKind: opts.headerExtras.bundleKind } : {}),\n ...(opts.headerExtras?.transferSeal !== undefined ? { transferSeal: opts.headerExtras.transferSeal } : {}),\n ...(opts.headerExtras?.engineRange !== undefined ? { engineRange: opts.headerExtras.engineRange } : {}),\n ...(opts.headerExtras?.unlockMethods !== undefined ? { unlockMethods: opts.headerExtras.unlockMethods } : {}),\n ...(opts.headerExtras?.hasApp !== undefined ? { hasApp: opts.headerExtras.hasApp } : {}),\n ...(opts.headerExtras?.species !== undefined ? { species: opts.headerExtras.species } : {}),\n ...(opts.headerExtras?.pointerMode !== undefined ? { pointerMode: opts.headerExtras.pointerMode } : {}),\n ...(opts.headerExtras?.redirect !== undefined ? { redirect: opts.headerExtras.redirect } : {}),\n }\n // Header signing (#943): sign the header object as it will stand at\n // formatVersion 2 WITH keyId + sigAlg but WITHOUT `sig`, then attach the\n // resulting 3-tuple + bump the version. `signRecord`/`canonicalJson`\n // throw on any `undefined`, so the signed payload is built with only the\n // fields actually present (spread of the header carries no undefined\n // keys). Only produce formatVersion 2 WHEN signing — an unsigned pod\n // stays formatVersion 1 with no sig fields.\n const signed: NoydbPodHeader = opts.signer === undefined\n ? header\n : await (async (s: DocSigner): Promise<NoydbPodHeader> => {\n const toSign = { ...header, formatVersion: NOYDB_POD_FORMAT_VERSION_SIGNED, keyId: s.keyId, sigAlg: 'ed25519' as const }\n const sig = await signRecord(s.privateKeyPkcs8B64, toSign)\n return { ...toSign, sig }\n })(opts.signer)\n const headerBytes = encodePodHeader(signed)\n\n const prefix = new Uint8Array(NOYDB_POD_PREFIX_BYTES)\n prefix.set(NOYDB_POD_MAGIC, 0)\n prefix[4] = (streamFormat === null ? 0 : FLAG_COMPRESSED) | FLAG_HAS_INTEGRITY_HASH\n prefix[5] = format\n writeUint32BE(prefix, 6, headerBytes.length)\n\n return concatBytes([prefix, headerBytes, body])\n}\n\nexport async function writePod(\n vault: Vault,\n opts: WritePodOptions = {},\n): Promise<Uint8Array> {\n assertKnownWritePodOptions(opts)\n if (opts.exportSecret !== undefined && opts.recipients !== undefined) {\n throw new Error(\n 'writePod: pass either exportSecret or recipients, not both',\n )\n }\n\n // Auto-unlock: normalize once, validate + build from the\n // same NormalizedAutoUnlock object so there's no double-normalize call.\n const normalizedAutoUnlock = normalizeAutoUnlock(opts)\n const autoUnlockMode = validateAutoUnlockOptions(opts, normalizedAutoUnlock)\n\n const handle = await vault.getPodHandle()\n const dumpJson = await vault.dump()\n\n // Satellite existence-authority filter (#591 Task 10) — unconditional,\n // runs before every other filter so dead-ciphertext satellite records\n // never reach recipient rewrap or the opt-in filters below.\n const satelliteFiltered = await applySatelliteLivenessFilter(vault, dumpJson)\n\n // Re-keying: when caller supplied recipients (or the single-recipient\n // shorthand), substitute the bundle's `keyrings` map with freshly\n // built recipient slots before slice filters run.\n const rekeyed = await applyRecipientRewrap(vault, satelliteFiltered, opts)\n // Plaintext-tier filters run BEFORE\n // the metadata-only slice — that way the metadata pass sees the\n // already-trimmed record set and the two filter chains compose\n // cleanly.\n const plainFiltered = await applyPlaintextFilters(vault, rekeyed, opts)\n const filtered = applySliceFilters(plainFiltered, opts)\n\n // If no auto-unlock requested, body remains the raw dump JSON.\n // Otherwise build the wrapped body containing the\n // dump + `_autoUnlock` blob and serialize.\n const bodyJsonStr = normalizedAutoUnlock === null\n ? filtered\n : JSON.stringify(await buildAutoUnlockWrapper(filtered, normalizedAutoUnlock))\n // Snapshot the source vault's cover into the header when one is\n // persisted. `Vault.getCover` tolerates a missing document and\n // returns undefined, which we propagate as \"no cover in the\n // header.\" Vaults without a `_meta/public-envelope` document\n // (the frozen wire name) produce minimum-disclosure headers\n // exactly like before, preserving back-compat.\n const cover = await vault.getCover()\n\n // Resolve the header signer (#943): explicit `false` disables signing;\n // an explicit DocSigner is used verbatim (no vault touch); otherwise fall\n // back to the vault's persisted signer, which is `null` (unsigned) when\n // none was ever minted — export never mints one.\n const signer = opts.sign === false\n ? undefined\n : opts.sign !== undefined\n ? opts.sign\n : (await vault._loadPodSigner()) ?? undefined\n\n return assemblePodContainer({\n handle,\n bodyJsonStr,\n compression: opts.compression,\n headerExtras: {\n // `publicEnvelope` is the frozen wire key for the cover (#799).\n ...(cover !== undefined ? { publicEnvelope: cover } : {}),\n ...(autoUnlockMode !== null ? { autoUnlock: autoUnlockMode } : {}),\n // Header L2 fields (#942) — passed through verbatim from opts.\n ...(opts.engineRange !== undefined ? { engineRange: opts.engineRange } : {}),\n ...(opts.unlockMethods !== undefined ? { unlockMethods: opts.unlockMethods } : {}),\n ...(opts.hasApp !== undefined ? { hasApp: opts.hasApp } : {}),\n ...(opts.species !== undefined ? { species: opts.species } : {}),\n ...(opts.pointerMode !== undefined ? { pointerMode: opts.pointerMode } : {}),\n ...(opts.redirect !== undefined ? { redirect: opts.redirect } : {}),\n },\n ...(signer !== undefined ? { signer } : {}),\n })\n}\n\n\n/**\n * Internal helper shared by both readers — parses just the prefix\n * + header region of a bundle without touching the body. Returns\n * the parsed header plus the offset where the body starts and the\n * compression algorithm needed to decompress it.\n *\n * Throws on any format violation: missing/invalid magic, truncated\n * prefix, header length larger than the file, or unknown\n * compression algorithm.\n */\nfunction parsePrefixAndHeader(bytes: Uint8Array): {\n header: NoydbPodHeader\n bodyOffset: number\n algo: CompressionAlgo\n flags: number\n} {\n if (!hasNoydbPodMagic(bytes)) {\n throw new Error(\n `Not a .noydb bundle: missing 'NDB1' magic prefix. The first 4 bytes ` +\n `are ${[...bytes.slice(0, 4)].map((b) => b.toString(16).padStart(2, '0')).join(' ')}.`,\n )\n }\n if (bytes.length < NOYDB_POD_PREFIX_BYTES) {\n throw new Error(\n `Truncated .noydb bundle: file is only ${bytes.length} bytes, ` +\n `which is less than the ${NOYDB_POD_PREFIX_BYTES}-byte fixed prefix.`,\n )\n }\n const flags = bytes[4]!\n const algo = bytes[5]!\n if (algo !== COMPRESSION_NONE && algo !== COMPRESSION_GZIP && algo !== COMPRESSION_BROTLI) {\n throw new Error(\n `.noydb bundle declares unknown compression algorithm ${algo}. ` +\n `Known values: 0 (none), 1 (gzip), 2 (brotli).`,\n )\n }\n const headerLength = readUint32BE(bytes, 6)\n const bodyOffset = NOYDB_POD_PREFIX_BYTES + headerLength\n if (bodyOffset > bytes.length) {\n throw new Error(\n `Truncated .noydb bundle: declared header length ${headerLength} ` +\n `would extend past end of file (${bytes.length} bytes).`,\n )\n }\n const headerBytes = bytes.slice(NOYDB_POD_PREFIX_BYTES, bodyOffset)\n const header = decodePodHeader(headerBytes)\n return { header, bodyOffset, algo: algo as CompressionAlgo, flags }\n}\n\n/**\n * Read just the bundle header — no body decompression, no\n * integrity verification. Intended for cloud-listing UIs that want\n * to show the handle and size before downloading the full body.\n *\n * Returns the same `NoydbPodHeader` shape as the writer, with\n * minimum-disclosure validation already applied.\n *\n * **Cost** — O(prefix + header bytes). The header is normally well\n * under 1 KB, but may grow to roughly 256 KB when a `publicEnvelope`\n * with an inline icon is present. Cloud-listing UIs that previously\n * assumed sub-KB header reads should account for this when sizing\n * range requests against bundles that may carry icons.\n */\nexport function readPodHeader(bytes: Uint8Array): NoydbPodHeader {\n return parsePrefixAndHeader(bytes).header\n}\n\n\n/**\n * Outcome of {@link verifyPodHeader}.\n *\n * - `verified` — the header carried a sig-tuple, its `keyId` is in\n * `trustedKeys`, and the signature checks out over the canonical\n * header bytes.\n * - `unsigned` — the header carried no signature (a legacy v1 pod, or a\n * v2 pod written with `{ sign: false }`). Not an error; just unauthenticated.\n * - `untrusted` — the header is signed but its `keyId` is not one the\n * caller trusts. The signature was NOT checked.\n * - `tampered` — the header is signed by a trusted key but the signature\n * does not verify: the signed bytes were altered, or the mapped public\n * key is not the one that actually signed.\n */\nexport interface PodVerifyResult {\n readonly status: 'verified' | 'unsigned' | 'untrusted' | 'tampered'\n /** The header's `keyId`, present whenever the header carried a sig-tuple. */\n readonly keyId?: string\n}\n\n/**\n * Authenticate a pod header (#943) — a pure, dependency-free, WebCrypto-only\n * verifier. Given only the raw pod bytes and a `trustedKeys` map\n * (`keyId → publicKeyB64` the caller already trusts), it reports whether the\n * header was signed by a trusted document signer.\n *\n * Pairs with the signing half in `assemblePodContainer`/`writePod`: the\n * signed payload is the final wire header with `sig` removed, so verification\n * reconstructs it by stripping EXACTLY the `sig` field — `keyId`, `sigAlg`,\n * and every other header field stay in the verified payload.\n *\n * No store, enclave, or vault dependency — reachable from a static page with\n * only the pod bytes, the trusted-key map, and `globalThis.crypto`.\n *\n * SECURITY — authenticity is not integrity. A `verified` result proves the\n * header (which includes the *claimed* `bodySha256`) is signed by a trusted\n * key; it does NOT prove the body matches that hash. To trust the pod's\n * BODY you MUST also confirm the body hashes to `header.bodySha256` — call\n * `readPod`, which throws `PodIntegrityError` on mismatch. A `verified`\n * header paired with a swapped body is caught only by that integrity check.\n * This function stays body-free by design so a static page can authenticate a\n * header without decompressing the body; composing the two checks is the\n * caller's responsibility. See `docs/subsystems/pod-signature.md`.\n */\nexport async function verifyPodHeader(\n bytes: Uint8Array,\n trustedKeys: Readonly<Record<string, string>>,\n): Promise<PodVerifyResult> {\n const { header } = parsePrefixAndHeader(bytes)\n\n // No signature tuple → unsigned, regardless of formatVersion. The format\n // layer guarantees sig/keyId/sigAlg are all-or-nothing, so any one being\n // absent means the header is unsigned.\n if (header.sig === undefined || header.keyId === undefined || header.sigAlg === undefined) {\n return { status: 'unsigned' }\n }\n\n const publicKeyB64 = trustedKeys[header.keyId]\n if (publicKeyB64 === undefined) {\n return { status: 'untrusted', keyId: header.keyId }\n }\n\n // Reconstruct the signed payload: the FINAL wire header minus EXACTLY `sig`.\n const payload: Record<string, unknown> = { ...header }\n delete payload['sig']\n\n const ok = await verifyRecord(publicKeyB64, header.sig, payload)\n return { status: ok ? 'verified' : 'tampered', keyId: header.keyId }\n}\n\n/**\n * Read just the bundle's cover (`https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/public-envelope.md`)\n * — without verifying the body or even parsing the dump JSON. Pass\n * the raw bundle bytes; receive the owner-curated metadata or\n * `undefined` if the bundle was written without one.\n *\n * Locale-resolves any `name` / `description` map fields when `locale`\n * is supplied. Omitting `locale` returns the raw cover.\n *\n * Same security caveat as the on-vault read path — the cover is an\n * **untrusted hint** in v1; the encrypted body remains\n * the source of truth for vault contents.\n */\nexport function readPodCover(\n bytes: Uint8Array,\n opts: { readonly locale?: string } = {},\n): Cover | undefined {\n const header = parsePrefixAndHeader(bytes).header\n const env = header.publicEnvelope\n if (!env) return undefined\n if (opts.locale === undefined) return env\n return {\n ...env,\n ...(env.name !== undefined ? { name: pickLocale(env.name, opts.locale, env.defaultLocale) } : {}),\n ...(env.description !== undefined ? { description: pickLocale(env.description, opts.locale, env.defaultLocale) } : {}),\n }\n}\n\n/**\n * Read just the bundle's Redirect record (#944) — without verifying the\n * body or even parsing the dump JSON. Pass the raw bundle bytes; receive\n * the header's `redirect` field or `undefined` if the bundle was written\n * without one.\n *\n * The record is returned UNVERIFIED: `validatePodHeaderFields` only checks\n * its shape at parse time (a parser has no `trustedKeys`). Callers that\n * intend to follow the redirect MUST separately call `verifyRedirect`\n * (`redirect.js`) before trusting `target`.\n */\nexport function readPodRedirect(bytes: Uint8Array): Redirect | undefined {\n return parsePrefixAndHeader(bytes).header.redirect\n}\n\n/**\n * Read a full `.noydb` bundle: validate magic + header, verify\n * integrity hash over the body bytes, decompress, and return the\n * original `vault.dump()` JSON string ready to pass to\n * `vault.load()`.\n *\n * Throws `PodIntegrityError` if the body's actual SHA-256 does\n * not match the value declared in the header. Distinct from a\n * format error so consumers can pattern-match in catch blocks\n * (corrupted-in-transit vs malformed-by-producer).\n *\n * Note: this function does NOT take a secret. The dump JSON\n * inside the body still contains encrypted records — restoring\n * the vault requires `vault.load(dumpJson, secret)`\n * after this call. Splitting the layers keeps the bundle module\n * free of crypto concerns and lets the same code feed format\n * inspectors that never decrypt anything.\n */\nexport async function readPod(\n bytes: Uint8Array,\n opts: ReadPodOptions = {},\n): Promise<PodReadResult> {\n const { header, bodyOffset, algo } = parsePrefixAndHeader(bytes)\n const body = bytes.slice(bodyOffset)\n\n // Length check before hash check — a length mismatch is the\n // cheapest tamper signal and produces a more actionable error.\n if (body.length !== header.bodyBytes) {\n throw new PodIntegrityError(\n `body length ${body.length} does not match header.bodyBytes ` +\n `${header.bodyBytes}. The bundle was truncated or padded ` +\n `between write and read.`,\n )\n }\n\n const actualSha = await sha256Hex(body)\n if (actualSha !== header.bodySha256) {\n throw new PodIntegrityError(\n `body sha256 ${actualSha} does not match header.bodySha256 ` +\n `${header.bodySha256}. The bundle bytes were modified between ` +\n `write and read — refuse to decompress.`,\n )\n }\n\n let dumpBytes: Uint8Array\n if (algo === COMPRESSION_NONE) {\n dumpBytes = body\n } else {\n const streamFormat: CompressionFormat =\n algo === COMPRESSION_BROTLI ? ('br' as CompressionFormat) : 'gzip'\n try {\n dumpBytes = await pumpThroughStream(body, new DecompressionStream(streamFormat))\n } catch (err) {\n throw new PodIntegrityError(\n `decompression failed: ${(err as Error).message}. The bundle ` +\n `passed the integrity hash but the body is not valid ` +\n `${streamFormat} data — likely a producer bug.`,\n )\n }\n }\n\n const bodyString = new TextDecoder('utf-8', { fatal: true }).decode(dumpBytes)\n\n // When the header signals auto-unlock, the body is a\n // JSON wrapper carrying the dump string + the auto-unlock blob.\n // When absent, the body IS the raw dump JSON.\n if (header.autoUnlock === undefined) {\n return { header, dumpJson: bodyString }\n }\n const { dump, blob } = parseAutoUnlockBody(bodyString)\n const autoUnlock = await resolveAutoUnlock(blob, opts)\n return { header, dumpJson: dump, autoUnlock }\n}\n\n"],"mappings":"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","names":["userCount","plaintextBytes","sha256Hex"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/via/money/iso4217.ts","../src/via/money/fixed-point.ts","../src/via/money/paths.ts","../src/via/money/normalize.ts","../src/via/money/where.ts","../src/via/money/money-reducer.ts","../src/via/money/binding.ts","../src/via/money/descriptor.ts","../src/via/money/exact.ts"],"sourcesContent":["/**\n * ISO 4217 minor-unit (scale) lookup.\n *\n * Maps a currency code to the number of decimal places its minor unit\n * uses — `EUR` → 2 (cents), `JPY` → 0 (no minor unit), `BHD` → 3 (fils).\n * Only listed codes are \"known\"; an unlisted code returns `null` so the\n * descriptor layer can demand an explicit `scale` / `scaleOverrides`\n * rather than silently assuming 2. This avoids the classic bug where a\n * 0-decimal currency (JPY) is stored as if it had cents.\n *\n * Coverage is the common ISO-4217 set; exotic or non-listed codes must\n * declare scale explicitly. This table is the single source of truth for\n * default scale resolution — see {@link scaleForCurrency}.\n */\nconst MINOR_UNITS: Readonly<Record<string, number>> = {\n // 2-decimal majors\n EUR: 2, USD: 2, GBP: 2, CHF: 2, CAD: 2, AUD: 2, NZD: 2, SGD: 2,\n HKD: 2, CNY: 2, INR: 2, BRL: 2, MXN: 2, ZAR: 2, RUB: 2, TRY: 2,\n PLN: 2, SEK: 2, NOK: 2, DKK: 2, CZK: 2, HUF: 2, RON: 2, ILS: 2,\n THB: 2, PHP: 2, MYR: 2, IDR: 2, AED: 2, SAR: 2, QAR: 2, EGP: 2,\n // 0-decimal\n JPY: 0, KRW: 0, ISK: 0, CLP: 0, VND: 0, XOF: 0, XAF: 0, PYG: 0,\n // 3-decimal\n BHD: 3, KWD: 3, OMR: 3, TND: 3, JOD: 3, IQD: 3, LYD: 3,\n}\n\n/**\n * Return the ISO-4217 minor-unit scale for a currency code, or `null`\n * when the code is not in the known set (caller must supply an explicit\n * scale).\n */\nexport function scaleForCurrency(code: string): number | null {\n const v = MINOR_UNITS[code]\n return v === undefined ? null : v\n}\n","/**\n * Pure fixed-point decimal core for the money descriptor.\n *\n * All conversion goes decimal-string ⇄ scaled `BigInt`. There is no\n * floating-point arithmetic anywhere: a value like `123.45` becomes\n * `12345n` purely by string manipulation, never by `value * 100` (which\n * would reintroduce the very drift money() exists to eliminate). BigInt\n * has no magnitude ceiling, so values past `Number.MAX_SAFE_INTEGER`\n * stay exact end-to-end.\n *\n * This module knows nothing about currencies, descriptors, or storage —\n * it is the isolated, exhaustively-tested arithmetic kernel.\n */\n\nexport type RoundingMode =\n | 'half-up'\n | 'half-even'\n | 'half-down'\n | 'up'\n | 'down'\n | 'ceil'\n | 'floor'\n\nexport type ParseResult =\n | { ok: true; value: bigint }\n | { ok: false; reason: 'precision' | 'nonfinite' }\n\n/**\n * Expand exponent notation (`1e-7`, `1.5e3`) into a plain decimal\n * string. Returns the input unchanged when it carries no exponent.\n */\nfunction expandExponent(s: string): string {\n const m = /^([+-]?)(\\d+)(?:\\.(\\d+))?[eE]([+-]?\\d+)$/.exec(s)\n if (!m) return s\n const sign = m[1] === '-' ? '-' : ''\n const intp = m[2]!\n const frac = m[3] ?? ''\n const exp = Number(m[4]!)\n const digits = intp + frac\n const pointPos = intp.length + exp\n let body: string\n if (pointPos <= 0) {\n body = '0.' + '0'.repeat(-pointPos) + digits\n } else if (pointPos >= digits.length) {\n body = digits + '0'.repeat(pointPos - digits.length)\n } else {\n body = digits.slice(0, pointPos) + '.' + digits.slice(pointPos)\n }\n return sign + body\n}\n\n/**\n * Normalize an input to a canonical decimal string, or `null` if it is\n * non-finite / not a valid decimal.\n */\nfunction toCanonicalDecimalString(input: number | string): string | null {\n let s: string\n if (typeof input === 'number') {\n if (!Number.isFinite(input)) return null\n s = String(input)\n } else {\n s = input.trim()\n }\n s = expandExponent(s)\n if (s.startsWith('+')) s = s.slice(1)\n // optional sign, then digits with at most one dot, at least one digit\n if (!/^-?(\\d+(\\.\\d*)?|\\.\\d+)$/.test(s)) return null\n return s\n}\n\n/**\n * Decide whether the truncated magnitude should be incremented by one\n * minor unit, given the discarded fractional tail. Only called when the\n * tail is known to contain a non-zero digit.\n */\nfunction shouldRoundUp(\n negative: boolean,\n lastKeptDigit: number,\n firstDiscarded: number,\n hasMoreNonZeroAfterFirst: boolean,\n mode: RoundingMode,\n): boolean {\n switch (mode) {\n case 'up':\n return true\n case 'down':\n return false\n case 'ceil':\n return !negative\n case 'floor':\n return negative\n case 'half-up':\n return firstDiscarded >= 5\n case 'half-down':\n return firstDiscarded > 5 || (firstDiscarded === 5 && hasMoreNonZeroAfterFirst)\n case 'half-even':\n if (firstDiscarded > 5) return true\n if (firstDiscarded < 5) return false\n // exactly 5 leading the tail\n return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1\n }\n}\n\n/**\n * Parse a decimal (`number | string`) into a scaled `BigInt` at the\n * given scale. When the input carries more fractional precision than\n * `scale`:\n * - `rounding` omitted ⇒ `{ ok: false, reason: 'precision' }`\n * - `rounding` set ⇒ the tail is rounded per the mode.\n * Non-finite / unparseable input ⇒ `{ ok: false, reason: 'nonfinite' }`.\n */\nexport function parseToScaledInt(\n input: number | string,\n scale: number,\n rounding?: RoundingMode,\n): ParseResult {\n const canonical = toCanonicalDecimalString(input)\n if (canonical === null) return { ok: false, reason: 'nonfinite' }\n\n const negative = canonical.startsWith('-')\n const unsigned = negative ? canonical.slice(1) : canonical\n const dot = unsigned.indexOf('.')\n const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot)\n const fracPart = dot === -1 ? '' : unsigned.slice(dot + 1)\n\n const intDigits = intPart === '' ? '0' : intPart\n\n if (fracPart.length <= scale) {\n const keep = fracPart.padEnd(scale, '0')\n const magnitude = BigInt(intDigits + keep)\n return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude }\n }\n\n // More precision than scale — inspect the discarded tail.\n const keep = fracPart.slice(0, scale)\n const tail = fracPart.slice(scale)\n const magnitudeDigits = intDigits + keep\n let magnitude = BigInt(magnitudeDigits)\n\n if (/^0+$/.test(tail)) {\n // tail is all zeros — exact, no rounding required.\n return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude }\n }\n\n if (rounding === undefined) return { ok: false, reason: 'precision' }\n\n const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1])\n const firstDiscarded = Number(tail[0])\n const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1))\n if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {\n magnitude += 1n\n }\n return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude }\n}\n\n/**\n * Number of fractional digits in the canonical decimal rendering of\n * `input` (`'123.45'` → 2, `100` → 0, `'1e-7'` → 7), or `null` when the\n * input is non-finite / not a decimal. Used to infer the working scale\n * of arithmetic helpers from their canonical-string inputs.\n */\nexport function decimalScaleOf(input: number | string): number | null {\n const s = toCanonicalDecimalString(input)\n if (s === null) return null\n const dot = s.indexOf('.')\n return dot === -1 ? 0 : s.length - dot - 1\n}\n\n/**\n * Re-scale a scaled `BigInt` from `fromScale` to `toScale`. Scaling up\n * is exact (append zeros); scaling down rounds the discarded tail per\n * `rounding`. The rounding decision reuses the same kernel as\n * {@link parseToScaledInt}, so `rescaleScaledInt(parse('1.005', 3), 3, 2)`\n * and `parse('1.005', 2, mode)` agree for every mode.\n */\nexport function rescaleScaledInt(\n value: bigint,\n fromScale: number,\n toScale: number,\n rounding: RoundingMode = 'half-up',\n): bigint {\n if (toScale >= fromScale) return value * 10n ** BigInt(toScale - fromScale)\n const drop = fromScale - toScale\n const negative = value < 0n\n const absStr = (negative ? -value : value).toString().padStart(drop + 1, '0')\n const keptStr = absStr.slice(0, absStr.length - drop)\n const tail = absStr.slice(absStr.length - drop)\n let magnitude = BigInt(keptStr)\n if (!/^0+$/.test(tail)) {\n const lastKeptDigit = Number(keptStr[keptStr.length - 1])\n const firstDiscarded = Number(tail[0])\n const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1))\n if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {\n magnitude += 1n\n }\n }\n return negative && magnitude !== 0n ? -magnitude : magnitude\n}\n\n/**\n * Render a scaled `BigInt` back to its canonical decimal string at the\n * given scale. `(12345n, 2)` → `'123.45'`; `(5n, 0)` → `'5'`;\n * `(-1n, 2)` → `'-0.01'`. Exact for any magnitude.\n */\nexport function formatScaledInt(value: bigint, scale: number): string {\n const negative = value < 0n\n const abs = (negative ? -value : value).toString()\n if (scale === 0) return (negative ? '-' : '') + abs\n const padded = abs.padStart(scale + 1, '0')\n const cut = padded.length - scale\n const intPart = padded.slice(0, cut)\n const fracPart = padded.slice(cut)\n return (negative ? '-' : '') + intPart + '.' + fracPart\n}\n","/**\n * Nested-path support for `moneyFields` declarations.\n *\n * A descriptor map key is a PATH, not just a top-level field name:\n *\n * - `'total'` — top-level scalar (the original form)\n * - `'billing.monthlyServiceFee'` — nested object member\n * - `'lineItems[].amount'` — member of every element of an array\n * - `'summary.*'` — every value of a record/map object\n * - `'income[].taxWithheld'`, `'byMonth.*.amount'` — segments compose\n *\n * Paths are parsed ONCE, at collection registration, and a syntactically\n * invalid declaration throws there — loudly, at setup time. (The\n * historical failure mode this kills: a declared-but-unreachable path\n * that was silently ignored, leaving the field un-quantized — a latent\n * 100× bug.) A path that doesn't match a given record at write time is\n * NOT an error — optional fields and empty arrays are legitimate — but\n * a path segment that hits a value of the WRONG SHAPE (`[]` on a\n * non-array, `.*` on a non-object) throws, because that means the\n * declaration and the data model disagree.\n */\n\nimport { ValidationError } from '../../kernel/errors.js'\n\nexport type MoneyPathSegment =\n | { readonly kind: 'key'; readonly key: string; readonly array: boolean }\n | { readonly kind: 'wildcard'; readonly array: boolean }\n\nconst SEGMENT_RE = /^(\\*|[^.[\\]*]+)(\\[\\])?$/\n\nconst parseCache = new Map<string, readonly MoneyPathSegment[]>()\n\n/**\n * Parse a moneyFields path into segments. Throws `ValidationError` on\n * bad syntax. Results are memoized — the same declared paths are walked\n * on every write/read.\n */\nexport function parseMoneyPath(path: string): readonly MoneyPathSegment[] {\n const cached = parseCache.get(path)\n if (cached) return cached\n\n if (typeof path !== 'string' || path.length === 0) {\n throw new ValidationError('moneyFields: path must be a non-empty string')\n }\n const segments: MoneyPathSegment[] = []\n for (const part of path.split('.')) {\n const m = SEGMENT_RE.exec(part)\n if (!m) {\n throw new ValidationError(\n `moneyFields: invalid path \"${path}\" — segment \"${part}\" must be a key, \"key[]\", \"*\", or \"*[]\"`,\n )\n }\n const array = m[2] === '[]'\n segments.push(\n m[1] === '*' ? { kind: 'wildcard', array } : { kind: 'key', key: m[1]!, array },\n )\n }\n parseCache.set(path, segments)\n return segments\n}\n\n/** True when the path is a plain top-level field name (the fast path). */\nexport function isSimpleMoneyPath(path: string): boolean {\n return !path.includes('.') && !path.includes('[') && !path.includes('*')\n}\n\n/**\n * Validate every declared path's syntax. Call at collection\n * registration so typos throw at setup, not silently no-op per write.\n */\nexport function validateMoneyFieldPaths(moneyFields: Record<string, unknown>): void {\n for (const path of Object.keys(moneyFields)) parseMoneyPath(path)\n}\n\n/**\n * The leaf visitor: receives the (already-cloned) container holding the\n * money value and the key/index of that value. Mutating the container\n * is safe — every container on a matched path is a fresh clone.\n */\nexport type MoneyLeafVisitor = (\n container: Record<string, unknown> | unknown[],\n key: string | number,\n) => void\n\n/**\n * Walk `node` along `segments`, copy-on-write-cloning every container on\n * a matched path, and call `visit` at each leaf position. Returns the\n * (possibly new) node; unmatched paths return the input untouched.\n *\n * Shape mismatches (`[]` on a non-array, `*` on a non-object) THROW on\n * the write path — the declaration and the data model disagree, and\n * writing through would store an un-quantized amount. On the READ path\n * pass `lenient: true` instead: stored data predating a declaration\n * change must stay readable, so a mismatched node is returned untouched\n * (mirrors the defensive `continue` the flat decoder always had).\n */\nexport function transformAtMoneyPath(\n node: unknown,\n path: string,\n segments: readonly MoneyPathSegment[],\n index: number,\n visit: MoneyLeafVisitor,\n lenient: boolean,\n): unknown {\n if (node === null || node === undefined) return node\n const seg = segments[index]!\n const last = index === segments.length - 1\n\n if (seg.kind === 'key') {\n if (typeof node !== 'object' || Array.isArray(node)) {\n if (lenient) return node\n throw new ValidationError(\n `moneyFields: path \"${path}\" expected an object at segment \"${seg.key}\", got ${Array.isArray(node) ? 'an array' : typeof node}`,\n )\n }\n const obj = node as Record<string, unknown>\n if (!(seg.key in obj) || obj[seg.key] === null || obj[seg.key] === undefined) return node\n\n if (seg.array) {\n const arr = obj[seg.key]\n if (!Array.isArray(arr)) {\n if (lenient) return node\n throw new ValidationError(\n `moneyFields: path \"${path}\" declares \"${seg.key}[]\" but the value is not an array`,\n )\n }\n const cloned = [...arr]\n if (last) {\n for (let i = 0; i < cloned.length; i++) visit(cloned, i)\n } else {\n for (let i = 0; i < cloned.length; i++) {\n cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient)\n }\n }\n return { ...obj, [seg.key]: cloned }\n }\n\n const clone = { ...obj }\n if (last) {\n visit(clone, seg.key)\n } else {\n clone[seg.key] = transformAtMoneyPath(clone[seg.key], path, segments, index + 1, visit, lenient)\n }\n return clone\n }\n\n // wildcard\n if (seg.array) {\n if (!Array.isArray(node)) {\n if (lenient) return node\n throw new ValidationError(`moneyFields: path \"${path}\" declares \"*[]\" but the value is not an array`)\n }\n const cloned = [...node]\n if (last) {\n for (let i = 0; i < cloned.length; i++) visit(cloned, i)\n } else {\n for (let i = 0; i < cloned.length; i++) {\n cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient)\n }\n }\n return cloned\n }\n if (typeof node !== 'object' || Array.isArray(node)) {\n if (lenient) return node\n throw new ValidationError(\n `moneyFields: path \"${path}\" applies \"*\" to a non-object (${Array.isArray(node) ? 'array — use \"*[]\"' : typeof node})`,\n )\n }\n const obj = node as Record<string, unknown>\n const clone: Record<string, unknown> = { ...obj }\n for (const key of Object.keys(obj)) {\n const v = clone[key]\n if (v === null || v === undefined) continue\n if (last) visit(clone, key)\n else clone[key] = transformAtMoneyPath(v, path, segments, index + 1, visit, lenient)\n }\n return clone\n}\n","/**\n * Write-side and read-side normalization for money fields.\n *\n * - **Write** ({@link quantizeMoneyFields}): user input (`number | string`,\n * or `{ amount, currency }` in multi mode) → the canonical *stored*\n * form — a scaled-integer **digit string** (`'12345'`), or\n * `{ amount: '12345', currency: 'EUR' }` in multi mode. A JSON number\n * would truncate past 2^53, so the integer is always stored as a string.\n * - **Read** ({@link decodeMoneyFields}): stored form → an exact decimal\n * string (`'123.45'`) plus, when formatting is requested, the virtual\n * `<field>Formatted` (locale currency string) and `<field>Number`\n * (convenience JS number, explicitly lossy past 2^53).\n *\n * Both return a shallow clone; neither mutates the input record.\n */\n\nimport { parseToScaledInt, formatScaledInt } from './fixed-point.js'\nimport { MoneyPrecisionError, type MoneyDescriptor } from './descriptor.js'\nimport { isSimpleMoneyPath, parseMoneyPath, transformAtMoneyPath } from './paths.js'\n\ninterface MoneyValueObject {\n amount: unknown\n currency: unknown\n}\n\nfunction isMoneyValueObject(v: unknown): v is MoneyValueObject {\n return typeof v === 'object' && v !== null && 'currency' in v\n}\n\n/** Parse one decimal input to a stored digit string at `scale`. */\nfunction quantizeAmount(\n field: string,\n input: number | string,\n scale: number,\n rounding: MoneyDescriptor['rounding'],\n): string {\n const r = parseToScaledInt(input, scale, rounding)\n if (!r.ok) {\n if (r.reason === 'precision') throw new MoneyPrecisionError(field, input, scale)\n throw new TypeError(`money: field \"${field}\" value ${JSON.stringify(input)} is not a finite decimal`)\n }\n return r.value.toString()\n}\n\n/**\n * Canonicalize a STORED-form record's money fields for an internal\n * callback boundary. Gate handlers (guard `check` /\n * `frozenFields` / `onDelete`, period guard, amendment invariants) and\n * derivation `derive(source, ctx)` callbacks are user-facing: they must\n * see the same decoded canonical decimal that `get()` returns — the\n * scaled-int storage form never escapes. Decoded with `'raw'` (no\n * `<field>Formatted`/`<field>Number` virtuals): these boundaries carry\n * no locale, and fabricating one would re-create a two-read-paths\n * skew inside comparisons.\n */\nexport function canonicalizeStoredMoney(\n record: unknown,\n moneyFields: Record<string, MoneyDescriptor> | undefined,\n): unknown {\n if (record === null || record === undefined) return record\n if (!moneyFields || Object.keys(moneyFields).length === 0) return record\n return decodeMoneyFields(record as Record<string, unknown>, moneyFields, 'raw')\n}\n\n/**\n * Canonicalize an INCOMING record's money fields at the top of the\n * write pipeline. Raw user input (pre-quantize) may hold a\n * number (`10000`), a major-unit string (`'10000.00'`), or a spread of\n * an already-decoded read. Quantize→decode folds all three to the\n * canonical decimal string, so gate handlers, computed-field\n * callbacks, and schema validation all see the `get()` shape — and\n * freeze-style guards comparing `incoming[f]` vs `existing[f]` see\n * equal values for an unchanged field. Best-effort: input that fails\n * to quantize passes through unchanged — the write path quantizes\n * again after validation and surfaces the real\n * `MoneyPrecisionError`/`TypeError`.\n */\nexport function canonicalizeIncomingMoney(\n record: unknown,\n moneyFields: Record<string, MoneyDescriptor> | undefined,\n): unknown {\n if (!moneyFields || Object.keys(moneyFields).length === 0) return record\n try {\n return decodeMoneyFields(\n quantizeMoneyFields(record as Record<string, unknown>, moneyFields),\n moneyFields,\n 'raw',\n )\n } catch {\n return record\n }\n}\n\n/** Quantize ONE field value (any nesting level) to its stored form. */\nfunction quantizeValue(field: string, raw: unknown, desc: MoneyDescriptor): unknown {\n if (desc.mode === 'fixed') {\n const currency = desc.fixedCurrency!\n return quantizeAmount(field, raw as number | string, desc.scaleFor(currency), desc.rounding)\n }\n // multi mode\n let amount: number | string\n let currency: string\n if (isMoneyValueObject(raw)) {\n currency = String(raw.currency)\n amount = raw.amount as number | string\n } else {\n const sole = desc.soleCurrency()\n if (sole === undefined) {\n throw new TypeError(\n `money: field \"${field}\" is multi-currency — write { amount, currency }, not a bare amount`,\n )\n }\n currency = sole\n amount = raw as number | string\n }\n const scale = desc.scaleFor(currency) // throws MoneyCurrencyError if disallowed\n return { amount: quantizeAmount(field, amount, scale, desc.rounding), currency }\n}\n\n/**\n * Canonicalize money values for a MATERIALIZED-VIEW row (#1018).\n *\n * A money field in an MV row is NOT in the scaled-integer storage form a\n * collection uses — the money-aware reducers emit `formatScaledInt(...)`, an\n * exact decimal string, and that is what lands in the output collection. So a\n * derived money field has to be canonicalized into the same shape as the\n * aggregated ones beside it.\n *\n * Quantizing it into storage form instead is what #1018 reported: `toPay` came\n * back as `\"1000000\"` where `netTotal` in the SAME row read `\"10000.00\"` —\n * 100× the true value, and a string that looks like a plausible amount rather\n * than an obvious error.\n *\n * Precision handling is identical to {@link quantizeMoneyFields} — the same\n * `parseToScaledInt` and the same `MoneyPrecisionError` — so a value that\n * cannot be represented at the declared scale is still refused rather than\n * silently rounded. Only the OUTPUT shape differs: decimal, not scaled.\n */\nexport function canonicalizeMoneyFieldsAsDecimal<T extends Record<string, unknown>>(\n record: T,\n moneyFields: Record<string, MoneyDescriptor>,\n): T {\n const out: Record<string, unknown> = { ...record }\n for (const [field, desc] of Object.entries(moneyFields)) {\n // Only simple top-level fields: an MV row is the flat product of\n // groupBy + aggregate + derive, so there are no nested money paths here.\n if (!isSimpleMoneyPath(field)) continue\n const raw = out[field]\n if (raw === null || raw === undefined) continue\n\n let amount: number | string\n let scale: number\n if (desc.mode === 'fixed') {\n amount = raw as number | string\n scale = desc.scaleFor(desc.fixedCurrency!)\n } else if (isMoneyValueObject(raw)) {\n amount = raw.amount as number | string\n scale = desc.scaleFor(String(raw.currency))\n } else {\n const sole = desc.soleCurrency()\n if (sole === undefined) {\n throw new TypeError(\n `money: field \"${field}\" is multi-currency — a derived value must be ` +\n '{ amount, currency }, not a bare amount',\n )\n }\n amount = raw as number | string\n scale = desc.scaleFor(sole)\n }\n\n const r = parseToScaledInt(amount, scale, desc.rounding)\n if (!r.ok) {\n if (r.reason === 'precision') throw new MoneyPrecisionError(field, amount, scale)\n throw new TypeError(`money: field \"${field}\" value ${JSON.stringify(amount)} is not a finite decimal`)\n }\n const decimal = formatScaledInt(r.value, scale)\n out[field] = desc.mode === 'fixed' || !isMoneyValueObject(raw)\n ? decimal\n : { amount: decimal, currency: String(raw.currency) }\n }\n return out as T\n}\n\n/**\n * Convert money fields in `record` from user input to their canonical\n * stored form. Returns a shallow clone (deep along declared nested\n * paths). A nested path whose declared shape disagrees with the\n * data throws: writing through would store an un-quantized amount.\n */\nexport function quantizeMoneyFields<T extends Record<string, unknown>>(\n record: T,\n moneyFields: Record<string, MoneyDescriptor>,\n): T {\n let out: Record<string, unknown> = { ...record }\n for (const [path, desc] of Object.entries(moneyFields)) {\n if (isSimpleMoneyPath(path)) {\n const raw = out[path]\n if (raw === null || raw === undefined) continue\n out[path] = quantizeValue(path, raw, desc)\n continue\n }\n out = transformAtMoneyPath(out, path, parseMoneyPath(path), 0, (container, key) => {\n const raw = (container as Record<string | number, unknown>)[key]\n if (raw === null || raw === undefined) return\n ;(container as Record<string | number, unknown>)[key] = quantizeValue(path, raw, desc)\n }, /* lenient */ false) as Record<string, unknown>\n }\n return out as T\n}\n\nfunction formatCurrency(decimal: string, currency: string, scale: number, locale: string): string {\n const fmt = new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n minimumFractionDigits: scale,\n maximumFractionDigits: scale,\n })\n // V8's Intl.format accepts a decimal STRING and formats it at full\n // precision (exact past 2^53). The TS lib types only declare\n // number|bigint, so cast — a number arg here would re-introduce the\n // float drift this whole feature exists to eliminate.\n return (fmt.format as unknown as (value: string) => string)(decimal)\n}\n\n/**\n * The stored scaled-integer value of a money field as a `BigInt`, for\n * exact numeric comparison (e.g. `orderBy` / sorting), or `null` when the\n * stored value is missing/malformed. Mirrors the stored form `decodeValue`\n * reads: a bare scaled-int (fixed mode) or `{ amount, currency }`\n * (multi-currency). Comparison is in scaled space and is exact within a\n * single currency/scale (the `where` / `sum` BigInt model); across\n * currencies of different scales the raw scaled comparison is best-effort.\n */\nexport function moneyScaledValue(stored: unknown, desc: MoneyDescriptor): bigint | null {\n let raw: unknown\n if (desc.mode === 'fixed') {\n raw = stored\n } else {\n if (!isMoneyValueObject(stored)) return null\n raw = stored.amount\n }\n if (typeof raw !== 'string' && typeof raw !== 'number') return null\n try {\n return BigInt(String(raw))\n } catch {\n return null\n }\n}\n\n/**\n * The `ViaBinding.canonicalizeIndexKey` implementation for money (#672) —\n * bucket an eager index's money field entries by the BigInt-normalized\n * scaled-int string, not the raw stored bytes, so a pre-declaration /\n * non-canonical value (e.g. `'0100'`) lands under the SAME key a canonical\n * write produces (`'100'`). Only FIXED-mode declared money fields\n * participate — multi-mode stores `{ amount, currency }`, which has no\n * single bucketable scalar (mirrors `moneyIndexProbe`'s fixed-only gate,\n * `via/money/where.ts`). `undefined` when `field` isn't a declared\n * fixed-mode money field, or the stored value doesn't parse — in both\n * cases the caller falls back to the raw stringified bucket, which is\n * exactly what the scan (`evaluateMoneyClause`) also treats as a\n * non-match, preserving fast-path/scan parity.\n */\nexport function canonicalizeMoneyIndexKey(\n field: string,\n rawValue: unknown,\n moneyFields: Record<string, MoneyDescriptor>,\n): string | undefined {\n const desc = moneyFields[field]\n if (!desc || desc.mode !== 'fixed') return undefined\n return moneyScaledValue(rawValue, desc)?.toString()\n}\n\n/**\n * Decode ONE stored field value to its read shape, or `null` when the\n * stored value is malformed (defensive — never brick a read).\n */\nfunction decodeValue(\n stored: unknown,\n desc: MoneyDescriptor,\n): { decoded: unknown; decimal: string; currency: string; scale: number } | null {\n let currency: string\n let scaledIntString: string\n if (desc.mode === 'fixed') {\n if (typeof stored !== 'string' && typeof stored !== 'number') return null\n currency = desc.fixedCurrency!\n scaledIntString = String(stored)\n } else {\n if (!isMoneyValueObject(stored)) return null // defensive: malformed stored value\n const amount = stored.amount\n if (typeof stored.currency !== 'string' || (typeof amount !== 'string' && typeof amount !== 'number')) return null\n currency = stored.currency\n scaledIntString = String(amount)\n }\n const scale = desc.scaleFor(currency)\n let decimal: string\n try {\n decimal = formatScaledInt(BigInt(scaledIntString), scale)\n } catch {\n return null // defensive: non-integer stored value\n }\n return {\n decoded: desc.mode === 'fixed' ? decimal : { amount: decimal, currency },\n decimal,\n currency,\n scale,\n }\n}\n\n/**\n * money's `presentLate` hook (#669) — for each field in `virtualMoney` (money ∩\n * virtual-mode computed on the SAME field), quantize the computed fn's fresh\n * MAJOR-UNITS output to the descriptor's scale (with its declared rounding)\n * and present it EXACTLY like a stored money field: the exact decimal string\n * (via {@link formatScaledInt}), plus `<field>Formatted`/`<field>Number` when\n * `locale !== 'raw'`. NEVER the scaled-int decode {@link decodeMoneyFields}\n * runs for a genuinely-stored value — that would misread a virtual field's\n * raw major-unit `21` as 21 SCALED units (`'0.21'`, the #665 corruption this\n * must not reintroduce). Runs AFTER computed's `present()` (the pipeline's\n * `presentLate` fold point, `kernel/via/pipeline.ts`), so `record[field]`\n * already holds the fn's fresh output by the time this runs.\n *\n * Absent/null value → left untouched (nothing to dress). Unparseable value\n * (fails `parseToScaledInt` — e.g. excess precision with no declared\n * rounding) → left RAW, no throw: read-time dressing must never brick a\n * read. Fixed-mode fields only — a virtual field's computed output has no\n * natural `{ amount, currency }` shape to parse for multi-currency mode.\n */\nexport function presentVirtualMoneyFields<T extends Record<string, unknown>>(\n record: T,\n moneyFields: Record<string, MoneyDescriptor>,\n virtualMoney: ReadonlySet<string>,\n locale: string | undefined,\n): T {\n let out: Record<string, unknown> = record\n const format = locale !== 'raw'\n const fmtLocale = typeof locale === 'string' && locale !== 'raw' ? locale : 'en-US'\n for (const field of virtualMoney) {\n const desc = moneyFields[field]\n if (!desc || desc.mode !== 'fixed') continue\n const raw = out[field]\n if (raw === null || raw === undefined) continue\n if (typeof raw !== 'number' && typeof raw !== 'string') continue\n const currency = desc.fixedCurrency!\n const scale = desc.scaleFor(currency)\n const r = parseToScaledInt(raw, scale, desc.rounding)\n if (!r.ok) continue // unparseable / excess precision with no rounding declared — leave raw, no throw\n if (out === record) out = { ...record }\n const decimal = formatScaledInt(r.value, scale)\n out[field] = decimal\n if (format) {\n out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale)\n out[`${field}Number`] = Number(decimal)\n }\n }\n return out as T\n}\n\n/**\n * Convert money fields in `record` from stored form to the read shape:\n * an exact decimal string, plus `<field>Formatted` / `<field>Number`\n * virtuals when `locale !== 'raw'`. Returns a shallow clone (deep along\n * declared nested paths; virtuals land as siblings inside the\n * nested container, e.g. each `lineItems[]` element gains\n * `amountFormatted`, except for values held directly in arrays where a\n * scalar has no sibling slot). The decode walk is LENIENT: stored data\n * predating a declaration change must stay readable.\n */\nexport function decodeMoneyFields<T extends Record<string, unknown>>(\n record: T,\n moneyFields: Record<string, MoneyDescriptor>,\n locale: string | undefined,\n): T {\n let out: Record<string, unknown> = { ...record }\n const format = locale !== 'raw'\n const fmtLocale = typeof locale === 'string' && locale !== 'raw' ? locale : 'en-US'\n\n for (const [path, desc] of Object.entries(moneyFields)) {\n if (isSimpleMoneyPath(path)) {\n const stored = out[path]\n if (stored === null || stored === undefined) continue\n const r = decodeValue(stored, desc)\n if (r === null) continue\n out[path] = r.decoded\n if (format) {\n out[`${path}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale)\n out[`${path}Number`] = Number(r.decimal)\n }\n continue\n }\n out = transformAtMoneyPath(out, path, parseMoneyPath(path), 0, (container, key) => {\n const stored = (container as Record<string | number, unknown>)[key]\n if (stored === null || stored === undefined) return\n const r = decodeValue(stored, desc)\n if (r === null) return\n ;(container as Record<string | number, unknown>)[key] = r.decoded\n if (format && typeof key === 'string' && !Array.isArray(container)) {\n container[`${key}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale)\n container[`${key}Number`] = Number(r.decimal)\n }\n }, /* lenient */ true) as Record<string, unknown>\n }\n return out as T\n}\n","/**\n * Money-aware `where()` comparison.\n *\n * Query clauses evaluate against RAW stored records — money decode\n * happens on output only — so a money field's stored form is a\n * scaled-integer digit string (`'1000000'`) while the caller naturally\n * writes the operand in major units (`10000`, `'10000.00'`). Without a\n * rewrite the comparison is silently wrong by the scale factor, and a\n * string-vs-number comparison is excluded by `isComparable` anyway.\n *\n * Two halves:\n *\n * - {@link moneyFieldClause} runs at QUERY BUILD time: it quantizes the\n * caller's major-unit operand into stored scaled-int space via the\n * same `parseToScaledInt` path as writes, so a malformed operand\n * throws at `.where()` — not silently filters everything out.\n * - {@link evaluateMoneyClause} runs per record and compares\n * `BigInt`-exact in scaled space (exact past 2^53, like the rest of\n * the money service).\n *\n * Currency semantics (multi mode): an operand carries one currency —\n * explicit via `{ amount, currency }`, or the descriptor's sole allowed\n * currency for a bare amount. A record in a DIFFERENT currency has no\n * defined order against the operand: it matches `!=` and nothing else.\n */\n\nimport { parseToScaledInt } from './fixed-point.js'\nimport { MoneyUnsupportedError, type MoneyDescriptor } from './descriptor.js'\nimport type { Operator } from '../../kernel/query/predicate.js'\n\n/** One quantized operand value: scaled digit string + its currency. */\ninterface MoneyOperandEntry {\n readonly scaled: string\n readonly currency: string\n}\n\n/**\n * The opaque `via` clause payload for a `where()` over a declared money\n * field (see `ViaBinding.buildClause` in kernel/via/index.ts). `entries` holds\n * one element for comparison ops, two for `between` (lo, hi — same\n * currency), N for `in`.\n */\nexport interface MoneyWhereOperand {\n readonly mode: 'fixed' | 'multi'\n readonly entries: ReadonlyArray<MoneyOperandEntry>\n}\n\ninterface MoneyValueObject {\n amount: unknown\n currency: unknown\n}\n\nfunction isMoneyValueObject(v: unknown): v is MoneyValueObject {\n return typeof v === 'object' && v !== null && 'currency' in v\n}\n\n/** Quantize ONE operand value to scaled space, resolving its currency. */\nfunction parseOperand(field: string, raw: unknown, desc: MoneyDescriptor): MoneyOperandEntry {\n let amount: unknown\n let currency: string\n if (desc.mode === 'fixed') {\n currency = desc.fixedCurrency!\n amount = raw\n } else if (isMoneyValueObject(raw)) {\n currency = String(raw.currency)\n amount = raw.amount\n } else {\n const sole = desc.soleCurrency()\n if (sole === undefined) {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): field is multi-currency — compare against { amount, currency }, not a bare amount`,\n )\n }\n currency = sole\n amount = raw\n }\n if (typeof amount !== 'number' && typeof amount !== 'string') {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): operand ${JSON.stringify(raw)} is not a money amount`,\n )\n }\n const r = parseToScaledInt(amount, desc.scaleFor(currency), desc.rounding)\n if (!r.ok) {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): operand ${JSON.stringify(amount)} is not a finite decimal`,\n )\n }\n return { scaled: r.value.toString(), currency }\n}\n\n/**\n * Build the `via` clause payload ({@link MoneyWhereOperand}) for a\n * `where()` over a declared money field — the `ViaBinding.buildClause`\n * implementation for money. The operand is quantized into stored\n * scaled-int space NOW — build time — so typos throw at the call site.\n */\nexport function moneyFieldClause(\n field: string,\n op: Operator,\n value: unknown,\n desc: MoneyDescriptor,\n): MoneyWhereOperand {\n switch (op) {\n case '==': case '!=': case '<': case '<=': case '>': case '>=': {\n const e = parseOperand(field, value, desc)\n return { mode: desc.mode, entries: [e] }\n }\n case 'between': {\n if (!Array.isArray(value) || value.length !== 2) {\n throw new MoneyUnsupportedError(`where(\"${field}\"): 'between' needs a [lo, hi] tuple`)\n }\n const lo = parseOperand(field, value[0], desc)\n const hi = parseOperand(field, value[1], desc)\n if (lo.currency !== hi.currency) {\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): 'between' bounds mix currencies (${lo.currency} vs ${hi.currency})`,\n )\n }\n return { mode: desc.mode, entries: [lo, hi] }\n }\n case 'in': {\n if (!Array.isArray(value)) {\n throw new MoneyUnsupportedError(`where(\"${field}\"): 'in' needs an array of amounts`)\n }\n return { mode: desc.mode, entries: value.map(v => parseOperand(field, v, desc)) }\n }\n default:\n // contains / startsWith — string ops have no meaning in scaled space.\n throw new MoneyUnsupportedError(\n `where(\"${field}\"): operator '${op}' is not supported on a money field`,\n )\n }\n}\n\n/**\n * The `via` index-probe operand for a `where()` over a declared money\n * field (see `ViaBinding.indexProbe` in kernel/via/index.ts) — the STORED-form\n * value `CollectionIndexes.lookupEqual`/`lookupIn` (`with-lookup/indexing/\n * eager-indexes.ts`) can bucket directly.\n *\n * Only FIXED-mode `==`/`in` are sound:\n * - fixed-mode stores a bare scaled-int digit string (no currency\n * component), and `entries[i].scaled` is built by {@link parseOperand}\n * via the exact same `parseToScaledInt(...).value.toString()` path\n * `quantizeMoneyFields` (`via-money/normalize.ts#quantizeAmount`) uses\n * to produce the STORED value — so the two strings are byte-identical\n * by construction, and `stringifyKey` (which passes a string through\n * unchanged) buckets them the same way.\n * - multi-mode stores `{ amount, currency }` — the index would stringify\n * that object to its no-match `'\\0OBJECT\\0'` sentinel — so multi-mode\n * always returns `undefined` (no sound probe).\n * - every op besides `==`/`in` (range comparisons, `between`) has no\n * single stored-form value the hash index can serve, so they return\n * `undefined` too — the caller falls back to a scan.\n *\n * MIXED-ERA DATA (#672, fixed — including the #672 review's C1 finding): a\n * record whose stored value predates the field's `money()` declaration, or\n * otherwise bypassed `quantizeMoneyFields`, may hold a NON-canonical scaled\n * string (e.g. `'0100'` instead of `'100'`). `CollectionIndexes` no longer\n * buckets it under that raw string verbatim at ANY bucket-mutation site —\n * `build`, `upsert`, and `remove` all consult the same registered\n * canonicalizer (`ViaPipeline.canonicalizeIndexKey`; money's implementation:\n * `canonicalizeMoneyIndexKey`, `via/money/normalize.ts`), which re-parses the\n * stored value the same way the scan's `evaluateMoneyClause`/`readStored`\n * does (`BigInt(actual).toString()`). So the legacy record lands in the SAME\n * bucket a canonical write produces, an `==`/`in` probe for `'100'` finds it,\n * and — because add and remove now agree on that bucket — a later `put()`\n * (update) or `delete()` of that same legacy record cleans up the correct\n * bucket instead of stranding the id under a bucket the removal could never\n * reach. The two-string byte-identity argument above still explains WHY a\n * canonical write's probe hits directly (no canonicalization needed for the\n * common case); this note covers the pre-declaration tail.\n *\n * Boundary — CLOSED for the index layer (#677): LAZY mode's\n * `PersistedCollectionIndex` (`with-lookup/indexing/persisted-indexes.ts`)\n * now consults the SAME `ViaPipeline.canonicalizeIndexKey` at every\n * bucket-mutation site (`ingest`/`upsert`/`remove`) as `CollectionIndexes`\n * does, and its query path (`with-lookup/indexing/lazy-builder.ts`'s\n * `resolveCandidateIds()`) canonicalizes the `==`/`in` probe value before\n * calling `lookupEqual`/`lookupIn` — mirroring `candidateRecords()` above\n * resolving `indexValue` before its own `lookupEqual`/`lookupIn` call. A\n * lazy-mode mixed-era record's bucket and a canonicalized probe now agree,\n * the same as eager mode.\n *\n * Residual boundary CLOSED by #684: `LazyQuery.where()` now builds\n * `clause.via` exactly like `Query.where()`/`ScanBuilder.where()` do, and\n * `LazyQuery`'s post-filter (`lazy-builder.ts`'s `toArray()`) runs it\n * against the RAW record (a dedicated raw-fetch seam on `Collection`, split\n * out of `get()`) instead of the DECODED one — `clause.via.evaluate` (this\n * file's {@link evaluateMoneyClause}) now sees the same stored-form\n * operand/actual-value space eager's `filterRecords` does, at any `scale`.\n * Only survivors are decoded (`present()`) on the way out. Money RANGE\n * clauses additionally route around `PersistedCollectionIndex.lookupRange`\n * (raw/typed, wrong space for scaled-int money) — `resolveCandidateIds()`\n * enumerates the field's indexed ids instead and lets this now-via-aware\n * post-filter apply the correct BigInt-exact comparison. `lazyQuery().\n * where()` on a money field now agrees with `scan().where()` /\n * `query().where()` at every `scale` — for `.where()` results specifically.\n * `orderBy` ordering is a separate, still-open lazy-vs-eager divergence,\n * tracked in #695.\n */\n// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\nexport function moneyIndexProbe(op: Operator, payload: MoneyWhereOperand): unknown | undefined {\n if (payload.mode !== 'fixed') return undefined\n if (op === '==') return payload.entries[0]?.scaled\n if (op === 'in') return payload.entries.map(e => e.scaled)\n return undefined\n}\n\n/** Read a raw STORED money value into scaled space, or null if absent/malformed. */\nfunction readStored(actual: unknown, operand: MoneyWhereOperand): MoneyOperandEntry | null {\n let amount: unknown\n let currency: string\n if (operand.mode === 'fixed') {\n if (typeof actual !== 'string' && typeof actual !== 'number') return null\n amount = actual\n currency = operand.entries[0]?.currency ?? ''\n } else {\n if (!isMoneyValueObject(actual)) return null\n if (typeof actual.currency !== 'string') return null\n amount = actual.amount\n currency = actual.currency\n }\n if (typeof amount !== 'string' && typeof amount !== 'number') return null\n try {\n return { scaled: BigInt(amount).toString(), currency }\n } catch {\n return null\n }\n}\n\n/**\n * Per-record evaluation of a money clause: BigInt-exact comparison in\n * scaled-integer space. `actual` is the RAW stored field value.\n *\n * Missing/malformed stored values and cross-currency comparisons match\n * `!=` only — consistent with the generic clause semantics where an\n * absent field is \"not equal\" and has no defined order.\n */\nexport function evaluateMoneyClause(\n actual: unknown,\n op: Operator,\n operand: MoneyWhereOperand,\n): boolean {\n const stored = readStored(actual, operand)\n if (stored === null) return op === '!='\n const a = BigInt(stored.scaled)\n\n if (op === 'in') {\n return operand.entries.some(\n e => e.currency === stored.currency && BigInt(e.scaled) === a,\n )\n }\n if (op === 'between') {\n const [lo, hi] = operand.entries\n if (!lo || !hi || lo.currency !== stored.currency) return false\n return a >= BigInt(lo.scaled) && a <= BigInt(hi.scaled)\n }\n\n const e = operand.entries[0]\n if (!e) return false\n if (e.currency !== stored.currency) return op === '!='\n const b = BigInt(e.scaled)\n switch (op) {\n case '==': return a === b\n case '!=': return a !== b\n case '<': return a < b\n case '<=': return a <= b\n case '>': return a > b\n case '>=': return a >= b\n default: return false\n }\n}\n","/**\n * Money-aware aggregation: exact `sum` / `min` / `max` over money\n * fields.\n *\n * The generic reducers (`aggregate/reducers.ts`) read a field via\n * `readNumber`, which coerces a string (the stored scaled-integer form\n * of money, e.g. `'12345'`) to `0` — so an un-wrapped money `sum` would\n * silently return zero. {@link wrapMoneyReducers} rewrites any `sum` /\n * `min` / `max` over a declared money field into a reducer that\n * accumulates per-currency `BigInt` totals of the stored integers, so\n * the result is exact for any magnitude (past `Number.MAX_SAFE_INTEGER`\n * included).\n *\n * Results are currency-aware and never silently mix currencies:\n * - **fixed** mode → a single exact decimal string.\n * - **multi** mode → an exact `Record<currency, string>` map.\n * - **`sum` with `convertTo` + `fx`** → a single exact string, with\n * each currency converted via BigInt arithmetic (no float).\n *\n * `remove()` is implemented for every money reducer so they participate\n * in incremental live-aggregation and materialized-view maintenance\n * exactly like the generic reducers they replace.\n */\n\nimport { readPath } from '../../kernel/query/predicate.js'\nimport { formatScaledInt, parseToScaledInt } from './fixed-point.js'\nimport { scaleForCurrency } from './iso4217.js'\nimport { MoneyUnsupportedError } from './descriptor.js'\nimport type { MoneyDescriptor } from './descriptor.js'\nimport type { Reducer } from '../../with-lookup/reduce/reducers.js'\nimport type { ReduceSpec } from '../../with-lookup/reduce/reduction.js'\n\nexport type FxRates = Record<string, number | string>\n\ninterface ReadMoney {\n currency: string\n value: bigint\n}\n\n/**\n * Coerce an arbitrary money-field value into its scaled `BigInt` at the\n * given `scale`. Handles the two shapes a money field can arrive in by\n * the time it reaches a reducer:\n *\n * - **stored form** — a bare scaled-integer string (`'12345'`) or a\n * `bigint`. No decimal point, so `BigInt(v)` is the fast path.\n * - **decoded form** — a canonical decimal string (`'123.45'`), which\n * is what `query().toArray()` / `decodeMoneyFields` produce (UNION\n * arms map over decoded rows). `BigInt('123.45')` throws, so these\n * route through `parseToScaledInt(v, scale)`.\n *\n * A `number` is treated as a decimal magnitude (parsed at `scale`).\n * Anything unparseable → `null`.\n */\nfunction toScaledIntFromAny(v: unknown, scale: number): bigint | null {\n if (typeof v === 'bigint') return v\n if (typeof v === 'number') {\n const r = parseToScaledInt(v, scale)\n return r.ok ? r.value : null\n }\n if (typeof v === 'string') {\n if (!v.includes('.')) {\n // Stored scaled-integer form (e.g. '12345') — no decimal point.\n try {\n return BigInt(v)\n } catch {\n return null\n }\n }\n // Decoded canonical-decimal form (e.g. '123.45').\n const r = parseToScaledInt(v, scale)\n return r.ok ? r.value : null\n }\n return null\n}\n\n/** Read the raw stored money value (scaled integer) from a record. */\nfunction readMoney(record: unknown, field: string, desc: MoneyDescriptor): ReadMoney | null {\n const raw = readPath(record, field)\n if (raw === null || raw === undefined) return null\n if (desc.mode === 'fixed') {\n const cur = desc.fixedCurrency!\n const value = toScaledIntFromAny(raw, desc.scaleFor(cur))\n return value === null ? null : { currency: cur, value }\n }\n // multi mode: stored as { amount, currency }\n if (typeof raw !== 'object') return null\n const o = raw as { amount?: unknown; currency?: unknown }\n if (typeof o.currency !== 'string') return null\n const scale = desc.allows(o.currency) ? desc.scaleFor(o.currency) : 0\n const value = toScaledIntFromAny(o.amount, scale)\n return value === null ? null : { currency: o.currency, value }\n}\n\n/** Resolve the scale to use for a target currency (may be outside the allow-list). */\nfunction targetScaleFor(desc: MoneyDescriptor, currency: string): number {\n if (desc.allows(currency)) return desc.scaleFor(currency)\n const s = scaleForCurrency(currency)\n if (s === null) {\n throw new Error(`money: cannot determine scale for conversion target \"${currency}\"`)\n }\n return s\n}\n\n/** Parse a rate (number | string) into a scaled BigInt + its scale. */\nfunction parseRate(rate: number | string): { int: bigint; scale: number } {\n const s = String(rate).trim()\n const neg = s.startsWith('-')\n const body = neg ? s.slice(1) : s\n const dot = body.indexOf('.')\n const intPart = dot === -1 ? body : body.slice(0, dot)\n const fracPart = dot === -1 ? '' : body.slice(dot + 1)\n const int = BigInt((intPart === '' ? '0' : intPart) + fracPart)\n return { int: neg ? -int : int, scale: fracPart.length }\n}\n\n/** BigInt division of `n / d` with half-even (banker's) rounding. */\nfunction divRoundHalfEven(n: bigint, d: bigint): bigint {\n const q = n / d\n const r = n % d\n const twiceR = (r < 0n ? -r : r) * 2n\n if (twiceR < d) return q\n if (twiceR > d) return q + (n < 0n ? -1n : 1n)\n return q % 2n === 0n ? q : q + (n < 0n ? -1n : 1n)\n}\n\n/** Convert a scaled integer from `srcScale` to `targetScale` applying `rate`. */\nfunction convertScaled(value: bigint, srcScale: number, rate: number | string, targetScale: number): bigint {\n const { int: rateInt, scale: rateScale } = parseRate(rate)\n const product = value * rateInt\n const curScale = srcScale + rateScale\n if (curScale === targetScale) return product\n if (curScale < targetScale) return product * 10n ** BigInt(targetScale - curScale)\n return divRoundHalfEven(product, 10n ** BigInt(curScale - targetScale))\n}\n\nfunction finalizeSum(\n state: Map<string, bigint>,\n desc: MoneyDescriptor,\n convertTo: string | undefined,\n fx: FxRates | undefined,\n): string | Record<string, string> {\n if (convertTo !== undefined) {\n if (fx === undefined) {\n throw new Error(`money: sum convertTo \"${convertTo}\" requires an fx rate map`)\n }\n const targetScale = targetScaleFor(desc, convertTo)\n let total = 0n\n for (const [cur, v] of state) {\n if (cur === convertTo) {\n total += convertScaled(v, desc.scaleFor(cur), 1, targetScale)\n continue\n }\n const rate = fx[`${cur}->${convertTo}`]\n if (rate === undefined) {\n throw new Error(`money: no fx rate for \"${cur}->${convertTo}\"`)\n }\n total += convertScaled(v, desc.scaleFor(cur), rate, targetScale)\n }\n return formatScaledInt(total, targetScale)\n }\n\n if (desc.mode === 'fixed') {\n const cur = desc.fixedCurrency!\n return formatScaledInt(state.get(cur) ?? 0n, desc.scaleFor(cur))\n }\n\n const out: Record<string, string> = {}\n for (const [cur, v] of state) out[cur] = formatScaledInt(v, desc.scaleFor(cur))\n return out\n}\n\nfunction moneySumReducer(\n field: string,\n desc: MoneyDescriptor,\n convertTo: string | undefined,\n fx: FxRates | undefined,\n): Reducer<unknown, Map<string, bigint>> {\n return {\n op: 'sum',\n field,\n init: () => new Map<string, bigint>(),\n step: (state, record) => {\n const m = readMoney(record, field, desc)\n if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) + m.value)\n return state\n },\n remove: (state, record) => {\n const m = readMoney(record, field, desc)\n if (m) state.set(m.currency, (state.get(m.currency) ?? 0n) - m.value)\n return state\n },\n finalize: (state) => finalizeSum(state, desc, convertTo, fx),\n }\n}\n\nfunction extremum(values: readonly bigint[], op: 'min' | 'max'): bigint {\n let out = values[0]!\n for (let i = 1; i < values.length; i++) {\n const v = values[i]!\n if (op === 'min' ? v < out : v > out) out = v\n }\n return out\n}\n\nfunction moneyMinMaxReducer(\n op: 'min' | 'max',\n field: string,\n desc: MoneyDescriptor,\n): Reducer<unknown, Map<string, bigint[]>> {\n return {\n op,\n field,\n init: () => new Map<string, bigint[]>(),\n step: (state, record) => {\n const m = readMoney(record, field, desc)\n if (m) {\n const arr = state.get(m.currency)\n if (arr) arr.push(m.value)\n else state.set(m.currency, [m.value])\n }\n return state\n },\n remove: (state, record) => {\n const m = readMoney(record, field, desc)\n if (m) {\n const arr = state.get(m.currency)\n if (arr) {\n const idx = arr.indexOf(m.value)\n if (idx >= 0) arr.splice(idx, 1)\n }\n }\n return state\n },\n finalize: (state) => {\n if (desc.mode === 'fixed') {\n const cur = desc.fixedCurrency!\n const arr = state.get(cur)\n if (!arr || arr.length === 0) return null\n return formatScaledInt(extremum(arr, op), desc.scaleFor(cur))\n }\n const out: Record<string, string> = {}\n for (const [cur, arr] of state) {\n if (arr.length > 0) out[cur] = formatScaledInt(extremum(arr, op), desc.scaleFor(cur))\n }\n return out\n },\n }\n}\n\n/**\n * Rewrite any `sum` / `min` / `max` reducer over a declared money field\n * into its exact BigInt money-aware equivalent. Other reducers (and\n * reducers over non-money fields) pass through unchanged. Returns a new\n * spec; the input is not mutated.\n */\nexport function wrapMoneyReducers(\n spec: ReduceSpec,\n moneyFields: Record<string, MoneyDescriptor>,\n): ReduceSpec {\n let changed = false\n const out: Record<string, Reducer<unknown, unknown>> = {}\n for (const [key, reducer] of Object.entries(spec)) {\n const field = reducer.field\n const desc = field ? moneyFields[field] : undefined\n if (desc && reducer.op === 'avg') {\n throw new MoneyUnsupportedError(\n field!,\n `avg() is not supported on money field \"${field}\" in v1 — use sum() and count() and divide at the boundary.`,\n )\n }\n if (desc && (reducer.op === 'sum' || reducer.op === 'min' || reducer.op === 'max')) {\n changed = true\n out[key] =\n reducer.op === 'sum'\n ? moneySumReducer(field!, desc, reducer.convertTo, reducer.fx as FxRates | undefined)\n : (moneyMinMaxReducer(reducer.op, field!, desc) as Reducer<unknown, unknown>)\n } else {\n out[key] = reducer\n }\n }\n return changed ? out : spec\n}\n","/**\n * The money `ViaBinding` — wires the money engine (normalize/paths/where/\n * money-reducer) into the kernel's generic Via port. `money()`\n * (descriptor.ts) calls {@link linkMoneyVia} at declaration time — the\n * same #553 static-link pattern the retired `linkMoneyEngine()` /\n * `kernel/money-runtime.ts` seam used, now the only one (Task 6 cut the\n * query DSL over to this binding and deleted the legacy seam).\n */\nimport type { ViaBinding, ViaReadCtx } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport type { MoneyDescriptor } from './descriptor.js'\nimport { quantizeMoneyFields, decodeMoneyFields, canonicalizeStoredMoney, canonicalizeIncomingMoney, moneyScaledValue, canonicalizeMoneyIndexKey, presentVirtualMoneyFields } from './normalize.js'\nimport { validateMoneyFieldPaths } from './paths.js'\nimport { moneyFieldClause, evaluateMoneyClause, moneyIndexProbe, type MoneyWhereOperand } from './where.js'\nimport { wrapMoneyReducers } from './money-reducer.js'\nimport type { Operator } from '../../kernel/query/predicate.js'\nimport type { ReduceSpec } from '../../with-lookup/reduce/reduction.js'\n\n/** #669 — the money binder's config bag. `virtualMoneyFields` is the money∩virtual-mode-\n * computed field-name intersection (`kernel/collection-config.ts#resolveVirtualMoneyFields`);\n * absent/empty means no field on this collection composes money with a virtual computed\n * field on itself — the pre-#669 behavior (no `presentLate`, ordinary `present()` for\n * every declared money field). */\nexport interface MoneyBindingConfig {\n readonly moneyFields: Record<string, MoneyDescriptor>\n readonly virtualMoneyFields?: ReadonlySet<string>\n}\n\nexport function moneyBinding(moneyFields: Record<string, MoneyDescriptor>, virtualMoneyFields?: ReadonlySet<string>): ViaBinding {\n validateMoneyFieldPaths(moneyFields) // declaration-time (replaces sites 1 & 2)\n const hasVirtualMoney = virtualMoneyFields !== undefined && virtualMoneyFields.size > 0\n // #669 — a field that is BOTH money AND virtual-mode computed has no value yet when\n // money's ORDINARY present() runs (money is first in `_presentOrder`, computed second —\n // the #665 invariant); decodeMoneyFields already no-ops on an absent value, but the skip\n // is made explicit here so the intent reads at the call site: dressing this field happens\n // in `presentLate` below, once computed's present() has actually produced a value.\n const ordinaryPresentFields = hasVirtualMoney\n ? Object.fromEntries(Object.entries(moneyFields).filter(([f]) => !virtualMoneyFields.has(f)))\n : moneyFields\n return {\n brand: 'money',\n posture: { encryptedAtRest: 'envelope', queryable: 'ordered', exportable: true, forgettable: true },\n covers: (field) => field in moneyFields,\n ingest: (r) => canonicalizeIncomingMoney(r, moneyFields) as Record<string, unknown>,\n canonicalizeStored: (r) => canonicalizeStoredMoney(r, moneyFields) as Record<string, unknown>,\n encodeWrite: (r) => quantizeMoneyFields(r, moneyFields),\n present: (r, ctx) => decodeMoneyFields(r, ordinaryPresentFields, typeof ctx.locale === 'string' ? ctx.locale : undefined),\n ...(hasVirtualMoney\n ? {\n presentLate: (r: Record<string, unknown>, ctx: ViaReadCtx) =>\n presentVirtualMoneyFields(r, moneyFields, virtualMoneyFields, typeof ctx.locale === 'string' ? ctx.locale : undefined),\n }\n : {}),\n buildClause: (field, op, value) => {\n const desc = moneyFields[field]\n if (!desc) return undefined\n return moneyFieldClause(field, op as Operator, value, desc) // the MoneyWhereOperand payload\n },\n evaluateClause: (actual, op, payload) => evaluateMoneyClause(actual, op as Operator, payload as MoneyWhereOperand),\n indexProbe: (op, payload) => moneyIndexProbe(op as Operator, payload as MoneyWhereOperand),\n canonicalizeIndexKey: (field, rawValue) => canonicalizeMoneyIndexKey(field, rawValue, moneyFields),\n decodeResults: (r) => decodeMoneyFields(r as Record<string, unknown>, moneyFields, 'raw'),\n compareForOrder: (field, a, b) => {\n const desc = moneyFields[field]\n if (!desc) return undefined\n const av = moneyScaledValue(a, desc); const bv = moneyScaledValue(b, desc)\n if (av === null) return bv === null ? 0 : 1\n if (bv === null) return -1\n return av < bv ? -1 : av > bv ? 1 : 0\n },\n wrapReducers: (spec) => wrapMoneyReducers(spec as ReduceSpec, moneyFields),\n }\n}\n\nexport function linkMoneyVia(): void {\n installViaBinder('money', (cfg) => {\n const c = cfg as MoneyBindingConfig\n return moneyBinding(c.moneyFields, c.virtualMoneyFields)\n })\n}\n","/**\n * The `money()` field descriptor — a branded schema-layer descriptor, a\n * sibling of `i18nText()` / `dictKey()`. It owns currency, scale, and\n * rounding policy for a field; the pure arithmetic lives in\n * {@link ./fixed-point} and default scale resolution in\n * {@link ./iso4217}.\n *\n * Two modes:\n * - **fixed** `money({ currency: 'EUR' })` — one currency for the\n * field; the value stores a bare scaled-integer string.\n * - **multi** `money({ currencies: 'any' | [...] })` — currency travels\n * per record; the value stores `{ amount, currency }`.\n *\n * `currency` and `currencies` are mutually exclusive.\n */\n\nimport type { RoundingMode } from './fixed-point.js'\nimport { scaleForCurrency } from './iso4217.js'\nimport { NoydbError } from '../../kernel/errors.js'\nimport { linkMoneyVia } from './binding.js'\n\nexport interface MoneyOptionsFixed {\n currency: string\n /** Override the ISO-4217 default scale (required for unlisted codes). */\n scale?: number\n rounding?: RoundingMode\n}\n\nexport interface MoneyOptionsMulti {\n currencies: 'any' | readonly string[]\n /** Per-currency scale overrides (required for unlisted codes). */\n scaleOverrides?: Record<string, number>\n rounding?: RoundingMode\n}\n\nexport type MoneyOptions = MoneyOptionsFixed | MoneyOptionsMulti\n\nexport interface MoneyDescriptor {\n readonly _noydbMoney: true\n /** Via port brand marker — lets a `MoneyDescriptor` satisfy the kernel's opaque `ViaDescriptor`. */\n readonly _viaBrand: 'money'\n readonly mode: 'fixed' | 'multi'\n readonly options: MoneyOptions\n readonly rounding: RoundingMode | undefined\n /** The currency for fixed mode; `undefined` in multi mode. */\n readonly fixedCurrency: string | undefined\n /** Resolve the scale for a currency, throwing if not allowed / unknown. */\n scaleFor(currency: string): number\n /** Whether this descriptor permits the given currency. */\n allows(currency: string): boolean\n /**\n * The single currency this descriptor implies, if any — fixed mode, or\n * multi mode with exactly one allow-listed currency. Lets a multi field\n * accept a bare amount unambiguously. `undefined` otherwise.\n */\n soleCurrency(): string | undefined\n}\n\n/** Raised when a written value carries more precision than `scale` allows. */\nexport class MoneyPrecisionError extends NoydbError {\n constructor(\n public readonly field: string,\n public readonly value: unknown,\n public readonly scale: number,\n ) {\n super(\n 'MONEY_PRECISION',\n `money: value ${JSON.stringify(value)} for field \"${field}\" exceeds scale ${scale} ` +\n `and no rounding mode is configured`,\n )\n this.name = 'MoneyPrecisionError'\n }\n}\n\n/** Raised when a currency is disallowed or has no resolvable scale. */\nexport class MoneyCurrencyError extends NoydbError {\n constructor(\n public readonly currency: string,\n public readonly reason: 'not-allowed' | 'unknown-scale',\n public readonly field?: string,\n ) {\n super(\n 'MONEY_CURRENCY',\n reason === 'not-allowed'\n ? `money: currency \"${currency}\" is not allowed${field ? ` for field \"${field}\"` : ''}`\n : `money: no scale known for currency \"${currency}\"${field ? ` (field \"${field}\")` : ''} — ` +\n `pass an explicit scale / scaleOverrides`,\n )\n this.name = 'MoneyCurrencyError'\n }\n}\n\n/** Raised when an aggregate operation is not supported on a money field. */\nexport class MoneyUnsupportedError extends NoydbError {\n constructor(\n public readonly field: string,\n message?: string,\n ) {\n super(\n 'MONEY_UNSUPPORTED',\n message ??\n `money: operation is not supported on field \"${field}\" — use sum() and count() and divide at the boundary`,\n )\n this.name = 'MoneyUnsupportedError'\n }\n}\n\nfunction isMultiOptions(o: MoneyOptions): o is MoneyOptionsMulti {\n return 'currencies' in o\n}\n\n/** Create a {@link MoneyDescriptor}. */\nexport function money(options: MoneyOptions): MoneyDescriptor {\n // The declaration is the Via binding's opt-in unit (#553): constructing\n // a descriptor statically links normalize/paths/where/money-reducer into\n // the kernel's Via port, so every later kernel consultation (including\n // the SYNC query DSL) finds the binding already installed.\n linkMoneyVia()\n const hasFixed = 'currency' in options\n const hasMulti = 'currencies' in options\n if (hasFixed && hasMulti) {\n throw new TypeError('money: `currency` and `currencies` are mutually exclusive')\n }\n if (!hasFixed && !hasMulti) {\n throw new TypeError('money: one of `currency` or `currencies` is required')\n }\n\n const rounding = options.rounding\n\n if (isMultiOptions(options)) {\n const overrides = options.scaleOverrides ?? {}\n const allowList = options.currencies\n const allows = (c: string): boolean =>\n allowList === 'any' ? true : allowList.includes(c)\n const scaleFor = (c: string): number => {\n if (!allows(c)) throw new MoneyCurrencyError(c, 'not-allowed')\n const s = overrides[c] ?? scaleForCurrency(c)\n if (s === null || s === undefined) throw new MoneyCurrencyError(c, 'unknown-scale')\n return s\n }\n // Eagerly validate the allow-list resolves (catch typos at construction).\n if (allowList !== 'any') for (const c of allowList) scaleFor(c)\n const soleCurrency = (): string | undefined =>\n allowList !== 'any' && allowList.length === 1 ? allowList[0] : undefined\n return {\n _noydbMoney: true,\n _viaBrand: 'money',\n mode: 'multi',\n options,\n rounding,\n fixedCurrency: undefined,\n scaleFor,\n allows,\n soleCurrency,\n }\n }\n\n // fixed\n const currency = options.currency\n const resolvedScale = options.scale ?? scaleForCurrency(currency)\n if (resolvedScale === null || resolvedScale === undefined) {\n throw new MoneyCurrencyError(currency, 'unknown-scale')\n }\n return {\n _noydbMoney: true,\n _viaBrand: 'money',\n mode: 'fixed',\n options,\n rounding,\n fixedCurrency: currency,\n scaleFor(c: string): number {\n if (c !== currency) throw new MoneyCurrencyError(c, 'not-allowed')\n return resolvedScale\n },\n allows: (c: string): boolean => c === currency,\n soleCurrency: (): string | undefined => currency,\n }\n}\n\n/** Runtime predicate for detecting a {@link MoneyDescriptor}. */\nexport function isMoneyDescriptor(x: unknown): x is MoneyDescriptor {\n return (\n typeof x === 'object' &&\n x !== null &&\n (x as { _noydbMoney?: unknown })._noydbMoney === true\n )\n}\n","/**\n * Exact decimal arithmetic for post-aggregate row math (#1007).\n *\n * Money leaves a reducer as a `MoneyString` — a decimal string, exact by\n * construction. Doing `Number(a) - Number(b)` on two of those re-introduces\n * binary floating point at the one step the money type exists to protect:\n * `10.05 - 0.10` becomes `9.950000000000001`, which `quantizeMoneyFields`\n * then correctly REFUSES rather than silently storing drift.\n *\n * So `derive` is handed these instead. Everything runs in scaled BigInt at the\n * widest scale of its inputs and formats back to a decimal string, which means\n * no operation here can introduce a representation error. Deliberately just\n * the additive set — `add` / `sub` / `neg` / `min` / `max` / `cmp`. Anything\n * needing multiplication or division needs a rounding policy, and a rounding\n * policy is a decision the caller must make explicitly, not one this module\n * should guess.\n *\n * @module\n */\n\nimport { ValidationError } from '../../kernel/errors.js'\n\n/** A value these helpers accept: a decimal string, a number, or a bigint of whole units. */\nexport type ExactOperand = string | number | bigint\n\n/** Scaled representation: `value` is the integer numerator, `scale` the number of decimal places. */\ninterface Scaled {\n readonly value: bigint\n readonly scale: number\n}\n\nconst DECIMAL = /^[+-]?\\d+(\\.\\d+)?$/\n\nfunction parse(operand: ExactOperand, op: string): Scaled {\n if (typeof operand === 'bigint') return { value: operand, scale: 0 }\n const text = typeof operand === 'number'\n // A non-integer JS number has already lost exactness before it reached us;\n // `toString()` gives the shortest round-trip form, which is the closest\n // honest reading of what the caller actually holds.\n ? String(operand)\n : operand.trim()\n if (!DECIMAL.test(text)) {\n throw new ValidationError(\n `exact.${op}: \"${text}\" is not a decimal value. Pass a decimal string (the shape money fields ` +\n 'carry), a number, or a bigint.',\n )\n }\n const [whole, fraction = ''] = text.split('.')\n const digits = `${whole}${fraction}`\n return { value: BigInt(digits), scale: fraction.length }\n}\n\n/** Bring two scaled values onto a common scale without losing a digit. */\nfunction align(a: Scaled, b: Scaled): { a: bigint; b: bigint; scale: number } {\n const scale = Math.max(a.scale, b.scale)\n const lift = (s: Scaled) => s.value * 10n ** BigInt(scale - s.scale)\n return { a: lift(a), b: lift(b), scale }\n}\n\nfunction format({ value, scale }: Scaled): string {\n if (scale === 0) return value.toString()\n const negative = value < 0n\n const digits = (negative ? -value : value).toString().padStart(scale + 1, '0')\n const whole = digits.slice(0, digits.length - scale)\n const fraction = digits.slice(digits.length - scale)\n return `${negative ? '-' : ''}${whole}.${fraction}`\n}\n\nfunction binary(op: string, a: ExactOperand, b: ExactOperand, f: (x: bigint, y: bigint) => bigint): string {\n const aligned = align(parse(a, op), parse(b, op))\n return format({ value: f(aligned.a, aligned.b), scale: aligned.scale })\n}\n\n/**\n * Exact decimal helpers handed to an MV `derive` as its second argument.\n *\n * Every result is a decimal string, so results compose without ever passing\n * through a float. Declare the derived field in the MV's `moneyFields` and the\n * final value is quantised to that descriptor's scale on the way to storage.\n */\nexport interface ExactMath {\n /** `a + b`, exact. */\n add(a: ExactOperand, b: ExactOperand): string\n /** `a - b`, exact. */\n sub(a: ExactOperand, b: ExactOperand): string\n /** `-a`, exact. */\n neg(a: ExactOperand): string\n /** The larger of `a` and `b`. */\n max(a: ExactOperand, b: ExactOperand): string\n /** The smaller of `a` and `b`. */\n min(a: ExactOperand, b: ExactOperand): string\n /** `-1` when `a < b`, `0` when equal, `1` when `a > b`. */\n cmp(a: ExactOperand, b: ExactOperand): -1 | 0 | 1\n}\n\n/** The singleton passed to `derive` — stateless, so there is nothing to construct per row. */\nexport const exactMath: ExactMath = {\n add: (a, b) => binary('add', a, b, (x, y) => x + y),\n sub: (a, b) => binary('sub', a, b, (x, y) => x - y),\n neg: (a) => binary('neg', 0, a, (x, y) => x - y),\n max: (a, b) => binary('max', a, b, (x, y) => (x > y ? x : y)),\n min: (a, b) => binary('min', a, b, (x, y) => (x < y ? x : y)),\n cmp: (a, b) => {\n const { a: x, b: y } = align(parse(a, 'cmp'), parse(b, 'cmp'))\n return x < y ? -1 : x > y ? 1 : 0\n },\n}\n"],"mappings":"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","names":["keep","magnitude","obj","clone","currency","format","isMoneyValueObject","e","value"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-party/team/echo-ceremony.ts","../src/with-party/team/device-seal.ts","../src/with-party/team/active.ts"],"sourcesContent":["/**\n * `beginEchoUnlock` — the interactive ceremony API (spec\n * design-history/2026-08-02-echo-secret-design.md, #940, Task 6).\n *\n * Split out of `echo-secret.ts` (which owns only the block builders +\n * verifiers) because this module orchestrates BOTH `echo-secret.ts`\n * (`verifyPrompt`/`resolveEchoReveal`/`verifyTypedEcho`) AND `keyring.ts`\n * (`loadKeyring`) — and `keyring.ts` itself imports `buildEchoBlock` from\n * `echo-secret.ts`. Keeping the ceremony here instead of inside\n * `echo-secret.ts` yields a clean DAG (`echo-ceremony.ts` → {`echo-secret.ts`,\n * `keyring.ts`}; `keyring.ts` → `echo-secret.ts`) instead of a two-file\n * cycle, and restores `echo-secret.ts`'s documented charter (builders +\n * verifiers only).\n *\n * @module\n */\n\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { DeviceSealProvider } from './device-seal.js'\nimport { WrongPromptError, WrongEchoError, ValidationError, NoAccessError } from '../../kernel/errors.js'\nimport { verifyPrompt, resolveEchoReveal, verifyTypedEcho } from './echo-secret.js'\nimport { loadKeyring, readKeyringFile, assertKeyringNotExpired, type UnlockedKeyring } from './keyring.js'\n\n/** Options for {@link beginEchoUnlock}. */\nexport interface BeginEchoUnlockOptions {\n readonly userId: string\n readonly prompt: string\n readonly deviceSeal?: DeviceSealProvider\n}\n\n/**\n * A prompt-verified, in-progress echo unlock. Stateless w.r.t. attempts —\n * `complete()` may be called more than once (e.g. retry after a wrong key).\n */\nexport interface EchoCeremony {\n /**\n * Echo to display for owner recognition; null ⇒ degraded: ask the owner to\n * TYPE it.\n *\n * Conforming-player contract (spec\n * design-history/2026-08-02-echo-secret-design.md, \"Ceremony\"):\n * when the owner does NOT recognize the revealed echo, the player MUST show\n * a hard phishing warning — do not continue, open the official app, rotate\n * the secret immediately — MUST NOT offer any skip or\n * \"verification unavailable\" fallback, and MUST simply abandon the ceremony\n * by never calling {@link EchoCeremony.complete}. The kernel cannot enforce\n * this terminal state; it is the player's obligation.\n */\n readonly reveal: string | null\n readonly maskHint: string | undefined\n /** Complete the ceremony. `echo` is REQUIRED when `reveal` is null. */\n complete(input: { readonly echo?: string; readonly key: string }): Promise<UnlockedKeyring>\n}\n\n/**\n * Begin the interactive anti-phishing unlock ceremony (spec decision 4):\n * prompt → echo reveal → key. Fetches and parses the `_keyring/{userId}`\n * row (same store path as `loadKeyring`), verifies the prompt, and resolves\n * the echo for display (or degrades to the typed-echo path when the reveal\n * cannot be resolved — sealed reveal under a foreign device).\n *\n * Conforming-player contract (spec\n * design-history/2026-08-02-echo-secret-design.md): an owner who does\n * not recognize `ceremony.reveal` is looking at a phish. The player MUST then\n * show a hard warning (stop, open the official app, rotate the secret now),\n * MUST NOT offer a skip / \"verification unavailable\" fallback or any\n * single-field secret entry, and MUST abandon the ceremony without calling\n * `complete()`.\n *\n * @throws NoAccessError when no keyring exists for `userId` (mirrors\n * `loadKeyring`'s missing-row error).\n * @throws KeyringExpiredError when the slot is past its `expires_at` —\n * checked before the prompt is verified, so an expired slot never reveals.\n * @throws ValidationError when the keyring is not an echo keyring.\n * @throws WrongPromptError when the prompt fails its verifier.\n */\nexport async function beginEchoUnlock(\n store: NoydbStore,\n vault: string,\n opts: BeginEchoUnlockOptions,\n): Promise<EchoCeremony> {\n const { userId, prompt, deviceSeal } = opts\n\n // Same store path + missing-row error as loadKeyring, now shared via\n // `readKeyringFile` (#951).\n const found = await readKeyringFile(store, vault, userId)\n if (!found) {\n throw new NoAccessError(`No keyring found for user \"${userId}\" in vault \"${vault}\"`)\n }\n const { file: keyringFile } = found\n\n // Same expiry gate as loadKeyring, applied BEFORE the prompt is verified so\n // an expired slot never reveals its echo (and leaks no timing on the\n // prompt). Now shared via `assertKeyringNotExpired` (#951).\n assertKeyringNotExpired(keyringFile)\n\n const block = keyringFile.echo\n if (block === undefined) {\n throw new ValidationError('not an echo keyring')\n }\n\n if (!(await verifyPrompt(block, prompt))) {\n throw new WrongPromptError()\n }\n\n const reveal = await resolveEchoReveal(block, prompt, deviceSeal)\n\n return {\n reveal,\n maskHint: block.mask_hint,\n async complete(input: { readonly echo?: string; readonly key: string }): Promise<UnlockedKeyring> {\n let echo: string\n if (reveal !== null) {\n // A supplied input.echo must agree with the revealed echo — defensive:\n // a player that collects a typed echo despite a reveal must not\n // silently unlock with a mismatched pair.\n if (input.echo !== undefined && input.echo !== reveal) {\n throw new WrongEchoError()\n }\n echo = reveal\n } else {\n if (input.echo === undefined) {\n throw new ValidationError('beginEchoUnlock: echo is required to complete a degraded (typed-echo) ceremony.')\n }\n if (!(await verifyTypedEcho(block, input.echo))) {\n throw new WrongEchoError()\n }\n echo = input.echo\n }\n return loadKeyring(store, vault, { userId, secret: { prompt, echo, key: input.key } })\n },\n }\n}\n","import { NoydbError } from '../../kernel/errors.js'\nimport type { DeviceSealProvider } from '../../port/with/device-seal-strategy.js'\n\n/**\n * Device-local sealer for the echo reveal-blob (spec decision 5).\n * DISTINCT from `SealingKeyProvider` (managed-secret mode, which seals\n * the WHOLE secret): this seals only the echo part, and only so an\n * enrolled device can display it during the unlock ceremony. The\n * sealed bytes may sit in the (untrusted) keyring file — attacker-B\n * resistance comes from `unseal` requiring this device's key store.\n *\n * The interface itself is declared on the `/with` port\n * ({@link DeviceSealProvider} from `port/with/device-seal-strategy.js`)\n * so the kernel spine can reference it without a static spine→service\n * import; re-exported here as the canonical consumer-facing home.\n */\nexport type { DeviceSealProvider }\n\n/** In-memory test provider — AES-GCM under a per-instance random key. */\nexport class MemoryDeviceSealProvider implements DeviceSealProvider {\n readonly id: string\n private readonly keyPromise: Promise<CryptoKey>\n constructor(opts: { id: string }) {\n this.id = opts.id\n this.keyPromise = globalThis.crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, [\n 'encrypt',\n 'decrypt',\n ])\n }\n async seal(plain: Uint8Array): Promise<Uint8Array> {\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12))\n const ct = new Uint8Array(\n await globalThis.crypto.subtle.encrypt({ name: 'AES-GCM', iv }, await this.keyPromise, plain as BufferSource),\n )\n const out = new Uint8Array(12 + ct.length)\n out.set(iv, 0)\n out.set(ct, 12)\n return out\n }\n async unseal(sealed: Uint8Array): Promise<Uint8Array> {\n try {\n return new Uint8Array(\n await globalThis.crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: sealed.slice(0, 12) },\n await this.keyPromise,\n sealed.slice(12) as BufferSource,\n ),\n )\n } catch {\n throw new NoydbError('DEVICE_SEAL_UNSEAL_FAILED', 'Device seal: unseal failed (tamper or wrong device).')\n }\n }\n}\n","/**\n * Enable the multi-user team capability (#267 Track A tail).\n *\n * Pass to `createNoydb({ teamStrategy: withTeam() })` to make `db.grant` /\n * `db.revoke` / `db.rotate` live. The keyring grant/revoke/rotate engines\n * are statically imported HERE — this module is reachable only through the\n * `@noy-db/hub/team` subpath (and the root barrel's tree-shakeable\n * re-export), so a single-user floor bundle never carries them. The facade\n * (`TeamFacade.runGrant` / `runRevoke` / `runRotate`) owns the policy-gate +\n * keyring plumbing and receives the engine as an argument.\n */\nimport type { TeamStrategy } from './strategy.js'\nimport { grant as grantEngine, revoke as revokeEngine, rotateKeys as rotateEngine, verifyRoster as verifyRosterEngine, quarantineKeyring as quarantineEngine } from './keyring.js'\n\nexport function withTeam(): TeamStrategy {\n return {\n async grant(team, vault, options, factors) {\n return team.runGrant(grantEngine, vault, options, factors)\n },\n async revoke(team, vault, options, factors) {\n return team.runRevoke(revokeEngine, vault, options, factors)\n },\n async rotate(team, vault, collections) {\n return team.runRotate(rotateEngine, vault, collections)\n },\n async verifyRoster(team, vault) {\n return team.runVerifyRoster(verifyRosterEngine, vault)\n },\n async quarantineKeyring(team, vault, userId, factors) {\n return team.runQuarantine(quarantineEngine, vault, userId, factors)\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA4EA,eAAsB,gBACpB,OACA,OACA,MACuB;AACvB,QAAM,EAAE,QAAQ,QAAQ,WAAW,IAAI;AAIvC,QAAM,QAAQ,MAAM,gBAAgB,OAAO,OAAO,MAAM;AACxD,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,cAAc,8BAA8B,MAAM,eAAe,KAAK,GAAG;AAAA,EACrF;AACA,QAAM,EAAE,MAAM,YAAY,IAAI;AAK9B,0BAAwB,WAAW;AAEnC,QAAM,QAAQ,YAAY;AAC1B,MAAI,UAAU,QAAW;AACvB,UAAM,IAAI,gBAAgB,qBAAqB;AAAA,EACjD;AAEA,MAAI,CAAE,MAAM,aAAa,OAAO,MAAM,GAAI;AACxC,UAAM,IAAI,iBAAiB;AAAA,EAC7B;AAEA,QAAM,SAAS,MAAM,kBAAkB,OAAO,QAAQ,UAAU;AAEhE,SAAO;AAAA,IACL;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,MAAM,SAAS,OAAmF;AAChG,UAAI;AACJ,UAAI,WAAW,MAAM;AAInB,YAAI,MAAM,SAAS,UAAa,MAAM,SAAS,QAAQ;AACrD,gBAAM,IAAI,eAAe;AAAA,QAC3B;AACA,eAAO;AAAA,MACT,OAAO;AACL,YAAI,MAAM,SAAS,QAAW;AAC5B,gBAAM,IAAI,gBAAgB,iFAAiF;AAAA,QAC7G;AACA,YAAI,CAAE,MAAM,gBAAgB,OAAO,MAAM,IAAI,GAAI;AAC/C,gBAAM,IAAI,eAAe;AAAA,QAC3B;AACA,eAAO,MAAM;AAAA,MACf;AACA,aAAO,YAAY,OAAO,OAAO,EAAE,QAAQ,QAAQ,EAAE,QAAQ,MAAM,KAAK,MAAM,IAAI,EAAE,CAAC;AAAA,IACvF;AAAA,EACF;AACF;;;ACjHO,IAAM,2BAAN,MAA6D;AAAA,EACzD;AAAA,EACQ;AAAA,EACjB,YAAY,MAAsB;AAChC,SAAK,KAAK,KAAK;AACf,SAAK,aAAa,WAAW,OAAO,OAAO,YAAY,EAAE,MAAM,WAAW,QAAQ,IAAI,GAAG,OAAO;AAAA,MAC9F;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EACA,MAAM,KAAK,OAAwC;AACjD,UAAM,KAAK,WAAW,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC;AAC/D,UAAM,KAAK,IAAI;AAAA,MACb,MAAM,WAAW,OAAO,OAAO,QAAQ,EAAE,MAAM,WAAW,GAAG,GAAG,MAAM,KAAK,YAAY,KAAqB;AAAA,IAC9G;AACA,UAAM,MAAM,IAAI,WAAW,KAAK,GAAG,MAAM;AACzC,QAAI,IAAI,IAAI,CAAC;AACb,QAAI,IAAI,IAAI,EAAE;AACd,WAAO;AAAA,EACT;AAAA,EACA,MAAM,OAAO,QAAyC;AACpD,QAAI;AACF,aAAO,IAAI;AAAA,QACT,MAAM,WAAW,OAAO,OAAO;AAAA,UAC7B,EAAE,MAAM,WAAW,IAAI,OAAO,MAAM,GAAG,EAAE,EAAE;AAAA,UAC3C,MAAM,KAAK;AAAA,UACX,OAAO,MAAM,EAAE;AAAA,QACjB;AAAA,MACF;AAAA,IACF,QAAQ;AACN,YAAM,IAAI,WAAW,6BAA6B,sDAAsD;AAAA,IAC1G;AAAA,EACF;AACF;;;ACtCO,SAAS,WAAyB;AACvC,SAAO;AAAA,IACL,MAAM,MAAM,MAAM,OAAO,SAAS,SAAS;AACzC,aAAO,KAAK,SAAS,OAAa,OAAO,SAAS,OAAO;AAAA,IAC3D;AAAA,IACA,MAAM,OAAO,MAAM,OAAO,SAAS,SAAS;AAC1C,aAAO,KAAK,UAAU,QAAc,OAAO,SAAS,OAAO;AAAA,IAC7D;AAAA,IACA,MAAM,OAAO,MAAM,OAAO,aAAa;AACrC,aAAO,KAAK,UAAU,YAAc,OAAO,WAAW;AAAA,IACxD;AAAA,IACA,MAAM,aAAa,MAAM,OAAO;AAC9B,aAAO,KAAK,gBAAgB,cAAoB,KAAK;AAAA,IACvD;AAAA,IACA,MAAM,kBAAkB,MAAM,OAAO,QAAQ,SAAS;AACpD,aAAO,KAAK,cAAc,mBAAkB,OAAO,QAAQ,OAAO;AAAA,IACpE;AAAA,EACF;AACF;","names":[]}