@noy-db/hub 0.3.0 → 0.4.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 (356) hide show
  1. package/dist/aggregate/index.js +4 -4
  2. package/dist/api-3A3S4SHH.js +20 -0
  3. package/dist/as/index.js +11 -11
  4. package/dist/attestation/index.js +18 -18
  5. package/dist/{backup-BJ57IKAO.js → backup-YPUEKIJC.js} +11 -11
  6. package/dist/blobs/index.js +12 -12
  7. package/dist/broker/index.js +12 -12
  8. package/dist/bundle/index.js +22 -22
  9. package/dist/by/index.js +2 -2
  10. package/dist/cargo/index.js +22 -22
  11. package/dist/{chunk-ZAJAVEXY.js → chunk-2PCAVW2I.js} +2 -2
  12. package/dist/{chunk-HTJQYHZP.js → chunk-3ELCWU56.js} +2 -2
  13. package/dist/{chunk-J2ZX2XUV.js → chunk-554KLAAN.js} +5 -5
  14. package/dist/{chunk-NR5LT5RY.js → chunk-5PFOMHTB.js} +7 -7
  15. package/dist/{chunk-B3HJYUOF.js → chunk-5WZXVULX.js} +2 -2
  16. package/dist/{chunk-ZJ2AK4V7.js → chunk-6HPG2YNX.js} +2 -2
  17. package/dist/{chunk-5M5DVGV7.js → chunk-6HY2X62D.js} +2 -2
  18. package/dist/{chunk-QRB7NAOJ.js → chunk-6NQMXTYC.js} +2 -2
  19. package/dist/{chunk-RIX4V2TB.js → chunk-6XE5TMPR.js} +3 -3
  20. package/dist/{chunk-7DSTRQIJ.js → chunk-A4FXFAKF.js} +2 -2
  21. package/dist/{chunk-454JXE57.js → chunk-A5BDDEXK.js} +10 -170
  22. package/dist/chunk-A5BDDEXK.js.map +1 -0
  23. package/dist/{chunk-EVVGJDRQ.js → chunk-ANATMTOA.js} +3 -3
  24. package/dist/{chunk-W22IKJE2.js → chunk-BOKIKJJB.js} +2 -2
  25. package/dist/{chunk-VZRODIM3.js → chunk-CJ5KKU6X.js} +4 -4
  26. package/dist/{chunk-CDUQTA3Z.js → chunk-CZTLE22N.js} +7 -7
  27. package/dist/{chunk-RCVJZMFE.js → chunk-DDHM2TLX.js} +4 -4
  28. package/dist/{chunk-PN2XMATY.js → chunk-DRWBEDYO.js} +2 -2
  29. package/dist/{chunk-EYZ6IQ6X.js → chunk-DSBQODMZ.js} +5 -5
  30. package/dist/{chunk-LASZKVMO.js → chunk-EO43ANYJ.js} +3 -3
  31. package/dist/{chunk-6QTNZJBS.js → chunk-EWPOHMN2.js} +3 -3
  32. package/dist/chunk-FH52K7EG.js +50 -0
  33. package/dist/chunk-FH52K7EG.js.map +1 -0
  34. package/dist/{chunk-FHS2HQSH.js → chunk-FXZDAV62.js} +11 -8
  35. package/dist/chunk-FXZDAV62.js.map +1 -0
  36. package/dist/{chunk-2NVOWNI3.js → chunk-GDG5VVYG.js} +4 -4
  37. package/dist/{chunk-6JTIAGHD.js → chunk-GULTLPFB.js} +3 -3
  38. package/dist/chunk-GULTLPFB.js.map +1 -0
  39. package/dist/{chunk-3B5VVCNY.js → chunk-GWKAVKHI.js} +2 -2
  40. package/dist/{chunk-BDVFPG6Y.js → chunk-H2KSZMIX.js} +5 -5
  41. package/dist/{chunk-CIE2BASH.js → chunk-HOONYZY2.js} +2 -2
  42. package/dist/{chunk-HDMEGAQE.js → chunk-HQBM64EE.js} +5 -5
  43. package/dist/{chunk-VFUPHI2Z.js → chunk-IAO54J4R.js} +6 -6
  44. package/dist/{chunk-5O3XF4DV.js → chunk-IFJTPAQQ.js} +4 -4
  45. package/dist/{chunk-OSDB5WW3.js → chunk-IK43HLDQ.js} +11 -11
  46. package/dist/{chunk-3QCRBHCY.js → chunk-ISMV2QVG.js} +2 -2
  47. package/dist/{chunk-HB3RFQSC.js → chunk-IY23KFNZ.js} +3 -3
  48. package/dist/{chunk-6CRIV7DE.js → chunk-JBBSYJHP.js} +2 -2
  49. package/dist/{chunk-IU6SNYX6.js → chunk-JFCV5QIK.js} +5 -5
  50. package/dist/{chunk-MEFJPTZ4.js → chunk-JRBNCUNT.js} +3 -3
  51. package/dist/{chunk-4VNQOLML.js → chunk-JVBO4SZL.js} +2 -2
  52. package/dist/{chunk-EQO2UVP4.js → chunk-KS6SAVCZ.js} +6 -6
  53. package/dist/{chunk-4CTHCN4J.js → chunk-LVGTA624.js} +4 -4
  54. package/dist/{chunk-K5IMOGHB.js → chunk-M75ZZHSR.js} +2 -2
  55. package/dist/{chunk-NJ4EOKMB.js → chunk-MT62YSPZ.js} +4 -4
  56. package/dist/{chunk-JKJOGPZN.js → chunk-N6WF5PKC.js} +2 -2
  57. package/dist/{chunk-4JBYV75E.js → chunk-NCCBLYEB.js} +5 -5
  58. package/dist/{chunk-SLFHQTTW.js → chunk-OCVM6KDH.js} +5 -5
  59. package/dist/{chunk-GKEWXIV5.js → chunk-ONTBCHCP.js} +2 -2
  60. package/dist/{chunk-X7QBD67R.js → chunk-PK6JIDQ4.js} +124 -104
  61. package/dist/{chunk-X7QBD67R.js.map → chunk-PK6JIDQ4.js.map} +1 -1
  62. package/dist/{chunk-EVX47VBM.js → chunk-QDPZWTN2.js} +2 -2
  63. package/dist/{chunk-IL6POWCW.js → chunk-R3BLCFD2.js} +6 -6
  64. package/dist/chunk-RA5VTXXG.js +173 -0
  65. package/dist/chunk-RA5VTXXG.js.map +1 -0
  66. package/dist/{chunk-QLWMEXCT.js → chunk-RDXW3OBQ.js} +2 -2
  67. package/dist/{chunk-3LQ2YKO4.js → chunk-RFI7GGFO.js} +2 -2
  68. package/dist/{chunk-FUG4SFN3.js → chunk-RJ6K6K5M.js} +2 -2
  69. package/dist/{chunk-T2FFWNPK.js → chunk-RNG4VRP4.js} +3 -3
  70. package/dist/{chunk-JZUV47FZ.js → chunk-RTEK4C4I.js} +2 -2
  71. package/dist/{chunk-KSKBRKGJ.js → chunk-S3BFKGET.js} +84 -6
  72. package/dist/chunk-S3BFKGET.js.map +1 -0
  73. package/dist/{chunk-5YOLBEQ6.js → chunk-S6Q37VGE.js} +2 -2
  74. package/dist/{chunk-E5ZQT5KX.js → chunk-SMIRGMS7.js} +2 -2
  75. package/dist/{chunk-6T2SCQRU.js → chunk-SV6EAJEP.js} +4 -4
  76. package/dist/chunk-T36RFODW.js +180 -0
  77. package/dist/chunk-T36RFODW.js.map +1 -0
  78. package/dist/{chunk-ERJ7FU55.js → chunk-T3GY65MA.js} +3 -3
  79. package/dist/{chunk-LXEXBTKL.js → chunk-TAGR72IR.js} +4 -4
  80. package/dist/{chunk-JIF24JPM.js → chunk-THVIPWRF.js} +9 -9
  81. package/dist/{chunk-ZUODRVHY.js → chunk-TIGPQDYP.js} +2 -2
  82. package/dist/{chunk-NWSGI5C7.js → chunk-U6JMEH2P.js} +3 -3
  83. package/dist/{chunk-ZICD77GM.js → chunk-UC2ETWF3.js} +2 -2
  84. package/dist/{chunk-VLFCJYTL.js → chunk-UDSR4X4L.js} +9 -9
  85. package/dist/{chunk-SNZ6URZZ.js → chunk-UHNGNKLV.js} +7 -7
  86. package/dist/{chunk-R4ROFEYC.js → chunk-VAIS3SOM.js} +2 -2
  87. package/dist/{chunk-VN3DBQAU.js → chunk-VCBHAE4R.js} +2 -2
  88. package/dist/{chunk-JNAI2HN2.js → chunk-VCTXU4AT.js} +1 -1
  89. package/dist/chunk-VCTXU4AT.js.map +1 -0
  90. package/dist/{chunk-M4CV6BRT.js → chunk-VEALDPHW.js} +2 -2
  91. package/dist/{chunk-AUSTCW2X.js → chunk-VKCADNMR.js} +8 -8
  92. package/dist/{chunk-XWZVFISC.js → chunk-VRBTNCM4.js} +6 -6
  93. package/dist/{chunk-BIOUI4HL.js → chunk-W2OG6JOL.js} +1 -1
  94. package/dist/{chunk-BIOUI4HL.js.map → chunk-W2OG6JOL.js.map} +1 -1
  95. package/dist/{chunk-XDYREZWE.js → chunk-W4TCUGQR.js} +8 -8
  96. package/dist/{chunk-FM3G3NOY.js → chunk-WG4QBZDN.js} +83 -6
  97. package/dist/chunk-WG4QBZDN.js.map +1 -0
  98. package/dist/{chunk-HDQIIUPH.js → chunk-WHXXI3W5.js} +3 -3
  99. package/dist/{chunk-F64IRYG3.js → chunk-WKGF2TJR.js} +2 -2
  100. package/dist/{chunk-CAF6WOK7.js → chunk-WNGGJWF7.js} +2 -2
  101. package/dist/{chunk-EXK7QRWF.js → chunk-WOM2HTML.js} +5 -5
  102. package/dist/{chunk-Q5VNKNE5.js → chunk-WQIYTNJV.js} +3 -3
  103. package/dist/{chunk-GTGPO5KW.js → chunk-WZSHL6DQ.js} +5 -5
  104. package/dist/{chunk-CRI4OUV4.js → chunk-XDIAFPUM.js} +2 -2
  105. package/dist/{chunk-RYCTDFUX.js → chunk-XJMBYYQK.js} +2 -2
  106. package/dist/{chunk-DLG74CNB.js → chunk-XTHCN77N.js} +4 -4
  107. package/dist/chunk-XUW3PXHU.js +248 -0
  108. package/dist/chunk-XUW3PXHU.js.map +1 -0
  109. package/dist/{chunk-I6GALP6E.js → chunk-YCH5GBN6.js} +6 -6
  110. package/dist/{chunk-RZ5SVOYR.js → chunk-YTGDPCPW.js} +2 -2
  111. package/dist/{chunk-UNFPPVNI.js → chunk-YZQOGE2L.js} +2 -2
  112. package/dist/{chunk-MV43JSJF.js → chunk-Z4PD65VQ.js} +4 -4
  113. package/dist/{chunk-YFXZEAYK.js → chunk-ZXEZCY6F.js} +6 -6
  114. package/dist/classified/index.js +5 -5
  115. package/dist/{classified-marker-CZKA6ITE.js → classified-marker-QWMASIT7.js} +3 -3
  116. package/dist/collection-facade-KMBWNCYP.js +44 -0
  117. package/dist/{computed-ZIQTYTDI.js → computed-DSMPXG2Y.js} +3 -3
  118. package/dist/consent/index.js +9 -9
  119. package/dist/{public-envelope-B4KNWVUA.js → cover-QJRXINN5.js} +25 -5
  120. package/dist/{dead-filter-ESOBZZP5.js → dead-filter-66VGI6JV.js} +2 -2
  121. package/dist/delegation-SU6CV3W7.js +25 -0
  122. package/dist/derivations/index.js +12 -12
  123. package/dist/derive-6RWU4EOA.js +21 -0
  124. package/dist/{enclave-AP3I5W7O.js → enclave-TKPN2G2U.js} +9 -9
  125. package/dist/executor-KGNLJD6M.js +9 -0
  126. package/dist/executor-UX4FASYZ.js +26 -0
  127. package/dist/executor-XGIS6CR7.js +9 -0
  128. package/dist/export-accessible-LKW3QV22.js +23 -0
  129. package/dist/extract-partition-UP2ARO75.js +38 -0
  130. package/dist/{fanout-sidecar-A765THMX.js → fanout-sidecar-B5LVUMQX.js} +9 -9
  131. package/dist/find-YS6XWB46.js +11 -0
  132. package/dist/forget/index.js +9 -9
  133. package/dist/guards/index.js +3 -3
  134. package/dist/history/index.js +12 -12
  135. package/dist/i18n/index.js +14 -14
  136. package/dist/index.d.ts +6 -3
  137. package/dist/index.js +110 -89
  138. package/dist/index.js.map +1 -1
  139. package/dist/indexing/index.js +2 -2
  140. package/dist/issue-3ACYX7LO.js +19 -0
  141. package/dist/kernel/errors.d.ts +10 -5
  142. package/dist/kernel/noydb.d.ts +21 -11
  143. package/dist/kernel/types.d.ts +12 -6
  144. package/dist/kernel/vault.d.ts +10 -6
  145. package/dist/{ledger-GFBKVPV4.js → ledger-PTK7XZYT.js} +11 -11
  146. package/dist/liberate-MQQXBUCY.js +24 -0
  147. package/dist/{link-set-Z7Q2ASFX.js → link-set-SLJHDNRB.js} +11 -11
  148. package/dist/materialized-views/index.js +19 -18
  149. package/dist/noydb-ANIW5FLE.js +69 -0
  150. package/dist/on/index.js +9 -9
  151. package/dist/overlay-views/index.js +4 -4
  152. package/dist/periods/index.js +12 -12
  153. package/dist/pod/index.js +13 -11
  154. package/dist/{policy-Z3RUAIY2.js → policy-YDLCOX4P.js} +5 -5
  155. package/dist/portability/index.js +8 -8
  156. package/dist/{post-register-JTH76BLY.js → post-register-2YHSADFI.js} +6 -6
  157. package/dist/query/index.js +10 -8
  158. package/dist/register-PAM3ASRZ.js +23 -0
  159. package/dist/registry-4S2U2TR4.js +19 -0
  160. package/dist/registry-NPOWJHC4.js +9 -0
  161. package/dist/registry-VZ3CCI2R.js +18 -0
  162. package/dist/request-withdrawal-DI3H5WUI.js +31 -0
  163. package/dist/{reveal-DIBHLUZX.js → reveal-HWO4YMTJ.js} +6 -6
  164. package/dist/revoke-R5CZSVUT.js +24 -0
  165. package/dist/satellites/index.js +1 -1
  166. package/dist/sealed-record/index.js +12 -12
  167. package/dist/{seed-Q6M2IZFL.js → seed-3ZMF5EQA.js} +11 -11
  168. package/dist/session/index.js +9 -9
  169. package/dist/shadow/index.js +2 -2
  170. package/dist/signer-MBVHW4WE.js +25 -0
  171. package/dist/snapshots/index.js +10 -10
  172. package/dist/{stale-OSQ7J3AA.js → stale-VZL4OGUB.js} +10 -10
  173. package/dist/storage-3MEJUENZ.js +23 -0
  174. package/dist/{store-coordination-provider-64OEZFGS.js → store-coordination-provider-FKX3F6TQ.js} +3 -3
  175. package/dist/sync/index.js +9 -9
  176. package/dist/team/index.js +15 -15
  177. package/dist/tiers/index.js +10 -10
  178. package/dist/to/index.js +1 -1
  179. package/dist/tx/index.js +3 -3
  180. package/dist/util/index.js +1 -1
  181. package/dist/{verify-ZASTRNPR.js → verify-26Y5GNMK.js} +5 -5
  182. package/dist/via/i18n/policy.d.ts +1 -1
  183. package/dist/{walk-SVNDIZ62.js → walk-IPLXE36W.js} +11 -11
  184. package/dist/with/index.js +11 -11
  185. package/dist/with-formula/materialized-views/dependency-analyzer.d.ts +15 -0
  186. package/dist/with-formula/materialized-views/index.d.ts +1 -1
  187. package/dist/with-formula/materialized-views/registry.d.ts +29 -0
  188. package/dist/with-formula/materialized-views/types.d.ts +81 -0
  189. package/dist/with-formula/materialized-views/with-materialized-view.d.ts +9 -5
  190. package/dist/with-party/directory/cover/custom.d.ts +34 -0
  191. package/dist/with-party/directory/cover/index.d.ts +19 -0
  192. package/dist/with-party/directory/cover/schema.d.ts +39 -0
  193. package/dist/with-party/directory/cover/storage.d.ts +71 -0
  194. package/dist/with-party/directory/cover/types.d.ts +149 -0
  195. package/dist/with-party/team/noydb-facade.d.ts +1 -1
  196. package/dist/with-pod/bundle.d.ts +9 -7
  197. package/dist/with-pod/format.d.ts +8 -4
  198. package/dist/with-pod/index.d.ts +2 -1
  199. package/dist/withdraw-accessible-TZDATQVR.js +28 -0
  200. package/package.json +3 -3
  201. package/dist/api-ZGS6LCOF.js +0 -20
  202. package/dist/chunk-454JXE57.js.map +0 -1
  203. package/dist/chunk-4NXKCLLQ.js +0 -97
  204. package/dist/chunk-4NXKCLLQ.js.map +0 -1
  205. package/dist/chunk-6JTIAGHD.js.map +0 -1
  206. package/dist/chunk-FHS2HQSH.js.map +0 -1
  207. package/dist/chunk-FM3G3NOY.js.map +0 -1
  208. package/dist/chunk-JNAI2HN2.js.map +0 -1
  209. package/dist/chunk-KSKBRKGJ.js.map +0 -1
  210. package/dist/chunk-WXSYDNBT.js +0 -39
  211. package/dist/chunk-WXSYDNBT.js.map +0 -1
  212. package/dist/chunk-X3YYQWOQ.js +0 -155
  213. package/dist/chunk-X3YYQWOQ.js.map +0 -1
  214. package/dist/collection-facade-TLWIPI76.js +0 -44
  215. package/dist/delegation-W7ZV2322.js +0 -25
  216. package/dist/derive-H2BJTLUI.js +0 -21
  217. package/dist/executor-KGWTNEQD.js +0 -9
  218. package/dist/executor-OVEXR6RT.js +0 -9
  219. package/dist/executor-UNEH5ZYH.js +0 -25
  220. package/dist/export-accessible-PCMXSUZS.js +0 -23
  221. package/dist/extract-partition-SWHFSYJO.js +0 -38
  222. package/dist/find-N6345CNT.js +0 -11
  223. package/dist/issue-5OKB6I2R.js +0 -19
  224. package/dist/liberate-3VDOT4KO.js +0 -24
  225. package/dist/noydb-ZCJOYBDI.js +0 -68
  226. package/dist/register-VAHONCID.js +0 -23
  227. package/dist/registry-7MLVD3VO.js +0 -19
  228. package/dist/registry-EPKPD5SM.js +0 -18
  229. package/dist/registry-P5T7PS2U.js +0 -9
  230. package/dist/request-withdrawal-TI3A5WPS.js +0 -31
  231. package/dist/revoke-FDRD6GRW.js +0 -24
  232. package/dist/signer-MUWEIUI6.js +0 -25
  233. package/dist/storage-MVFCGEQE.js +0 -23
  234. package/dist/with-party/directory/public-envelope/index.d.ts +0 -12
  235. package/dist/with-party/directory/public-envelope/schema.d.ts +0 -24
  236. package/dist/with-party/directory/public-envelope/storage.d.ts +0 -59
  237. package/dist/with-party/directory/public-envelope/types.d.ts +0 -80
  238. package/dist/withdraw-accessible-WZIEVWSR.js +0 -28
  239. /package/dist/{api-ZGS6LCOF.js.map → api-3A3S4SHH.js.map} +0 -0
  240. /package/dist/{backup-BJ57IKAO.js.map → backup-YPUEKIJC.js.map} +0 -0
  241. /package/dist/{chunk-ZAJAVEXY.js.map → chunk-2PCAVW2I.js.map} +0 -0
  242. /package/dist/{chunk-HTJQYHZP.js.map → chunk-3ELCWU56.js.map} +0 -0
  243. /package/dist/{chunk-J2ZX2XUV.js.map → chunk-554KLAAN.js.map} +0 -0
  244. /package/dist/{chunk-NR5LT5RY.js.map → chunk-5PFOMHTB.js.map} +0 -0
  245. /package/dist/{chunk-B3HJYUOF.js.map → chunk-5WZXVULX.js.map} +0 -0
  246. /package/dist/{chunk-ZJ2AK4V7.js.map → chunk-6HPG2YNX.js.map} +0 -0
  247. /package/dist/{chunk-5M5DVGV7.js.map → chunk-6HY2X62D.js.map} +0 -0
  248. /package/dist/{chunk-QRB7NAOJ.js.map → chunk-6NQMXTYC.js.map} +0 -0
  249. /package/dist/{chunk-RIX4V2TB.js.map → chunk-6XE5TMPR.js.map} +0 -0
  250. /package/dist/{chunk-7DSTRQIJ.js.map → chunk-A4FXFAKF.js.map} +0 -0
  251. /package/dist/{chunk-EVVGJDRQ.js.map → chunk-ANATMTOA.js.map} +0 -0
  252. /package/dist/{chunk-W22IKJE2.js.map → chunk-BOKIKJJB.js.map} +0 -0
  253. /package/dist/{chunk-VZRODIM3.js.map → chunk-CJ5KKU6X.js.map} +0 -0
  254. /package/dist/{chunk-CDUQTA3Z.js.map → chunk-CZTLE22N.js.map} +0 -0
  255. /package/dist/{chunk-RCVJZMFE.js.map → chunk-DDHM2TLX.js.map} +0 -0
  256. /package/dist/{chunk-PN2XMATY.js.map → chunk-DRWBEDYO.js.map} +0 -0
  257. /package/dist/{chunk-EYZ6IQ6X.js.map → chunk-DSBQODMZ.js.map} +0 -0
  258. /package/dist/{chunk-LASZKVMO.js.map → chunk-EO43ANYJ.js.map} +0 -0
  259. /package/dist/{chunk-6QTNZJBS.js.map → chunk-EWPOHMN2.js.map} +0 -0
  260. /package/dist/{chunk-2NVOWNI3.js.map → chunk-GDG5VVYG.js.map} +0 -0
  261. /package/dist/{chunk-3B5VVCNY.js.map → chunk-GWKAVKHI.js.map} +0 -0
  262. /package/dist/{chunk-BDVFPG6Y.js.map → chunk-H2KSZMIX.js.map} +0 -0
  263. /package/dist/{chunk-CIE2BASH.js.map → chunk-HOONYZY2.js.map} +0 -0
  264. /package/dist/{chunk-HDMEGAQE.js.map → chunk-HQBM64EE.js.map} +0 -0
  265. /package/dist/{chunk-VFUPHI2Z.js.map → chunk-IAO54J4R.js.map} +0 -0
  266. /package/dist/{chunk-5O3XF4DV.js.map → chunk-IFJTPAQQ.js.map} +0 -0
  267. /package/dist/{chunk-OSDB5WW3.js.map → chunk-IK43HLDQ.js.map} +0 -0
  268. /package/dist/{chunk-3QCRBHCY.js.map → chunk-ISMV2QVG.js.map} +0 -0
  269. /package/dist/{chunk-HB3RFQSC.js.map → chunk-IY23KFNZ.js.map} +0 -0
  270. /package/dist/{chunk-6CRIV7DE.js.map → chunk-JBBSYJHP.js.map} +0 -0
  271. /package/dist/{chunk-IU6SNYX6.js.map → chunk-JFCV5QIK.js.map} +0 -0
  272. /package/dist/{chunk-MEFJPTZ4.js.map → chunk-JRBNCUNT.js.map} +0 -0
  273. /package/dist/{chunk-4VNQOLML.js.map → chunk-JVBO4SZL.js.map} +0 -0
  274. /package/dist/{chunk-EQO2UVP4.js.map → chunk-KS6SAVCZ.js.map} +0 -0
  275. /package/dist/{chunk-4CTHCN4J.js.map → chunk-LVGTA624.js.map} +0 -0
  276. /package/dist/{chunk-K5IMOGHB.js.map → chunk-M75ZZHSR.js.map} +0 -0
  277. /package/dist/{chunk-NJ4EOKMB.js.map → chunk-MT62YSPZ.js.map} +0 -0
  278. /package/dist/{chunk-JKJOGPZN.js.map → chunk-N6WF5PKC.js.map} +0 -0
  279. /package/dist/{chunk-4JBYV75E.js.map → chunk-NCCBLYEB.js.map} +0 -0
  280. /package/dist/{chunk-SLFHQTTW.js.map → chunk-OCVM6KDH.js.map} +0 -0
  281. /package/dist/{chunk-GKEWXIV5.js.map → chunk-ONTBCHCP.js.map} +0 -0
  282. /package/dist/{chunk-EVX47VBM.js.map → chunk-QDPZWTN2.js.map} +0 -0
  283. /package/dist/{chunk-IL6POWCW.js.map → chunk-R3BLCFD2.js.map} +0 -0
  284. /package/dist/{chunk-QLWMEXCT.js.map → chunk-RDXW3OBQ.js.map} +0 -0
  285. /package/dist/{chunk-3LQ2YKO4.js.map → chunk-RFI7GGFO.js.map} +0 -0
  286. /package/dist/{chunk-FUG4SFN3.js.map → chunk-RJ6K6K5M.js.map} +0 -0
  287. /package/dist/{chunk-T2FFWNPK.js.map → chunk-RNG4VRP4.js.map} +0 -0
  288. /package/dist/{chunk-JZUV47FZ.js.map → chunk-RTEK4C4I.js.map} +0 -0
  289. /package/dist/{chunk-5YOLBEQ6.js.map → chunk-S6Q37VGE.js.map} +0 -0
  290. /package/dist/{chunk-E5ZQT5KX.js.map → chunk-SMIRGMS7.js.map} +0 -0
  291. /package/dist/{chunk-6T2SCQRU.js.map → chunk-SV6EAJEP.js.map} +0 -0
  292. /package/dist/{chunk-ERJ7FU55.js.map → chunk-T3GY65MA.js.map} +0 -0
  293. /package/dist/{chunk-LXEXBTKL.js.map → chunk-TAGR72IR.js.map} +0 -0
  294. /package/dist/{chunk-JIF24JPM.js.map → chunk-THVIPWRF.js.map} +0 -0
  295. /package/dist/{chunk-ZUODRVHY.js.map → chunk-TIGPQDYP.js.map} +0 -0
  296. /package/dist/{chunk-NWSGI5C7.js.map → chunk-U6JMEH2P.js.map} +0 -0
  297. /package/dist/{chunk-ZICD77GM.js.map → chunk-UC2ETWF3.js.map} +0 -0
  298. /package/dist/{chunk-VLFCJYTL.js.map → chunk-UDSR4X4L.js.map} +0 -0
  299. /package/dist/{chunk-SNZ6URZZ.js.map → chunk-UHNGNKLV.js.map} +0 -0
  300. /package/dist/{chunk-R4ROFEYC.js.map → chunk-VAIS3SOM.js.map} +0 -0
  301. /package/dist/{chunk-VN3DBQAU.js.map → chunk-VCBHAE4R.js.map} +0 -0
  302. /package/dist/{chunk-M4CV6BRT.js.map → chunk-VEALDPHW.js.map} +0 -0
  303. /package/dist/{chunk-AUSTCW2X.js.map → chunk-VKCADNMR.js.map} +0 -0
  304. /package/dist/{chunk-XWZVFISC.js.map → chunk-VRBTNCM4.js.map} +0 -0
  305. /package/dist/{chunk-XDYREZWE.js.map → chunk-W4TCUGQR.js.map} +0 -0
  306. /package/dist/{chunk-HDQIIUPH.js.map → chunk-WHXXI3W5.js.map} +0 -0
  307. /package/dist/{chunk-F64IRYG3.js.map → chunk-WKGF2TJR.js.map} +0 -0
  308. /package/dist/{chunk-CAF6WOK7.js.map → chunk-WNGGJWF7.js.map} +0 -0
  309. /package/dist/{chunk-EXK7QRWF.js.map → chunk-WOM2HTML.js.map} +0 -0
  310. /package/dist/{chunk-Q5VNKNE5.js.map → chunk-WQIYTNJV.js.map} +0 -0
  311. /package/dist/{chunk-GTGPO5KW.js.map → chunk-WZSHL6DQ.js.map} +0 -0
  312. /package/dist/{chunk-CRI4OUV4.js.map → chunk-XDIAFPUM.js.map} +0 -0
  313. /package/dist/{chunk-RYCTDFUX.js.map → chunk-XJMBYYQK.js.map} +0 -0
  314. /package/dist/{chunk-DLG74CNB.js.map → chunk-XTHCN77N.js.map} +0 -0
  315. /package/dist/{chunk-I6GALP6E.js.map → chunk-YCH5GBN6.js.map} +0 -0
  316. /package/dist/{chunk-RZ5SVOYR.js.map → chunk-YTGDPCPW.js.map} +0 -0
  317. /package/dist/{chunk-UNFPPVNI.js.map → chunk-YZQOGE2L.js.map} +0 -0
  318. /package/dist/{chunk-MV43JSJF.js.map → chunk-Z4PD65VQ.js.map} +0 -0
  319. /package/dist/{chunk-YFXZEAYK.js.map → chunk-ZXEZCY6F.js.map} +0 -0
  320. /package/dist/{classified-marker-CZKA6ITE.js.map → classified-marker-QWMASIT7.js.map} +0 -0
  321. /package/dist/{collection-facade-TLWIPI76.js.map → collection-facade-KMBWNCYP.js.map} +0 -0
  322. /package/dist/{computed-ZIQTYTDI.js.map → computed-DSMPXG2Y.js.map} +0 -0
  323. /package/dist/{delegation-W7ZV2322.js.map → cover-QJRXINN5.js.map} +0 -0
  324. /package/dist/{dead-filter-ESOBZZP5.js.map → dead-filter-66VGI6JV.js.map} +0 -0
  325. /package/dist/{derive-H2BJTLUI.js.map → delegation-SU6CV3W7.js.map} +0 -0
  326. /package/dist/{enclave-AP3I5W7O.js.map → derive-6RWU4EOA.js.map} +0 -0
  327. /package/dist/{executor-KGWTNEQD.js.map → enclave-TKPN2G2U.js.map} +0 -0
  328. /package/dist/{executor-OVEXR6RT.js.map → executor-KGNLJD6M.js.map} +0 -0
  329. /package/dist/{executor-UNEH5ZYH.js.map → executor-UX4FASYZ.js.map} +0 -0
  330. /package/dist/{export-accessible-PCMXSUZS.js.map → executor-XGIS6CR7.js.map} +0 -0
  331. /package/dist/{extract-partition-SWHFSYJO.js.map → export-accessible-LKW3QV22.js.map} +0 -0
  332. /package/dist/{find-N6345CNT.js.map → extract-partition-UP2ARO75.js.map} +0 -0
  333. /package/dist/{fanout-sidecar-A765THMX.js.map → fanout-sidecar-B5LVUMQX.js.map} +0 -0
  334. /package/dist/{issue-5OKB6I2R.js.map → find-YS6XWB46.js.map} +0 -0
  335. /package/dist/{ledger-GFBKVPV4.js.map → issue-3ACYX7LO.js.map} +0 -0
  336. /package/dist/{liberate-3VDOT4KO.js.map → ledger-PTK7XZYT.js.map} +0 -0
  337. /package/dist/{link-set-Z7Q2ASFX.js.map → liberate-MQQXBUCY.js.map} +0 -0
  338. /package/dist/{noydb-ZCJOYBDI.js.map → link-set-SLJHDNRB.js.map} +0 -0
  339. /package/dist/{policy-Z3RUAIY2.js.map → noydb-ANIW5FLE.js.map} +0 -0
  340. /package/dist/{public-envelope-B4KNWVUA.js.map → policy-YDLCOX4P.js.map} +0 -0
  341. /package/dist/{post-register-JTH76BLY.js.map → post-register-2YHSADFI.js.map} +0 -0
  342. /package/dist/{register-VAHONCID.js.map → register-PAM3ASRZ.js.map} +0 -0
  343. /package/dist/{registry-7MLVD3VO.js.map → registry-4S2U2TR4.js.map} +0 -0
  344. /package/dist/{registry-EPKPD5SM.js.map → registry-NPOWJHC4.js.map} +0 -0
  345. /package/dist/{registry-P5T7PS2U.js.map → registry-VZ3CCI2R.js.map} +0 -0
  346. /package/dist/{request-withdrawal-TI3A5WPS.js.map → request-withdrawal-DI3H5WUI.js.map} +0 -0
  347. /package/dist/{reveal-DIBHLUZX.js.map → reveal-HWO4YMTJ.js.map} +0 -0
  348. /package/dist/{revoke-FDRD6GRW.js.map → revoke-R5CZSVUT.js.map} +0 -0
  349. /package/dist/{seed-Q6M2IZFL.js.map → seed-3ZMF5EQA.js.map} +0 -0
  350. /package/dist/{signer-MUWEIUI6.js.map → signer-MBVHW4WE.js.map} +0 -0
  351. /package/dist/{stale-OSQ7J3AA.js.map → stale-VZL4OGUB.js.map} +0 -0
  352. /package/dist/{storage-MVFCGEQE.js.map → storage-3MEJUENZ.js.map} +0 -0
  353. /package/dist/{store-coordination-provider-64OEZFGS.js.map → store-coordination-provider-FKX3F6TQ.js.map} +0 -0
  354. /package/dist/{verify-ZASTRNPR.js.map → verify-26Y5GNMK.js.map} +0 -0
  355. /package/dist/{walk-SVNDIZ62.js.map → walk-IPLXE36W.js.map} +0 -0
  356. /package/dist/{withdraw-accessible-WZIEVWSR.js.map → withdraw-accessible-TZDATQVR.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/port/with/i18n-strategy.ts","../src/kernel/paths.ts","../src/via/i18n/policy.ts","../src/via/i18n/script.ts","../src/via/i18n/binding.ts","../src/via/i18n/core.ts"],"sourcesContent":["/**\n * i18n strategy seam (#623 Task 7 — moved out of `via/i18n/strategy.ts`\n * and `via/i18n/dictionary.ts`, precedent: `port/with/lazy-strategy.ts`).\n * Lives on the `/with` port (the one seam the kernel spine may import\n * statically) so `Collection`/`Vault` can hold the `NO_I18N` default and the\n * two reserved-name/brand predicates without a spine→service static import.\n *\n * Core imports `I18nStrategy` type-only + `NO_I18N` stub; real\n * `applyI18nLocale` / `validateI18nTextValue` / `DictionaryHandle` are only\n * reachable via `withI18n()` in `via/i18n/active.ts`.\n *\n * Solo apps that don't use `i18nText()` fields, don't declare `dictKey()`\n * fields, and don't open a `vault.dictionary(...)` handle ship none of the\n * ~854 LOC behind this seam.\n *\n * Behavior under NO_I18N:\n *\n * - **applyI18nLocale** — returns the record unchanged. Apps without\n * any i18n descriptors never observe a difference; apps that\n * *did* declare i18nText/dictKey fields without opting into the\n * strategy still get raw values back (locale resolution silently\n * skipped). The validators below ensure the misconfiguration is\n * caught at write time instead.\n * - **validateI18nTextValue** — throws when called. Only fires when\n * a collection declared `i18nFields`; if you declared the field,\n * you must opt in.\n * - **buildDictionaryHandle** — throws when called. Only fires when\n * user code calls `vault.dictionary(...)`.\n *\n * #650 Task 1 (via-lookup extraction, phase D): the real\n * `buildDictionaryHandle` (in `via/i18n/active.ts`) now delegates to\n * `withLookup().buildLookupHandle` (`via/lookup/active.ts`) — same\n * handle (`LookupHandle`, aliased as `DictionaryHandle`), new home. This\n * interface's shape is unchanged.\n *\n * @internal\n */\n\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { LedgerStore } from '../../with-commit/history/ledger/store.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport type { NoydbEventEmitter } from '../../kernel/events.js'\nimport type { I18nTextDescriptor } from '../../via/i18n/core.js'\nimport type { Layer } from '../../via/i18n/policy.js'\nimport type { ScriptWarning } from '../../via/i18n/script.js'\nimport type { DictKeyDescriptor, DictionaryHandle, DictionaryOptions, StaticDictDescriptor } from '../../via/i18n/dictionary.js'\nimport type { ViaCryptoCtx } from '../../kernel/via/index.js'\nimport type { EncryptedEnvelope } from '../../kernel/types.js'\n\n/**\n * Options accepted by `I18nStrategy.buildDictionaryHandle`. Mirrors\n * the `DictionaryHandle` constructor verbatim — kept here so core\n * code never imports the dictionary module at runtime.\n *\n * @internal\n */\nexport interface BuildDictionaryHandleOptions<Keys extends string = string> {\n adapter: NoydbStore\n compartmentName: string\n dictionaryName: string\n keyring: UnlockedKeyring\n /**\n * The `reservedEnvelopes('_dict_')` capability (#629 Task 4) — DictionaryHandle's\n * sanctioned crypto door onto its `_dict_<name>` collection. Bound by the\n * Vault to its own `getDEK`, the same per-collection-name DEK resolver\n * every other collection uses.\n */\n reservedEnvelopes: ReturnType<ViaCryptoCtx['reservedEnvelopes']>\n encrypted: boolean\n ledger: LedgerStore | undefined\n options: DictionaryOptions\n findAndUpdateReferences:\n | ((\n dictionaryName: string,\n oldKey: string,\n newKey: string,\n ) => Promise<void>)\n | undefined\n emitter: NoydbEventEmitter\n /**\n * Used by the active strategy to satisfy the generic-key parameter\n * on the returned handle. The NO_I18N stub never reads it.\n */\n // marker generic — runtime sees no value\n _keyMarker?: Keys\n /** #650 Task 4 (#647) — choke-point participation hooks, passed through to `buildLookupHandle`. */\n onDirty?: ((collection: string, id: string, action: 'put' | 'delete', version: number) => Promise<void>) | undefined\n onRecordMutated?: ((collection: string, id: string, action: 'put' | 'delete', version: number) => Promise<void>) | undefined\n /**\n * #647 fix wave 1 — mints a version-ordered delete-marker envelope, passed through to\n * `buildLookupHandle`. See `port/with/lookup-strategy.ts`'s `BuildLookupHandleOptions` doc\n * comment for why this is injected rather than imported by the handle itself.\n */\n buildDeleteMarker: (version: number, actor: string) => EncryptedEnvelope\n /** #650 Task 5 (#648) — the real reference check, passed through to `buildLookupHandle`. */\n checkReferencesOnDelete?: ((key: string) => Promise<unknown>) | undefined\n}\n\n/**\n * @internal\n */\nexport interface I18nStrategy {\n /**\n * Resolve `i18nText` fields on a record to the requested locale and\n * return a new object. Returns the input unchanged under\n * `NO_I18N`.\n */\n applyI18nLocale(\n record: Record<string, unknown>,\n fields: Record<string, I18nTextDescriptor>,\n locale: string,\n fallback?: string | readonly string[],\n layer?: Layer,\n ): Record<string, unknown>\n\n /**\n * Validate that an i18nText field's value satisfies its descriptor\n * (required locales present, etc.). Throws under `NO_I18N` —\n * declaring i18nFields without opting in is a misconfiguration.\n */\n validateI18nTextValue(\n value: unknown,\n field: string,\n descriptor: I18nTextDescriptor,\n ): void\n\n /**\n * Enforce per-locale script constraints over an i18nText value map\n * (write-time). Returns the (possibly filtered) value plus any\n * non-fatal warnings. Returns the value unchanged when the field has\n * no `script` option, or under `NO_I18N`.\n */\n enforceScript(\n value: Record<string, unknown>,\n field: string,\n descriptor: I18nTextDescriptor,\n exempt?: ReadonlySet<string>,\n ): { value: Record<string, unknown>; warnings: ScriptWarning[] }\n\n /**\n * Per-field locales that are unchanged round-tripped densify fills (exempt\n * from write-time script enforcement). Empty under NO_I18N.\n */\n computeExemptFills(\n prior: Record<string, unknown> | undefined,\n incoming: Record<string, unknown>,\n fields: Record<string, I18nTextDescriptor>,\n ): Map<string, Set<string>>\n\n /**\n * Eager-fill empty i18n slots from each field's substitute chain and record\n * provenance in `record['_i18nFilled']`. Mutates `record`. No-op under\n * NO_I18N.\n */\n densify(\n record: Record<string, unknown>,\n prior: Record<string, unknown> | undefined,\n fields: Record<string, I18nTextDescriptor>,\n ): void\n\n /**\n * Construct a typed `DictionaryHandle` for the named dictionary.\n * Throws under `NO_I18N`.\n */\n buildDictionaryHandle<Keys extends string = string>(\n opts: BuildDictionaryHandleOptions<Keys>,\n ): DictionaryHandle<Keys>\n}\n\nfunction notEnabled(op: string): Error {\n return new Error(\n `${op} requires the i18n strategy. Import ` +\n '`{ withI18n }` from \"@noy-db/hub/i18n\" and pass it to ' +\n '`createNoydb({ i18nStrategy: withI18n() })`.',\n )\n}\n\n/**\n * No-i18n stub. Locale resolution is the identity; validation and\n * dictionary construction throw with an actionable pointer.\n *\n * @internal\n */\nexport const NO_I18N: I18nStrategy = {\n applyI18nLocale(record) { return record },\n validateI18nTextValue() { throw notEnabled('i18nText field validation') },\n enforceScript(value) { return { value, warnings: [] } },\n computeExemptFills() { return new Map<string, Set<string>>() },\n densify() {},\n buildDictionaryHandle() { throw notEnabled('vault.dictionary()') },\n}\n\n/**\n * Return true when a collection name is a reserved dictionary collection\n * (the `_dict_*` prefix). Mirrors `DICT_COLLECTION_PREFIX` — now on\n * `via/lookup/handle.ts` (#650 Task 1), re-exported from\n * `via/i18n/dictionary.ts` for compat — duplicated here (not\n * imported) so this port has no VALUE dependency back on the feature; keep\n * the two in sync if the prefix ever changes.\n */\nexport function isDictCollectionName(name: string): boolean {\n return name.startsWith('_dict_')\n}\n\n/** Runtime predicate for detecting a StaticDictDescriptor. */\nexport function isStaticDictDescriptor(x: unknown): x is StaticDictDescriptor {\n return (\n typeof x === 'object' &&\n x !== null &&\n (x as { _noydbStaticDict?: unknown })._noydbStaticDict === true\n )\n}\n\n/**\n * Runtime predicate for detecting an I18nTextDescriptor. Pure tag check —\n * moved here (#623 Task 11) alongside `isStaticDictDescriptor` so\n * `kernel/via/compose.ts` (`mergeViaFields`'s descriptor-shape classification)\n * can reach it through the port instead of importing `via/i18n/core.js`\n * directly. Re-exported from that module for compat with existing importers.\n */\nexport function isI18nTextDescriptor(x: unknown): x is I18nTextDescriptor {\n return (\n typeof x === 'object' &&\n x !== null &&\n (x as { _noydbI18nText?: unknown })._noydbI18nText === true\n )\n}\n\n/**\n * Runtime predicate for detecting a DictKeyDescriptor. Pure tag check —\n * moved here (#623 Task 11) for the same reason as `isI18nTextDescriptor`\n * above. Re-exported from `via/i18n/dictionary.js` for compat with\n * existing importers.\n */\nexport function isDictKeyDescriptor(x: unknown): x is DictKeyDescriptor {\n return (\n typeof x === 'object' &&\n x !== null &&\n (x as { _noydbDictKey?: unknown })._noydbDictKey === true\n )\n}\n\n/**\n * Type-only re-exports (#623 Task 8) — the kernel spine (collection.ts,\n * collection-config.ts, vault.ts, types.ts) imports these descriptor/handle\n * types through the port instead of reaching into `via/i18n/*`\n * directly, so `src/kernel/**` carries no via-i18n specifier at all.\n * `isolatedModules: true` erases these at build time — no runtime coupling.\n */\nexport type { I18nTextDescriptor } from '../../via/i18n/core.js'\nexport type { DictKeyDescriptor, DictionaryHandle, DictionaryOptions, StaticDictDescriptor } from '../../via/i18n/dictionary.js'\nexport type { Layer } from '../../via/i18n/policy.js'\nexport type { ScriptWarning } from '../../via/i18n/script.js'\n","/**\n * Generic dotted-path / `[]`-wildcard record helpers. Kernel-owned:\n * these carry zero i18n logic — they were extracted out of\n * `via/i18n/core.ts` (Task 7, #623) so kernel call-sites (put-time\n * validation, densify) can import them without a spine→service edge.\n *\n * @internal\n */\n\n/**\n * Return all leaf values at `path`, expanding `[].` array wildcards.\n *\n * - `'name'` → `[obj.name]`\n * - `'address.lineOne'` → `[obj.address.lineOne]`\n * - `'contacts[].title'` → `[obj.contacts[0].title, obj.contacts[1].title, …]`\n *\n * Returns an empty array when the path does not resolve (missing key,\n * wrong type, etc.). Used by `enforceI18nOnPut` to validate nested fields.\n */\nexport function getAtPath(obj: Record<string, unknown>, path: string): unknown[] {\n const arrayIdx = path.indexOf('[].')\n if (arrayIdx !== -1) {\n const arrayKey = path.slice(0, arrayIdx)\n const restPath = path.slice(arrayIdx + 3)\n const arr = obj[arrayKey]\n if (!Array.isArray(arr)) return []\n return arr.flatMap(item => {\n if (!item || typeof item !== 'object' || Array.isArray(item)) return []\n return getAtPath(item as Record<string, unknown>, restPath)\n })\n }\n const dotIdx = path.indexOf('.')\n if (dotIdx !== -1) {\n const head = path.slice(0, dotIdx)\n const rest = path.slice(dotIdx + 1)\n const nested = obj[head]\n if (!nested || typeof nested !== 'object' || Array.isArray(nested)) return []\n return getAtPath(nested as Record<string, unknown>, rest)\n }\n const val = obj[path]\n return val !== undefined ? [val] : []\n}\n\n/**\n * Mutate `obj` in-place, setting `value` at the nested `path`.\n * Supports dot notation (`'address.lineOne'`) but not array wildcards —\n * auto-translate on `contacts[].title` style paths is not supported.\n */\nexport function setAtPathInPlace(\n obj: Record<string, unknown>,\n path: string,\n value: unknown,\n): void {\n const dotIdx = path.indexOf('.')\n if (dotIdx !== -1) {\n const head = path.slice(0, dotIdx)\n const rest = path.slice(dotIdx + 1)\n const nested = obj[head]\n if (!nested || typeof nested !== 'object' || Array.isArray(nested)) return\n setAtPathInPlace(nested as Record<string, unknown>, rest, value)\n return\n }\n obj[path] = value\n}\n","/**\n * Per-layer i18n resolution policy.\n *\n * `onMissing` governs what happens when a multilingual field is resolved\n * to a target locale that is absent. It may be a single scalar policy or\n * a per-layer map, so a field can be lenient at the app read boundary but\n * strict inside a materialized view.\n *\n * Effective policy for layer `λ`:\n *\n * ```\n * explicit(λ) = typeof onMissing === 'object' ? onMissing[λ] : undefined\n * scalar = typeof onMissing === 'string' ? onMissing : undefined\n * policy(λ) = explicit(λ) ?? layerDefault(λ) ?? scalar ?? 'throw'\n * ```\n *\n * - `layerDefault('guard') = 'substitute'` — guards are lenient unless\n * EXPLICITLY overridden; they never inherit a scalar policy (a guard\n * reading a display value must not hard-fail on a missing locale).\n * - every other layer has no default, so it inherits the scalar, else\n * falls back to `'throw'` (today's behavior — zero breaking change).\n *\n * @public\n */\nexport type OnMissing = 'substitute' | 'null' | 'throw'\n\n/**\n * The contexts in which a multilingual field is resolved. Each can carry\n * its own `onMissing` policy.\n *\n * - `read` — ordinary app reads (`get`/`list`/query projection).\n * - `guard` — a guard callback reading a value.\n * - `join` — a joined record expanded onto a row.\n * - `mv` — materialized-view input.\n * - `derivation` — derivation input.\n * - `export` — bundle/public-envelope export.\n */\nexport type Layer = 'read' | 'guard' | 'join' | 'mv' | 'derivation' | 'export'\n\n/** Field-level policy: a single scalar, or a per-layer map. */\nexport type OnMissingPolicy = OnMissing | Partial<Record<Layer, OnMissing>>\n\n/**\n * Resolve the effective `OnMissing` for a layer from a field's declared\n * policy. See module docs for the resolution rule.\n */\nexport function resolvePolicy(\n onMissing: OnMissingPolicy | undefined,\n layer: Layer,\n): OnMissing {\n const explicit =\n onMissing && typeof onMissing === 'object' ? onMissing[layer] : undefined\n const scalar = typeof onMissing === 'string' ? onMissing : undefined\n const layerDefault: OnMissing | undefined =\n layer === 'guard' ? 'substitute' : undefined\n return explicit ?? layerDefault ?? scalar ?? 'throw'\n}\n","/**\n * Per-locale script enforcement for `i18nText` fields (write-time).\n *\n * Each locale slot's string is validated against an allowed set of\n * Unicode scripts. `'auto'` infers the set from the locale code with\n * **asymmetric Latin tolerance**: every non-Latin-script locale\n * also allows `Latin`, because proper names and addresses in those\n * locales routinely embed Latin brand/building/technical names — while\n * Latin-script locales do NOT allow other scripts, so the common error\n * (e.g. Thai text dumped into an `en` slot) is still caught.\n *\n * The always-on baseline is `Common` (digits, punctuation), `Inherited`\n * and `Mark` (combining diacritics, joiners, harakat, tone marks), and\n * whitespace — so Latin digits and in-script combining marks never\n * false-reject.\n *\n * @public\n */\nimport { ScriptViolationError } from '../../kernel/errors.js'\nimport type { I18nTextDescriptor } from './core.js'\n\n/** Locales whose base language is written in the Latin script. */\nconst LATIN_BASE = new Set([\n 'en', 'fr', 'de', 'es', 'it', 'pt', 'nl', 'sv', 'no', 'da', 'fi', 'is',\n 'pl', 'cs', 'sk', 'hu', 'ro', 'hr', 'sl', 'et', 'lv', 'lt', 'tr', 'vi',\n 'id', 'ms', 'tl', 'sw', 'af', 'ca', 'gl', 'eu', 'cy', 'ga',\n])\n\n/** Base-language → primary (non-Latin) scripts. Latin is appended by inferScripts. */\nconst SCRIPT_TABLE: Record<string, readonly string[]> = {\n th: ['Thai'],\n ko: ['Hangul', 'Han'],\n ja: ['Han', 'Hiragana', 'Katakana'],\n zh: ['Han'],\n ar: ['Arabic'],\n fa: ['Arabic'],\n ur: ['Arabic'],\n ru: ['Cyrillic'],\n uk: ['Cyrillic'],\n bg: ['Cyrillic'],\n sr: ['Cyrillic'],\n he: ['Hebrew'],\n el: ['Greek'],\n hi: ['Devanagari'],\n ta: ['Tamil'],\n km: ['Khmer'],\n lo: ['Lao'],\n my: ['Myanmar'],\n}\n\n/** Map a BCP-47 script subtag (e.g. `Latn`, `Cyrl`) to allowed scripts. */\nconst SUBTAG_SCRIPTS: Record<string, readonly string[]> = {\n Latn: ['Latin'],\n Cyrl: ['Cyrillic', 'Latin'],\n Hans: ['Han', 'Latin'],\n Hant: ['Han', 'Latin'],\n Thai: ['Thai', 'Latin'],\n Arab: ['Arabic', 'Latin'],\n}\n\n/**\n * Infer the allowed Unicode scripts for a BCP-47 locale, with asymmetric\n * Latin tolerance. A script subtag (`th-Latn`) wins over the base\n * language. Unknown locales default to `['Latin']`.\n */\nexport function inferScripts(locale: string): readonly string[] {\n const parts = locale.split('-')\n const subtag = parts.find((t) => /^[A-Z][a-z]{3}$/.test(t))\n if (subtag && SUBTAG_SCRIPTS[subtag]) return SUBTAG_SCRIPTS[subtag]\n\n const base = (parts[0] ?? '').toLowerCase()\n if (LATIN_BASE.has(base)) return ['Latin']\n const primary = SCRIPT_TABLE[base]\n if (primary) return [...primary, 'Latin'] // asymmetric Latin tolerance\n return ['Latin']\n}\n\n/** Resolve the allowed scripts for a field's locale slot. */\nfunction allowedFor(descriptor: I18nTextDescriptor, locale: string): readonly string[] {\n const script = descriptor.options.script\n if (script && script !== 'auto') {\n const explicit = script[locale]\n if (explicit) return explicit\n }\n return inferScripts(locale)\n}\n\n/** Always-allowed baseline character classes (besides the named scripts). */\nconst BASELINE = String.raw`\\p{White_Space}\\p{Script=Common}\\p{Script=Inherited}\\p{Mark}`\n\n/** Build a whole-string matcher for the allowed scripts. */\nfunction fullMatcher(scripts: readonly string[]): RegExp {\n const cls = scripts.map((s) => `\\\\p{Script=${s}}`).join('')\n return new RegExp(`^[${BASELINE}${cls}]*$`, 'u')\n}\n\n/** Build a single-character matcher (for sampling / stripping). */\nfunction charMatcher(scripts: readonly string[]): RegExp {\n const cls = scripts.map((s) => `\\\\p{Script=${s}}`).join('')\n return new RegExp(`[${BASELINE}${cls}]`, 'u')\n}\n\n/** Collect a short sample of characters that violate the allowed scripts. */\nfunction offendingSample(str: string, scripts: readonly string[]): string {\n const ok = charMatcher(scripts)\n const bad: string[] = []\n for (const ch of str) {\n if (!ok.test(ch)) bad.push(ch)\n if (bad.length >= 8) break\n }\n return bad.join('')\n}\n\n/** Remove characters that violate the allowed scripts. */\nfunction stripDisallowed(str: string, scripts: readonly string[]): string {\n const ok = charMatcher(scripts)\n let out = ''\n for (const ch of str) if (ok.test(ch)) out += ch\n return out\n}\n\n/** A non-fatal script violation recorded under `'filter'`/`'warn'` modes. */\nexport interface ScriptWarning {\n readonly field: string\n readonly locale: string\n readonly expected: readonly string[]\n readonly sample: string\n}\n\n/**\n * Enforce a field's script constraint over an i18nText value map.\n *\n * - No `script` option ⇒ returns the value unchanged.\n * - `onScriptViolation: 'reject'` (default) ⇒ throws {@link ScriptViolationError}.\n * - `'filter'` ⇒ returns a copy with disallowed characters stripped + warnings.\n * - `'warn'` ⇒ returns the value unchanged + warnings.\n */\nexport function enforceScript(\n value: Record<string, unknown>,\n field: string,\n descriptor: I18nTextDescriptor,\n exempt?: ReadonlySet<string>,\n): { value: Record<string, unknown>; warnings: ScriptWarning[] } {\n const opt = descriptor.options\n if (!opt.script) return { value, warnings: [] }\n\n const mode = opt.onScriptViolation ?? 'reject'\n const warnings: ScriptWarning[] = []\n let out = value\n\n for (const [locale, raw] of Object.entries(value)) {\n if (exempt?.has(locale)) continue\n if (typeof raw !== 'string') continue\n const allowed = allowedFor(descriptor, locale)\n if (fullMatcher(allowed).test(raw)) continue\n\n const sample = offendingSample(raw, allowed)\n if (mode === 'reject') {\n throw new ScriptViolationError(field, locale, allowed, sample)\n }\n warnings.push({ field, locale, expected: allowed, sample })\n if (mode === 'filter') {\n if (out === value) out = { ...value }\n out[locale] = stripDisallowed(raw, allowed)\n }\n }\n\n return { value: out, warnings }\n}\n","/**\n * The i18n `ViaBinding` — wires the i18n engine (auto-translate/script\n * enforcement/validation/densify on write; locale + dict-label presentation\n * on read) into the kernel's generic Via port. Mirrors\n * `via/money/binding.ts`'s #553 static-link pattern.\n *\n * LIVE (#623 Task 8 wired the cutover): `i18nBinding()`/`linkI18nVia()` are\n * the real put/read path for every i18n-declaring collection —\n * `collection.ts`'s `_putInternal`/`applyLocaleToRecord` (the call sites\n * this file's bodies were moved/adapted from) now delegate their i18n\n * stages to `this.via.encodeWrite`/`this.via.present` instead of running\n * the hand-wired i18n logic inline.\n *\n * `i18nText()`, `dictKey()`, `staticDict()` each call {@link linkI18nVia}\n * first — the same #553 pattern `money()` uses.\n */\nimport type { ViaBinding, ViaWriteCtx, ViaReadCtx } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport type { I18nTextDescriptor } from './core.js'\nimport { stripI18nFilled } from './core.js'\nimport type { DictKeyDescriptor, StaticDictDescriptor } from './dictionary.js'\nimport type { I18nStrategy } from '../../port/with/i18n-strategy.js'\nimport { isStaticDictDescriptor } from '../../port/with/i18n-strategy.js'\nimport { resolvePolicy, type Layer } from './policy.js'\nimport { getAtPath, setAtPathInPlace } from '../../kernel/paths.js'\nimport { TranslatorNotConfiguredError, LocaleNotSpecifiedError } from '../../kernel/errors.js'\n\n/**\n * Config a collection's i18n declarations resolve to — the binding's\n * construction input. Fields are loosely typed (`unknown`) at this seam\n * because the kernel's `ViaBinder` contract is opaque-config-in; the\n * concrete i18n types are applied inside the binding's own functions.\n */\nexport interface I18nViaConfig {\n i18nFields?: Record<string, unknown> // I18nTextDescriptor map\n dictKeyFields?: Record<string, unknown> // DictKeyDescriptor | StaticDictDescriptor map\n i18nDensifyFields?: Record<string, unknown> // I18nTextDescriptor subset (densifyOnWrite: true)\n strategy: unknown // I18nStrategy (real or NO_I18N)\n defaultLocale?: string\n autoTranslateHook?: (...args: unknown[]) => Promise<string>\n dictLabelResolver?: (...args: unknown[]) => Promise<string | undefined>\n i18nPutValidator?: (record: unknown) => void // the vault-built closure (unchanged wiring)\n collectionName: string\n}\n\n// ─── write pipeline ─────────────────────────────────────────────────────\n\n/**\n * Auto-translate missing i18nText translations. Runs BEFORE i18n\n * validation so translated values satisfy the required-locale constraint.\n * Throws `TranslatorNotConfiguredError` when a field has\n * `autoTranslate: true` but no hook was configured. Mutates `record`\n * in place (mirrors `collection.ts:1830-1874`).\n */\nasync function runI18nAutoTranslate(\n record: Record<string, unknown>,\n i18nFields: Record<string, I18nTextDescriptor>,\n cfg: I18nViaConfig,\n): Promise<void> {\n for (const [field, descriptor] of Object.entries(i18nFields)) {\n if (!descriptor.options.autoTranslate) continue\n // getAtPath returns [] for array-wildcard paths — auto-translate on\n // 'contacts[].field' style paths is not supported; skip silently.\n const leafValues = getAtPath(record, field)\n if (leafValues.length !== 1) continue\n const value = leafValues[0]\n if (!value || typeof value !== 'object' || Array.isArray(value)) continue\n const map = value as Record<string, string>\n // Determine which locales need translation. For 'all', translate all\n // declared languages that are missing. For 'any', only translate if\n // none are present. For string[], translate the listed required ones.\n const { languages, required } = descriptor.options\n const missing: string[] = languages.filter(\n (lang) => !(lang in map) || map[lang] === '',\n )\n if (missing.length === 0) continue\n // Find a source locale (first present non-empty value)\n const sourceLocale = languages.find((l) => l in map && map[l] !== '')\n if (!sourceLocale) continue\n if (!cfg.autoTranslateHook) {\n throw new TranslatorNotConfiguredError(field, cfg.collectionName)\n }\n // Only translate locales that are actually needed\n const toTranslate =\n required === 'any'\n ? [] // 'any' is already satisfied since sourceLocale exists\n : required === 'all'\n ? missing\n : missing.filter((l) => required.includes(l))\n const translated = { ...map }\n for (const targetLocale of toTranslate) {\n translated[targetLocale] = await cfg.autoTranslateHook(\n map[sourceLocale]!,\n sourceLocale,\n targetLocale,\n field,\n cfg.collectionName,\n )\n }\n setAtPathInPlace(record, field, translated)\n }\n}\n\n/**\n * i18nText script enforcement — runs AFTER auto-translate (so generated\n * values are checked too). Throws `ScriptViolationError` (from inside\n * `strategy.enforceScript`) under the default 'reject'; 'filter' strips\n * disallowed chars in place; 'warn' leaves the value unchanged. Mirrors\n * `collection.ts:1896-1927`.\n */\nfunction runI18nScriptEnforcement(\n record: Record<string, unknown>,\n i18nFields: Record<string, I18nTextDescriptor>,\n strategy: I18nStrategy,\n exemptFills: Map<string, Set<string>> | undefined,\n ctx: ViaWriteCtx,\n cfg: I18nViaConfig,\n): void {\n for (const [field, descriptor] of Object.entries(i18nFields)) {\n if (!descriptor.options.script) continue\n for (const leaf of getAtPath(record, field)) {\n if (!leaf || typeof leaf !== 'object' || Array.isArray(leaf)) continue\n const leafMap = leaf as Record<string, unknown>\n const { value: cleaned, warnings } = strategy.enforceScript(\n leafMap,\n field,\n descriptor,\n exemptFills?.get(field),\n )\n if (cleaned !== leafMap) Object.assign(leafMap, cleaned)\n // enforceScript only returns warnings under 'warn'/'filter' ('reject'\n // throws first), so this guard never fires — it makes that invariant\n // explicit and keeps `mode` off the optional-undefined type.\n const mode = descriptor.options.onScriptViolation\n if (mode === 'warn' || mode === 'filter') {\n for (const w of warnings) {\n // Exact original payload shape (collection.ts:1916-1922 pre-cutover).\n ctx.emit('i18n:script-violation', {\n vault: ctx.vault,\n collection: cfg.collectionName,\n id: ctx.id,\n mode,\n warning: w,\n })\n }\n }\n }\n }\n}\n\n/**\n * `encodeWrite` — translate → script → validate → densify, in the EXACT\n * order `_putInternal` runs them today (seam-map Part 2, stages 7-11).\n */\nasync function runI18nWriteStages(\n record: Record<string, unknown>,\n ctx: ViaWriteCtx,\n cfg: I18nViaConfig,\n): Promise<Record<string, unknown>> {\n const i18nFields = cfg.i18nFields as Record<string, I18nTextDescriptor> | undefined\n const i18nDensifyFields = cfg.i18nDensifyFields as Record<string, I18nTextDescriptor> | undefined\n const strategy = cfg.strategy as I18nStrategy\n\n // 1. Auto-translate.\n if (i18nFields) {\n await runI18nAutoTranslate(record, i18nFields, cfg)\n }\n\n // 2. densifyOnWrite: read prior fills so a round-tripped derived copy is\n // exempt from script enforcement and can be refreshed. Read once here,\n // reused by densify() below. `ctx.prior()` resolves the real prior\n // record (collection.ts wires it to `resolveDensifyPrior`, #623 Task 8).\n let densifyPrior: Record<string, unknown> | undefined\n let exemptFills: Map<string, Set<string>> | undefined\n if (i18nDensifyFields) {\n densifyPrior = (await ctx.prior()) ?? undefined\n exemptFills = strategy.computeExemptFills(densifyPrior, record, i18nDensifyFields)\n }\n\n // 3. Script enforcement.\n if (i18nFields) {\n runI18nScriptEnforcement(record, i18nFields, strategy, exemptFills, ctx, cfg)\n }\n\n // 4. i18nText validation — AFTER auto-translate + script so the record is\n // trustworthy. Throws MissingTranslationError when required translations\n // are absent. The vault-built closure (unchanged wiring).\n cfg.i18nPutValidator?.(record)\n\n // 5. Eager-fill empty slots + record provenance. Runs AFTER the authored\n // gates (required + script) so only authored slots are validated.\n if (i18nDensifyFields) {\n strategy.densify(record, densifyPrior, i18nDensifyFields)\n }\n\n return record\n}\n\n// ─── read pipeline ──────────────────────────────────────────────────────\n\n/**\n * `present` — i18nText locale resolution + dictKey/staticDict label\n * resolution + `_i18nFilled` stripping. Adapted from\n * `applyLocaleToRecord`'s i18n+dict+strip portions (`collection.ts:4184-\n * 4301`) — the money-decode portion of that method is a separate binding\n * (`via/money`) and is NOT this function's concern.\n */\nasync function runI18nPresent(\n record: Record<string, unknown>,\n ctx: ViaReadCtx,\n cfg: I18nViaConfig,\n): Promise<Record<string, unknown>> {\n const i18nFields = cfg.i18nFields as Record<string, I18nTextDescriptor> | undefined\n const dictKeyFields = cfg.dictKeyFields as Record<string, DictKeyDescriptor | StaticDictDescriptor> | undefined\n const strategy = cfg.strategy as I18nStrategy\n const hasI18n = i18nFields !== undefined && Object.keys(i18nFields).length > 0\n const hasDict = dictKeyFields !== undefined && Object.keys(dictKeyFields).length > 0\n if (!hasI18n && !hasDict) return record\n\n const locale = typeof ctx.locale === 'string' ? ctx.locale : undefined\n const fallback = ctx.fallback as string | readonly string[] | undefined\n const layer = ctx.layer as Layer\n\n let result = record\n\n // i18nText / dictKey resolution require an active locale — EXCEPT a\n // static dict declaring a `displayLocale`, which resolves its\n // `<field>Label` even under a locale-less read (the hybrid hinge). Only\n // this second gate relaxes, and ONLY for static-display fields — folding\n // `hasI18n` in here would let an i18nText-only collection fall through to\n // applyI18nLocale(…, undefined) on a locale-less read, breaking the\n // raw-{th,en}-map invariant.\n const hasStaticDisplay =\n hasDict &&\n dictKeyFields !== undefined &&\n Object.values(dictKeyFields).some(\n (d) => isStaticDictDescriptor(d) && d.displayLocale !== undefined,\n )\n\n // Strip the internal densify marker even when no locale is active\n // (applyI18nLocale, which normally strips it, is skipped on this path).\n // Non-mutating: never touches the cached/stored record object.\n if (!locale && !hasStaticDisplay) return stripI18nFilled(result)\n\n // 1. i18nText resolution — guarded on `locale`, because the relaxed gate\n // above can now be entered with `locale === undefined` (static-display).\n // The layer selects the field's per-layer `onMissing` policy inside\n // applyI18nLocale.\n if (locale && hasI18n && i18nFields) {\n result = strategy.applyI18nLocale(result, i18nFields, locale, fallback, layer)\n }\n\n // 2. dictKey / staticDict label resolution\n if (hasDict && dictKeyFields && cfg.dictLabelResolver && locale !== 'raw') {\n const withLabels = { ...result }\n const resolver = cfg.dictLabelResolver\n for (const [field, desc] of Object.entries(dictKeyFields)) {\n // dictKey default policy is 'null' (omit/null on miss) — today's\n // behavior — unless the field declares onMissing. 'substitute'\n // walks the declared substitute chain (passed as the resolver's\n // fallback); 'throw' raises LocaleNotSpecifiedError.\n const policy = desc.onMissing ? resolvePolicy(desc.onMissing, layer) : 'null'\n const fieldFallback =\n policy === 'substitute'\n ? (fallback ?? desc.substitute)\n : fallback\n // Per-field effective locale: a static dict falls back to its\n // `displayLocale` when no locale is active (the hybrid hinge); a\n // plain dictKey with no displayLocale gets `undefined` → its\n // <field>Label is omitted on a locale-less read (today's behavior).\n const effLocale =\n locale ??\n (isStaticDictDescriptor(desc) ? desc.displayLocale : undefined)\n // Resolve one key → label | null, honoring the policy. With no\n // effective locale there is nothing to resolve against.\n const resolveKey = async (key: string): Promise<string | null> => {\n if (!effLocale) {\n if (policy === 'throw') {\n throw new LocaleNotSpecifiedError(\n field,\n `dictKey \"${field}\": no locale active to resolve key \"${key}\".`,\n )\n }\n return null\n }\n const label = await resolver(desc.name, key, effLocale, fieldFallback)\n if (label === undefined) {\n if (policy === 'throw') {\n throw new LocaleNotSpecifiedError(\n field,\n `dictKey \"${field}\": no label for key \"${key}\" in locale \"${effLocale}\".`,\n )\n }\n return null\n }\n return label\n }\n\n if (field.includes('[].')) {\n // Wildcard path `arrayKey[].leaf`: add a per-element sibling\n // `<leaf>Label`. Single level + simple leaf.\n const parts = field.split('[].')\n const arrayKey = parts[0]!\n const leaf = parts[1]\n if (!leaf || leaf.includes('.')) continue\n const arr = (withLabels as Record<string, unknown>)[arrayKey]\n if (!Array.isArray(arr)) continue\n const labelKey = `${leaf}Label`\n ;(withLabels as Record<string, unknown>)[arrayKey] = await Promise.all(\n arr.map(async (el) => {\n if (!el || typeof el !== 'object' || Array.isArray(el)) return el\n const k = (el as Record<string, unknown>)[leaf]\n if (typeof k !== 'string') return el\n return { ...(el as Record<string, unknown>), [labelKey]: await resolveKey(k) }\n }),\n )\n continue\n }\n\n const val = result[field]\n if (Array.isArray(val)) {\n // Array-of-keys → [{ key, label }] pair objects (key preserved).\n withLabels[`${field}Label`] = await Promise.all(\n val.map(async (k) => ({\n key: k,\n label: typeof k === 'string' ? await resolveKey(k) : null,\n })),\n )\n } else if (typeof val === 'string') {\n const label = await resolveKey(val)\n // Scalar under 'null'/default omits the label key (today's\n // behavior); 'substitute' returns a value; 'throw' threw above.\n if (label !== null) withLabels[`${field}Label`] = label\n }\n }\n result = withLabels\n }\n\n // Final guard: the locale-less static-display path skips\n // applyI18nLocale's strip, so ensure the densify marker never leaks here\n // either. Non-mutating (no-op when absent or already stripped above).\n return stripI18nFilled(result)\n}\n\n// ─── introspection ──────────────────────────────────────────────────────\n\nfunction buildI18nDescribeFragment(cfg: I18nViaConfig): Record<string, unknown> {\n return {\n i18nFields: cfg.i18nFields ? Object.keys(cfg.i18nFields) : [],\n dictKeyFields: cfg.dictKeyFields ? Object.keys(cfg.dictKeyFields) : [],\n ...(cfg.defaultLocale !== undefined ? { defaultLocale: cfg.defaultLocale } : {}),\n }\n}\n\n// ─── binding ────────────────────────────────────────────────────────────\n\nexport function i18nBinding(cfg: I18nViaConfig): ViaBinding {\n return {\n brand: 'i18n',\n posture: { encryptedAtRest: 'envelope', queryable: 'full', exportable: true, forgettable: true },\n // DictionaryHandle's `_dict_*` reserved collections (#629 Task 4) — the\n // codec boundary's `reservedEnvelopes` door may address any collection\n // name under this prefix.\n reservedPrefixes: ['_dict_'],\n covers: (field) =>\n (cfg.i18nFields !== undefined && field in cfg.i18nFields)\n || (cfg.dictKeyFields !== undefined && field in cfg.dictKeyFields),\n encodeWrite: async (record, ctx) => runI18nWriteStages(record, ctx, cfg),\n present: async (record, ctx) => runI18nPresent(record, ctx, cfg),\n describeFragment: () => buildI18nDescribeFragment(cfg),\n }\n}\n\nexport function linkI18nVia(): void {\n installViaBinder('i18n', (c) => i18nBinding(c as I18nViaConfig))\n}\n","/**\n * i18nText schema type —\n *\n * `i18nText({ languages, required })` creates a descriptor for a\n * multi-language content field whose value is stored as a\n * `{ [locale]: string }` map (e.g. `{ en: 'Consulting', th: 'ที่ปรึกษา' }`).\n *\n * On put, the descriptor validates that required languages are present.\n * On read (when a `locale` option is passed), the map is collapsed to the\n * caller's locale string via the fallback chain.\n *\n * Design decisions\n * ────────────────\n *\n * **Descriptor pattern (not a Zod type).**\n * `i18nText()` returns a plain descriptor object used in the collection's\n * `i18nFields` option — same pattern as `ref()` / `dictKey()`. This keeps\n * `@noy-db/core` at zero runtime dependencies and avoids Zod v3 field-type\n * constraints. TypeScript inference is handled via the descriptor's type.\n *\n * **Enforcement at the collection boundary.**\n * The `required` option is checked by `Collection.put()` via the compartment's\n * registered `i18nFields`. Failed validation throws `MissingTranslationError`\n * — a distinct class from `SchemaValidationError` so callers can tell\n * \"wrong shape\" from \"missing translations\".\n *\n * **Resolution is post-decryption.**\n * Locale resolution happens AFTER `decryptRecord()`, as a pure in-memory\n * transform. No additional crypto work is needed. The resolved record is\n * returned in place of the stored one, with i18nText fields replaced by\n * their locale-resolved strings.\n *\n * **`locale: 'raw'`.**\n * Passing `{ locale: 'raw' }` skips resolution and returns the full\n * `{ [locale]: string }` map — useful for bilingual exports, admin UIs,\n * and any context where all translations must be visible at once.\n *\n * **Out of scope.**\n * Pluralization, RTL rendering, date/number formatting, per-locale CRDT\n * merging.\n */\n\nimport { MissingTranslationError, LocaleNotSpecifiedError } from '../../kernel/errors.js'\nimport type { OnMissing, OnMissingPolicy, Layer } from './policy.js'\nimport { resolvePolicy } from './policy.js'\nimport { inferScripts } from './script.js'\nimport { linkI18nVia } from './binding.js'\n\n// ─── I18nMap type helper ───────────────────────────────────────────────\n\n/** Flatten an intersection into a single object literal for nicer hovers. */\ntype Prettify<T> = { [K in keyof T]: T[K] } & {}\n\n/**\n * The stored shape of a multilingual field, inferred from its `required`\n * mode — so the compiler forces you to handle an absent optional locale\n * (`string | undefined`) instead of silently yielding `undefined`.\n *\n * Mirrors `i18nText({ languages, required })`:\n * - `'all'` (default) — every locale required: `{ th: string; en: string }`\n * - `'any'` — every locale optional: `{ th?: string; en?: string }`\n * (the \"at least one present\" guarantee is runtime-only — not expressible\n * in TypeScript — so each key is optional)\n * - `readonly L[]` — listed locales required, the rest optional:\n * `I18nMap<'th'|'en', ['th']>` → `{ th: string; en?: string }`\n *\n * @example\n * ```ts\n * type Lang = 'th' | 'en'\n * interface Contact {\n * name: I18nMap<Lang, 'any'> // { th?: string; en?: string }\n * legalName: I18nMap<Lang, ['th']> // { th: string; en?: string }\n * slug: I18nMap<Lang> // { th: string; en: string }\n * }\n * ```\n *\n * @public\n */\nexport type I18nMap<\n Langs extends string,\n Required extends 'all' | 'any' | readonly Langs[] = 'all',\n> = Required extends 'all'\n ? Record<Langs, string>\n : Required extends 'any'\n ? Partial<Record<Langs, string>>\n : Required extends readonly (infer R extends Langs)[]\n ? Prettify<Record<R, string> & Partial<Record<Exclude<Langs, R>, string>>>\n : never\n\n// ─── i18nText descriptor ───────────────────────────────────────────────\n\n/**\n * Options for `i18nText()`.\n *\n * `languages` declares the full set of supported locales. `required`\n * controls which must be present on every `put()`.\n *\n * `autoTranslate` is the per-field opt-in for the `plaintextTranslator`\n * hook. When `true` and a `plaintextTranslator` is configured\n * on `createNoydb()`, missing translations are generated before `put()`.\n * Default: `false`.\n */\nexport interface I18nTextOptions {\n /** All supported locale codes (BCP 47). */\n readonly languages: readonly string[]\n /**\n * Which locales must be present on every `put()`.\n *\n * - `'all'` — every declared language must be present.\n * - `'any'` — at least one declared language must be present.\n * - `string[]` — listed locales are required; others are optional.\n */\n readonly required: 'all' | 'any' | readonly string[]\n /**\n * Per-field opt-in for the `plaintextTranslator` hook.\n * When `true`, missing required translations are auto-generated\n * before `put()` if a translator is configured. Default: `false`.\n */\n readonly autoTranslate?: boolean\n /**\n * What to do when this field is resolved to a locale that is absent.\n * A single policy, or a per-layer map (read/guard/join/mv/derivation/\n * export). Default `'throw'` — today's behavior, zero breaking change.\n * See {@link OnMissingPolicy}.\n *\n * NOTE (current wiring): ALL layers are enforced — `read` (`get`/`list`),\n * `guard`, `derivation`, `mv`, `join`, `export`. Guard / derivation\n * `ctx.vault` reads resolve under their own layer policy (`guard` defaults to\n * the lenient `'substitute'`). The `mv` layer fires for materialized views\n * that declare `{ i18nLocale, i18nFields }` — UNION (group-key i18n fields\n * resolve before the unified-row bucketing) and query-form (resolved in\n * `GroupedAggregation.run` before `groupAndReduce`); grouping a raw i18n field\n * without a locale throws. The `join` layer resolves a joined right-side i18n\n * field to the query locale (`toArray({ locale })` or the vault default; raw\n * when locale-less). The `export` layer fires for\n * `exportStream`/`exportJSON({ resolveLabels })` — records collapse to the\n * export locale.\n */\n readonly onMissing?: OnMissingPolicy\n /**\n * Ordered preferred-substitute locales used when `onMissing` resolves\n * to `'substitute'` and the target locale is absent. `'any'` as an\n * element means \"first non-empty value\". A caller-supplied `fallback`\n * at read time takes precedence over this declared list.\n */\n readonly substitute?: readonly string[]\n /**\n * Smart-substitute. When `true`, a missing-locale `substitute` walk that\n * misses the explicit chain prefers the available locale whose script is\n * nearest the target (same script, then Latin) rather than an arbitrary value\n * — e.g. a missing Thai label prefers another Thai (or Latin) translation over\n * an unreadable script. Default `false` (legacy first-non-empty behavior).\n */\n readonly smartSubstitute?: boolean\n /**\n * Per-locale script enforcement (write-time). `'auto'` infers the\n * allowed Unicode scripts per locale (asymmetric Latin tolerance); an\n * object overrides per slot. Absent ⇒ no check. See `./script.ts`.\n */\n readonly script?: 'auto' | Partial<Record<string, readonly string[]>>\n /**\n * What to do when a slot's value contains characters outside its\n * allowed script set. Default `'reject'`.\n */\n readonly onScriptViolation?: 'reject' | 'filter' | 'warn'\n /**\n * Eager-fill empty locale slots from the substitute chain at\n * write time, recording provenance in the internal `_i18nFilled` marker.\n * Mutually exclusive with an EXPLICIT `'throw'` onMissing policy (densify\n * fills every hole, so a throw would be unreachable). Without an explicit\n * `substitute`, fills from `'any'` (first non-empty). Default absent.\n */\n readonly densifyOnWrite?: boolean\n}\n\n/**\n * Descriptor returned by `i18nText()`. Attach to the collection's\n * `i18nFields` option:\n *\n * ```ts\n * const lineItems = company.collection<LineItem>('line-items', {\n * i18nFields: {\n * description: i18nText({ languages: ['en', 'th'], required: 'all' }),\n * },\n * })\n * ```\n */\nexport interface I18nTextDescriptor {\n readonly _noydbI18nText: true\n /** Via port brand marker — lets an `I18nTextDescriptor` satisfy the kernel's opaque `ViaDescriptor` (#623 Task 9). */\n readonly _viaBrand: 'i18n'\n readonly options: I18nTextOptions\n}\n\n/**\n * Create an `I18nTextDescriptor` for a multi-language content field.\n *\n * @param options Language list + enforcement mode.\n *\n * @example\n * ```ts\n * i18nText({ languages: ['en', 'th'], required: 'all' })\n * i18nText({ languages: ['en', 'th'], required: ['th'], autoTranslate: true })\n * ```\n */\nexport function i18nText(options: I18nTextOptions): I18nTextDescriptor {\n // The declaration is the Via binding's opt-in unit (#553): constructing\n // a descriptor statically links the i18n binding into the kernel's Via\n // port, so every later kernel consultation finds it already installed.\n linkI18nVia()\n if (options.densifyOnWrite === true && hasThrowPolicy(options.onMissing)) {\n throw new Error(\n `i18nText: densifyOnWrite cannot be combined with an explicit onMissing 'throw' ` +\n `policy — densify fills every empty slot, so a 'throw' would be unreachable. ` +\n `Remove the 'throw' policy or disable densifyOnWrite.`,\n )\n }\n return { _noydbI18nText: true, _viaBrand: 'i18n', options }\n}\n\n/** True when `onMissing` declares `'throw'` for any layer (scalar or per-layer). */\nfunction hasThrowPolicy(onMissing: OnMissingPolicy | undefined): boolean {\n if (onMissing === undefined) return false\n if (typeof onMissing === 'string') return onMissing === 'throw'\n return Object.values(onMissing).includes('throw')\n}\n\n// `isI18nTextDescriptor` now lives on the kernel port (#623 Task 11,\n// port/with/i18n-strategy.ts, alongside `isStaticDictDescriptor`) —\n// re-exported here for compat with existing importers of this module.\nexport { isI18nTextDescriptor } from '../../port/with/i18n-strategy.js'\n\n// ─── Validation helpers ────────────────────────────────────────────────\n\n/**\n * Validate that a value is a valid `{ [locale]: string }` map and that\n * all required locales are present. Throws `MissingTranslationError`\n * when the required constraint is violated.\n *\n * Called by `Collection.put()` for each registered `i18nField`.\n *\n * @param value The raw field value from the record being put.\n * @param field The field name (used in the thrown error message).\n * @param descriptor The `i18nText()` descriptor for this field.\n */\nexport function validateI18nTextValue(\n value: unknown,\n field: string,\n descriptor: I18nTextDescriptor,\n): void {\n const { options } = descriptor\n\n // Must be a non-null object\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new MissingTranslationError(\n field,\n options.languages,\n `Field \"${field}\" must be a { [locale]: string } map, got ${typeof value}.`,\n )\n }\n\n const map = value as Record<string, unknown>\n\n // All values must be strings\n for (const [locale, v] of Object.entries(map)) {\n if (typeof v !== 'string') {\n throw new MissingTranslationError(\n field,\n [locale],\n `Field \"${field}\": locale \"${locale}\" must be a string, got ${typeof v}.`,\n )\n }\n }\n\n // Check required constraint\n const { required } = options\n if (required === 'all') {\n const missing = options.languages.filter(\n (lang) => !(lang in map) || map[lang] === '',\n )\n if (missing.length > 0) {\n throw new MissingTranslationError(\n field,\n missing,\n `Field \"${field}\" requires all declared languages. Missing: ${missing.join(', ')}.`,\n )\n }\n } else if (required === 'any') {\n const present = options.languages.some(\n (lang) => lang in map && map[lang] !== '',\n )\n if (!present) {\n throw new MissingTranslationError(\n field,\n options.languages,\n `Field \"${field}\" requires at least one declared language. None present.`,\n )\n }\n } else {\n // string[] — named required locales; TypeScript narrows required to readonly string[]\n const requiredList = required\n const missing = requiredList.filter(\n (lang) => !(lang in map) || map[lang] === '',\n )\n if (missing.length > 0) {\n throw new MissingTranslationError(\n field,\n missing,\n `Field \"${field}\" requires: ${requiredList.join(', ')}. Missing: ${missing.join(', ')}.`,\n )\n }\n }\n}\n\n// ─── Locale resolution ─────────────────────────────────────────────────\n\n/**\n * Resolve an i18nText value (`{ [locale]: string }` map) to a string\n * for the given locale.\n *\n * @param value The stored locale map.\n * @param locale The requested locale code, or `'raw'` to return the map.\n * @param fallback Single locale or ordered list; use `'any'` as the last\n * element to fall back to any available translation.\n * @param field Field name used in `LocaleNotSpecifiedError` messages.\n * @returns The resolved string, OR the original map when `locale === 'raw'`.\n */\n/** Options for the policy-aware form of {@link resolveI18nText}. */\nexport interface ResolveI18nOptions {\n /** Effective policy for the resolution layer. Default `'throw'`. */\n readonly policy?: OnMissing\n /** Declared substitute chain; applied only under policy `'substitute'`. */\n readonly substitute?: readonly string[]\n /**\n * Smart-substitute. When `true` and policy is `'substitute'`, after the\n * explicit chain misses, pick the available locale whose script is nearest the\n * target (same script first, then Latin's broad readability) instead of an\n * arbitrary value. Default `false`.\n */\n readonly smartSubstitute?: boolean\n}\n\n/** Normalize a single-or-list fallback into an array. */\nfunction toChain(fallback: string | readonly string[] | undefined): readonly string[] {\n return Array.isArray(fallback) ? fallback : fallback ? [fallback as string] : []\n}\n\n/** Walk a chain, returning the first non-empty value (or `'any'` match). */\nfunction pickFromChain(\n value: Record<string, string>,\n chain: readonly string[],\n): string | undefined {\n for (const fb of chain) {\n if (fb === 'any') {\n const any = Object.values(value).find((v) => v !== '')\n if (any !== undefined) return any\n } else if (value[fb] !== undefined && value[fb] !== '') {\n return value[fb]\n }\n }\n return undefined\n}\n\n// Legacy 4-arg form: can only throw or return — never null. Keeps every\n// existing call site's type unchanged (default policy is 'throw').\nexport function resolveI18nText(\n value: Record<string, string>,\n locale: string,\n fallback?: string | readonly string[],\n field?: string,\n): string | Record<string, string>\n// Policy-aware form: may return null under 'null'/'substitute' policies.\nexport function resolveI18nText(\n value: Record<string, string>,\n locale: string,\n fallback: string | readonly string[] | undefined,\n field: string | undefined,\n opts: ResolveI18nOptions,\n): string | Record<string, string> | null\nexport function resolveI18nText(\n value: Record<string, string>,\n locale: string,\n fallback?: string | readonly string[],\n field?: string,\n opts?: ResolveI18nOptions,\n): string | Record<string, string> | null {\n if (locale === 'raw') {\n return value\n }\n\n if (!locale) {\n throw new LocaleNotSpecifiedError(field ?? '<unknown>')\n }\n\n // Primary locale\n if (value[locale] !== undefined && value[locale] !== '') {\n return value[locale]\n }\n\n const policy: OnMissing = opts?.policy ?? 'throw'\n\n // Caller-supplied fallback ALWAYS applies first (backward compat +\n // explicit read-time override), regardless of policy.\n const callerChain = toChain(fallback)\n const callerHit = pickFromChain(value, callerChain)\n if (callerHit !== undefined) return callerHit\n\n // Declared substitute applies ONLY under policy 'substitute'.\n if (policy === 'substitute') {\n const subHit = pickFromChain(value, toChain(opts?.substitute))\n if (subHit !== undefined) return subHit\n // Smart-substitute: after the explicit chain, prefer the script-nearest\n // available locale over an arbitrary first-non-empty value.\n if (opts?.smartSubstitute) {\n const smartHit = pickNearestScript(value, locale)\n if (smartHit !== undefined) return smartHit\n }\n }\n\n // Exhausted.\n if (policy === 'throw') {\n throw new LocaleNotSpecifiedError(\n field ?? '<unknown>',\n `No translation available for locale \"${locale}\"` +\n (callerChain.length > 0 ? ` or fallback chain [${callerChain.join(', ')}]` : '') +\n '.',\n )\n }\n return null\n}\n\n/**\n * Smart-substitute: among the non-empty locales in `value`, pick the one\n * whose primary script is nearest the target `locale` — same script first, then\n * Latin (broadly readable), then any other — so a missing Thai label falls back\n * to another Thai (or Latin) value rather than, say, an Arabic one. First-seen\n * wins on ties. Returns the value string, or `undefined` when `value` has no\n * non-empty entry.\n */\nfunction pickNearestScript(value: Record<string, string>, target: string): string | undefined {\n const targetScript = inferScripts(target)[0] ?? 'Latin'\n let best: { score: number; v: string } | undefined\n for (const [loc, v] of Object.entries(value)) {\n if (typeof v !== 'string' || v === '') continue\n const s = inferScripts(loc)[0] ?? 'Latin'\n const score = s === targetScript ? 0 : s === 'Latin' ? 1 : 2\n if (best === undefined || score < best.score) best = { score, v }\n if (score === 0) break // nothing beats a same-script match\n }\n return best?.v\n}\n\n// ─── Path helpers (nested i18nFields like 'address.lineOne') ──────────\n\n// `getAtPath`/`setAtPathInPlace` are generic dotted-path/[]-wildcard record\n// helpers, not i18n logic — moved to `kernel/paths.ts` (Task 7, #623).\n// `getAtPath` re-exported here for compat with existing importers of this\n// module (embeddings/descriptor.ts, search/build-docs.ts); `setAtPathInPlace`\n// is not re-exported — its only consumer (via-i18n/binding.ts) imports it\n// directly from kernel/paths.ts.\nexport { getAtPath } from '../../kernel/paths.js'\n\n/** Recursively resolve i18nText at a single path within a record copy. */\nfunction applyAtPath(\n obj: Record<string, unknown>,\n path: string,\n locale: string,\n fallback: string | readonly string[] | undefined,\n opts: ResolveI18nOptions,\n): Record<string, unknown> {\n const arrayIdx = path.indexOf('[].')\n if (arrayIdx !== -1) {\n const arrayKey = path.slice(0, arrayIdx)\n const restPath = path.slice(arrayIdx + 3)\n const arr = obj[arrayKey]\n if (!Array.isArray(arr)) return obj\n return {\n ...obj,\n [arrayKey]: arr.map(item => {\n if (!item || typeof item !== 'object' || Array.isArray(item)) return item\n return applyAtPath(item as Record<string, unknown>, restPath, locale, fallback, opts)\n }),\n }\n }\n const dotIdx = path.indexOf('.')\n if (dotIdx !== -1) {\n const head = path.slice(0, dotIdx)\n const rest = path.slice(dotIdx + 1)\n const nested = obj[head]\n if (!nested || typeof nested !== 'object' || Array.isArray(nested)) return obj\n return {\n ...obj,\n [head]: applyAtPath(nested as Record<string, unknown>, rest, locale, fallback, opts),\n }\n }\n const raw = obj[path]\n if (raw === undefined || raw === null) return obj\n if (typeof raw !== 'object' || Array.isArray(raw)) return obj\n return {\n ...obj,\n [path]: resolveI18nText(raw as Record<string, string>, locale, fallback, path, opts),\n }\n}\n\n/**\n * Apply locale resolution to a single record, returning a new copy.\n *\n * For each field registered as an `i18nText` descriptor:\n * - If `locale === 'raw'`, the field value is left as the stored map.\n * - Otherwise, the field value is replaced with the resolved string.\n *\n * Field paths support dot notation (`'address.lineOne'`) and array\n * wildcards (`'contacts[].title'`). Top-level fields work as before.\n *\n * @param record The decrypted record.\n * @param i18nFields Map of field path → `I18nTextDescriptor`.\n * @param locale The requested locale (or `'raw'`).\n * @param fallback Fallback chain (optional).\n * @param layer Resolution layer (default `'read'`). Each field's\n * `onMissing` policy is resolved for this layer, so the\n * same record resolves leniently on a get but strictly\n * inside an mv/derivation.\n */\nexport function applyI18nLocale(\n record: Record<string, unknown>,\n i18nFields: Record<string, I18nTextDescriptor>,\n locale: string,\n fallback?: string | readonly string[],\n layer: Layer = 'read',\n): Record<string, unknown> {\n const fieldNames = Object.keys(i18nFields)\n if (fieldNames.length === 0) return record\n\n let result = record\n\n for (const [field, descriptor] of Object.entries(i18nFields)) {\n const { onMissing, substitute, smartSubstitute } = descriptor.options\n const opts: ResolveI18nOptions = {\n policy: resolvePolicy(onMissing, layer),\n ...(substitute !== undefined ? { substitute } : {}),\n ...(smartSubstitute ? { smartSubstitute } : {}),\n }\n result = applyAtPath(result, field, locale, fallback, opts)\n }\n\n // The internal densify provenance marker never leaves the store.\n result = stripI18nFilled(result)\n\n return result\n}\n\n/**\n * Sync i18n-text-only present-for-join dressing — the exact `join`-layer\n * partial application `kernel/query/join.ts` used to import and call\n * directly before #626's retirement (#650 Task 6): fixes `fallback`\n * `undefined` and `layer` `'join'`. `via/lookup/snapshot.ts`'s\n * `buildPresentForJoin` composes this with the lookup-label half to build\n * the `JoinableSource.presentForJoin` hook the join executor now calls\n * instead of reaching into this module.\n */\nexport function presentI18nForJoin(\n record: Record<string, unknown>,\n i18nFields: Record<string, I18nTextDescriptor>,\n locale: string,\n): Record<string, unknown> {\n return applyI18nLocale(record, i18nFields, locale, undefined, 'join')\n}\n\n/**\n * Remove the internal densify provenance marker (`_i18nFilled`) from a\n * read-facing record. NON-mutating: returns the same object when the\n * marker is absent, otherwise a shallow copy without the marker.\n *\n * MUST be applied on every user-facing read return — even locale-less ones,\n * which bypass {@link applyI18nLocale}. MUST NOT be applied on the internal\n * prior-read path (decryptRecord / resolveDensifyPrior), where densify needs\n * the marker.\n */\nexport function stripI18nFilled<T extends Record<string, unknown>>(record: T): T {\n if (!Object.prototype.hasOwnProperty.call(record, '_i18nFilled')) return record\n const rest: T = { ...record }\n delete (rest as Record<string, unknown>)._i18nFilled\n return rest\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-pod/format.ts","../src/with-pod/bundle.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 passphrase.\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 { PublicEnvelope } from '../with-party/directory/public-envelope/types.js'\n\n/** Magic bytes 'NDB1' (ASCII), identifying a NOYDB bundle. */\nexport const NOYDB_BUNDLE_MAGIC = new Uint8Array([0x4e, 0x44, 0x42, 0x31])\n\n/** Total fixed prefix before the header JSON: 4+1+1+4 bytes. */\nexport const NOYDB_BUNDLE_PREFIX_BYTES = 10\n\n/** Current bundle format version. Bumped on layout changes. */\nexport const NOYDB_BUNDLE_FORMAT_VERSION = 1\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 * `validateBundleHeader` 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 public envelope (`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 envelope 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?: PublicEnvelope\n /**\n * Auto-unlock material indicator. When present, the bundle\n * body wraps the dump JSON in a structure carrying per-user\n * passphrases — 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\n/** @deprecated Use `NoydbPodHeader`. */\nexport type NoydbBundleHeader = NoydbPodHeader\n\n/**\n * Allowlist of header keys. Any key not in this set is forbidden\n * and causes `validateBundleHeader` 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])\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 validateBundleHeader(\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 (typeof h['formatVersion'] !== 'number' || h['formatVersion'] !== NOYDB_BUNDLE_FORMAT_VERSION) {\n throw new Error(\n `.noydb bundle header.formatVersion must be ${NOYDB_BUNDLE_FORMAT_VERSION}, ` +\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 // 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}\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 encodeBundleHeader(header: NoydbPodHeader): Uint8Array {\n validateBundleHeader(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 })\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 decodeBundleHeader(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 validateBundleHeader(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 hasNoydbBundleMagic(bytes: Uint8Array): boolean {\n if (bytes.length < NOYDB_BUNDLE_MAGIC.length) return false\n for (let i = 0; i < NOYDB_BUNDLE_MAGIC.length; i++) {\n if (bytes[i] !== NOYDB_BUNDLE_MAGIC[i]) return false\n }\n return true\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, passphrase)`. Splitting the layers\n * keeps the bundle module free of any crypto/passphrase\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_BUNDLE_FORMAT_VERSION,\n NOYDB_BUNDLE_MAGIC,\n NOYDB_BUNDLE_PREFIX_BYTES,\n decodeBundleHeader,\n encodeBundleHeader,\n hasNoydbBundleMagic,\n readUint32BE,\n writeUint32BE,\n type CompressionAlgo,\n type NoydbPodHeader,\n} from './format.js'\nimport { sha256Hex as sha256HexBytes } from '../kernel/enclave/index.js'\nimport { BundleIntegrityError, BundleSealMismatchError, ValidationError } from '../kernel/errors.js'\nimport type { Vault } from '../kernel/vault.js'\nimport type { BundleRecipient } from '../with-party/team/keyring.js'\nimport { pickLocale } from '../with-party/directory/public-envelope/storage.js'\nimport type { PublicEnvelope } from '../with-party/directory/public-envelope/types.js'\nimport type { SealingKeyProvider, RecipientSealer, RecipientHint } from '../with-party/team/managed-passphrase.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 = 'passphrase' | '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 passphrases.\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 passphrase. The recipient inherits the source keyring's\n * userId, role, and permissions. Mutually exclusive with `recipients`.\n */\n readonly exportPassphrase?: 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 * passphrase. DEKs are unwrapped from the source keyring once and\n * re-wrapped per recipient — record ciphertext is unchanged.\n *\n * Mutually exclusive with `exportPassphrase`. 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 BundleRecipient[]\n /**\n * Auto-unlock — unsealed per-user credentials.\n *\n * Generalises `autoPassphrases` to support any bundleable credential\n * kind (`passphrase` | `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`, `autoPassphrases`,\n * and `sealedPassphrases`.\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 `sealedPassphrases` to support any bundleable\n * credential kind (`passphrase` | `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`, `autoPassphrases`,\n * and `sealedPassphrases`.\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 passphrases.\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 `sealedPassphrases`.\n */\n readonly autoPassphrases?: {\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 passphrases sealed under a\n * {@link SealingKeyProvider} (self-target only).\n *\n * The hub seals each user's plaintext passphrase 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 `sealedPassphrases` — 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 `autoPassphrases`.\n */\n readonly sealedPassphrases?: {\n readonly mode: 'self-target'\n readonly provider: SealingKeyProvider\n readonly perUser: Record<string, string>\n }\n}\n\n/** @deprecated Use `WritePodOptions`. */\nexport type WriteNoydbBundleOptions = WritePodOptions\n\n/**\n * Result returned by `readPod`. The caller is expected to\n * pass `dumpJson` into `vault.load(json, passphrase)` 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 NoydbBundleReadResult {\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: 'passphrase', 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 `sealedPassphrases`).\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 `'passphrase'` 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: 'passphrase', 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 ReadNoydbBundleOptions {\n /**\n * Recipient-side sealing providers used to unseal entries from\n * `sealedPassphrases`. 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 * autoPassphrases, sealedPassphrases). 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 passphrase-only map) into\n * a `Record<string, AutoCredential>` by tagging each entry as\n * `kind: 'passphrase'`. Used by the normalizer to promote the deprecated\n * `autoPassphrases`/`sealedPassphrases` sugar.\n */\nfunction toAutoCredentials(m: Record<string, string>): Record<string, AutoCredential> {\n return Object.fromEntries(\n Object.entries(m).map(([u, value]) => [u, { kind: 'passphrase' 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.autoPassphrases,\n opts.sealedPassphrases,\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 + 'autoPassphrases / sealedPassphrases may be set.',\n )\n }\n if (opts.autoCredentials !== undefined) {\n return { mode: 'unsealed', perUser: opts.autoCredentials.perUser }\n }\n if (opts.autoPassphrases !== undefined) {\n return { mode: 'unsealed', perUser: toAutoCredentials(opts.autoPassphrases.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 // sealedPassphrases — only remaining option\n return {\n mode: 'sealed-self',\n provider: opts.sealedPassphrases!.provider,\n perUser: toAutoCredentials(opts.sealedPassphrases!.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 ∈ {passphrase, 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(['passphrase', '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 passphrase/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.autoPassphrases?.policy\n if (policy !== 'public-by-design') {\n throw new ValidationError(\n 'writePod: `autoCredentials` (or `autoPassphrases`) 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 `autoPassphrases.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.sealedPassphrases?.mode\n if (selfTargetMode !== 'self-target') {\n throw new ValidationError(\n `writePod: \\`sealedCredentials.mode\\` (or \\`sealedPassphrases.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 `sealedPassphrases.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 `sealedPassphrases.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 BundleIntegrityError(\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 BundleIntegrityError('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 BundleIntegrityError(\n 'autoUnlock body missing `_noydb_bundle_body: 1` discriminator',\n )\n }\n if (typeof obj['dump'] !== 'string') {\n throw new BundleIntegrityError('autoUnlock body must carry a string `dump` field')\n }\n const blob = obj['_autoUnlock']\n if (typeof blob !== 'object' || blob === null) {\n throw new BundleIntegrityError('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 BundleIntegrityError(\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 BundleIntegrityError(\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 BundleIntegrityError('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 BundleIntegrityError(\n 'extracted-partition body missing `_noydb_bundle_body: 1` discriminator',\n )\n }\n if (typeof obj['dump'] !== 'string') {\n throw new BundleIntegrityError('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 BundleIntegrityError('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 BundleIntegrityError('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: 'passphrase', 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: 'passphrase', 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 `BundleSealMismatchError`. 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 `'passphrase'`.\n */\nasync function resolveAutoUnlock(\n blob: AutoUnlockBody['_autoUnlock'],\n opts: ReadNoydbBundleOptions,\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 ?? 'passphrase', 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 ?? 'passphrase'\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 BundleSealMismatchError(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 BundleSealMismatchError(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 `exportPassphrase`\n * nor `recipients` is set — the source keyring is inherited as-is.\n *\n * The single-passphrase 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 passphrase\" 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.exportPassphrase === undefined && opts.recipients === undefined) {\n return dumpJson\n }\n\n const recipients: readonly BundleRecipient[] =\n opts.recipients ?? [\n {\n id: vault.userId,\n passphrase: opts.exportPassphrase 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 * `getBundleHandle`/`dump`/`load`/`getPublicEnvelope`); 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 )\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.getBundleHandle()` — 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 assembleBundleContainer(opts: {\n handle: string\n bodyJsonStr: string\n compression: WritePodOptions['compression']\n /** Header fields beyond the always-present four. */\n headerExtras?: Partial<Pick<NoydbPodHeader, 'publicEnvelope' | 'autoUnlock' | 'bundleKind' | 'transferSeal'>>\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_BUNDLE_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 }\n const headerBytes = encodeBundleHeader(header)\n\n const prefix = new Uint8Array(NOYDB_BUNDLE_PREFIX_BYTES)\n prefix.set(NOYDB_BUNDLE_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 if (opts.exportPassphrase !== undefined && opts.recipients !== undefined) {\n throw new Error(\n 'writePod: pass either exportPassphrase 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.getBundleHandle()\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 public envelope into the header\n // when one is persisted. `Vault.getPublicEnvelope` tolerates a\n // missing document and returns undefined, which we propagate as\n // \"no envelope in the header.\" Vaults without a\n // `_meta/public-envelope` document produce minimum-disclosure\n // headers exactly like before, preserving back-compat.\n const publicEnvelope = await vault.getPublicEnvelope()\n\n return assembleBundleContainer({\n handle,\n bodyJsonStr,\n compression: opts.compression,\n headerExtras: {\n ...(publicEnvelope !== undefined ? { publicEnvelope } : {}),\n ...(autoUnlockMode !== null ? { autoUnlock: autoUnlockMode } : {}),\n },\n })\n}\n\n/** @deprecated Use `writePod`. */\nexport const writeNoydbBundle = writePod\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 (!hasNoydbBundleMagic(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_BUNDLE_PREFIX_BYTES) {\n throw new Error(\n `Truncated .noydb bundle: file is only ${bytes.length} bytes, ` +\n `which is less than the ${NOYDB_BUNDLE_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_BUNDLE_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_BUNDLE_PREFIX_BYTES, bodyOffset)\n const header = decodeBundleHeader(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/** @deprecated Use `readPodHeader`. */\nexport const readNoydbBundleHeader = readPodHeader\n\n/**\n * Read just the bundle's public envelope (`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 envelope.\n *\n * Same security caveat as the on-vault read path — the public\n * envelope is **untrusted hint** in v1; the encrypted body remains\n * the source of truth for vault contents.\n */\nexport function readNoydbBundlePublicEnvelope(\n bytes: Uint8Array,\n opts: { readonly locale?: string } = {},\n): PublicEnvelope | 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 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 `BundleIntegrityError` 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 passphrase. The dump JSON\n * inside the body still contains encrypted records — restoring\n * the vault requires `vault.load(dumpJson, passphrase)`\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: ReadNoydbBundleOptions = {},\n): Promise<NoydbBundleReadResult> {\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 BundleIntegrityError(\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 BundleIntegrityError(\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 BundleIntegrityError(\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/** @deprecated Use `readPod`. */\nexport const readNoydbBundle = readPod\n"],"mappings":"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","names":["userCount","plaintextBytes","sha256Hex"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-formula/materialized-views/executor.ts"],"sourcesContent":["import type { Collection } from '../../kernel/collection.js'\nimport type { TxContext } from '../../with-commit/tx/transaction.js'\nimport type { EncryptedEnvelope } from '../../kernel/types.js'\nimport { MaterializedViewTooLargeError, LocaleNotSpecifiedError } from '../../kernel/errors.js'\nimport type { MaterializedFromMeta, MVQueryContext, MaterializedViewStrategy } from './types.js'\nimport type { RegisteredMV } from './registry.js'\nimport { wrapDbWithPredicates } from './registry.js'\nimport { groupAndReduce } from '../../with-lookup/aggregate/groupby.js'\nimport { canonicalGroupKey } from '../../with-lookup/aggregate/canonical-key.js'\nimport { applyI18nLocale, type I18nTextDescriptor } from '../../via/i18n/core.js'\nimport { putDerivedOutput, type PutDerivedOutputCtx } from '../../kernel/via/dispatch.js'\n\n/**\n * Accessor shape passed in from the owning Vault. Mirrors v1's\n * `DerivationStaleAccessor` — provides the per-collection resolver\n * and the active TxContext so refresh writes/tombstones register on\n * `_executed` for rollback symmetry.\n */\nexport interface MVExecutorAccessor {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n getCollection(name: string): Collection<any>\n getActiveTxContext(): TxContext | null\n /**\n * Vault-shaped accessor passed to the MV's `query()` callback at\n * each refresh. Same instance the registry used at registration\n * time; threading through the executor lets the refresh path\n * re-evaluate the closure against the live vault state.\n */\n getQueryContext(): MVQueryContext\n /**\n * #638 Task 5 — ctx for `putDerivedOutput`'s frozen-period skip+audit. #641: the lazy\n * resolve-on-read caller (`stale.ts#resolveStaleMVOnRead`) now supplies one too — built at\n * the `Collection.get()`/`list()` entry point with a `'resolve-on-read'` sentinel id (no\n * real \"reacting write\" for a read-triggered materialize, mirroring `refreshView()`'s\n * `'refreshView'` sentinel). Still declared optional here since `MVExecutorAccessor` is a\n * general shape and a future caller could reasonably omit it.\n */\n dispatchCtx?: PutDerivedOutputCtx\n}\n\nexport interface RefreshResult {\n /** Rows newly written / overwritten. */\n written: number\n /** Rows tombstoned via `_internalDelete` (only when `onEmpty: 'delete'`). */\n deleted: number\n /** Failed row writes (non-strict mode). */\n failed: number\n /** #782/#785 — `outputCollection:id` entries from the tombstone pass whose ownership stamp\n * couldn't be decoded under the collection's default DEK (undecodable, mirroring\n * `invalidateMVAtRest`'s #776 posture) — ownership UNCONFIRMED. Only populated when\n * `onEmpty: 'delete'`. */\n residueUndecodable: string[]\n /** #782/#785 — `outputCollection:id` entries that decoded, stamp-matched this MV, but\n * `_internalDelete` declined (the #718 tier-elevation gate) — ownership CONFIRMED, a real\n * silent survival, surfaced rather than dropped. Only populated when `onEmpty: 'delete'`. */\n residueDeclined: string[]\n}\n\n/** Default cost ceiling — overridable per-MV via `spec.maxRows`. */\nconst DEFAULT_MAX_ROWS = 100_000\n\n/**\n * Materialize a query terminal that may be a `Query<T>` (call\n * `.toArray()`), an `Aggregation<R>` (call `.run()` returning a\n * single object — wrap as a one-row array), or a `GroupedAggregation<R>`\n * (call `.run()` returning an array of grouped rows). Branches on\n * available terminal at runtime — no type-discrimination at registration.\n */\nasync function materializeQueryResult(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n q: any,\n mvName: string,\n i18nLocale?: string,\n i18nFields?: Record<string, I18nTextDescriptor>,\n): Promise<ReadonlyArray<Record<string, unknown>>> {\n if (typeof q?.toArray === 'function') {\n // Query<T> — non-aggregate path. `.toArray()` returns Promise<T[]>.\n return await q.toArray()\n }\n if (typeof q?.run === 'function') {\n // Aggregation<R> or GroupedAggregation<R>. `.run()` is synchronous\n // and returns either a single object (Aggregation) or an array of\n // rows (GroupedAggregation). Promise.resolve() normalizes both\n // sync and async (future) variants.\n // Query-form MV grouping: when the MV declares i18nLocale, pass it +\n // i18nFields so a GroupedAggregation resolves i18n group keys before\n // bucketing (the Aggregation path ignores the extra arg).\n const runOpts = i18nLocale !== undefined ? { locale: i18nLocale, i18nFields } : undefined\n const result: unknown = await Promise.resolve(q.run(runOpts))\n if (Array.isArray(result)) {\n return result as ReadonlyArray<Record<string, unknown>>\n }\n // Single-aggregate result — wrap as one-row array. The consumer's\n // `rowKey()` should return a stable identity (often a literal\n // constant like `'total'`) since there's only one row.\n return [result as Record<string, unknown>]\n }\n throw new Error(\n `MV \"${mvName}\": query() must return a Query<T>, Aggregation, or GroupedAggregation. ` +\n `Got something without a .toArray() or .run() terminal.`,\n )\n}\n\n/**\n * Materialize a UNION-form MV: read every arm's source\n * collection, apply each arm's `map` to project rows into the unified\n * MV row shape, concatenate the mapped streams, then optionally run\n * `groupBy` + `aggregate` over the result.\n *\n * Modes (driven by `spec.groupBy` / `spec.aggregate`):\n *\n * - No `groupBy` → return the concatenated mapped rows unchanged.\n * - `groupBy` without `aggregate` → dedupe by composite group key,\n * keep the first row seen per key (later arms don't overwrite\n * earlier arms — Map insertion order rules).\n * - `groupBy` + `aggregate` → delegate to the shared `groupAndReduce`\n * pipeline used by `Query.groupBy().aggregate()`.\n *\n * Per-arm `map` is the schema-unification boundary; the strategy's\n * `TRow` type parameter enforces that every arm projects into the\n * same shape at compile time.\n *\n * @internal\n */\nasync function materializeUnionResult<TRow extends Record<string, unknown>>(\n spec: MaterializedViewStrategy<TRow>,\n db: MVQueryContext,\n): Promise<ReadonlyArray<Record<string, unknown>>> {\n const unified: TRow[] = []\n for (const arm of spec.unionSources!) {\n const coll = db.collection<Record<string, unknown>>(arm.collection)\n // Optional per-arm FK joins: chain `.join(field, { as, ... })` for\n // each declared leg before terminating. The aliased right-side\n // record lands at `sourceRow[leg.as]`, where the arm's `map` reads\n // it. Cast to `any` because the chained join widens the row type but\n // the executor treats every row as `Record<string, unknown>` — same\n // pattern the query-form join path uses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let q: any = coll.query()\n if (arm.join?.length) {\n for (const leg of arm.join) {\n q = q.join(leg.field, { as: leg.as, maxRows: leg.maxRows, strategy: leg.strategy })\n }\n }\n const sourceRows = q.toArray() as ReadonlyArray<Record<string, unknown>>\n for (const r of sourceRows) {\n const mapped = arm.map(r)\n // null / undefined means \"omit this source row\" — skip without\n // pushing so groupBy/aggregate never see a null entry.\n if (mapped == null) continue\n unified.push(mapped)\n }\n }\n\n if (!spec.groupBy) return unified\n\n const groupFields: readonly string[] =\n typeof spec.groupBy === 'string' ? [spec.groupBy] : spec.groupBy\n\n // i18n-aware group keys. An `i18nText` group field carries a raw\n // `{ locale: string }` map, an unstable object key. When `i18nLocale` is\n // declared (with `i18nFields` describing those fields), resolve the declared\n // group-key i18n fields to it at the `mv` layer FIRST — the same unified-rows\n // boundary where money is threaded — so buckets are stable strings and the\n // `mv`-layer `onMissing` policy fires here.\n if (spec.i18nLocale !== undefined && spec.i18nFields !== undefined) {\n const groupI18n: Record<string, I18nTextDescriptor> = {}\n for (const f of groupFields) {\n const d = spec.i18nFields[f]\n if (d !== undefined) groupI18n[f] = d\n }\n if (Object.keys(groupI18n).length > 0) {\n for (let i = 0; i < unified.length; i++) {\n unified[i] = applyI18nLocale(unified[i] as Record<string, unknown>, groupI18n, spec.i18nLocale, undefined, 'mv') as TRow\n }\n }\n }\n // Guard (always): a remaining object-valued group key — an undeclared i18n\n // field or a locale-less MV — would bucket on a map. Refuse, don't bucket wrong.\n for (const f of groupFields) {\n for (const row of unified) {\n const v = (row as Record<string, unknown>)[f]\n if (v !== null && typeof v === 'object') {\n throw new LocaleNotSpecifiedError(\n f,\n `Materialized view \"${spec.name}\" groups by \"${f}\", whose value is a raw i18n locale map — ` +\n `an unstable object group key. Declare { i18nLocale, i18nFields } on the MV to resolve it at ` +\n `the 'mv' layer, or group by a dictKey/staticDict code (the stable key) and resolve the label at read time.`,\n )\n }\n }\n }\n\n // groupBy without aggregate — dedupe by composite key, keep first\n // seen row per key. Useful for cross-arm uniqueness (e.g. unify two\n // sibling collections, keeping one row per natural key).\n if (!spec.aggregate) {\n const seen = new Map<string, TRow>()\n for (const row of unified) {\n const k = canonicalGroupKey(groupFields, row as Record<string, unknown>)\n if (!seen.has(k)) seen.set(k, row)\n }\n return [...seen.values()]\n }\n\n // groupBy + aggregate — delegate to the shared pipeline used by\n // `Query.groupBy().aggregate()`. Result rows carry each grouped\n // field in declaration order followed by the spec's reducer outputs.\n return groupAndReduce<Record<string, unknown>>(unified, groupFields, spec.aggregate, spec.moneyFields)\n}\n\n/**\n * Run an MV's `query()` and write the result rows to the output\n * collection. Same-DEK encryption: routes through the standard\n * `Collection.put` pipeline, so the output collection's DEK is what\n * gets used (matches the v2 spec's \"same DEK as the left-most source\"\n * invariant — `Collection.put` looks up the DEK by collection name,\n * and the output collection IS the MV's owned collection).\n *\n * Stamps `_materializedFrom` onto every emitted row.\n *\n * **Tombstoning:** when `spec.onEmpty: 'delete'` (default), rows\n * that existed in a prior refresh but no longer appear in the new\n * materialized result are deleted via `Collection._internalDelete` —\n * the housekeeping bypass primitive prevents user\n * `onDelete` guards on the output collection from firing on these\n * system-internal deletes. `onEmpty: 'keep'` opts out (rows from\n * prior refreshes linger even when the new result lacks them).\n *\n * **Cost ceiling:** if the materialized row count exceeds\n * `spec.maxRows` (default 100k), throws `MaterializedViewTooLargeError`\n * before any writes hit the store — so strict-mode rollback is\n * clean.\n *\n * **Strict mode:** `spec.strict === true` re-throws on any\n * row-write failure; the active TxContext registration means the\n * source-write rolls back atomically via `revertExecuted`.\n *\n * @internal\n */\nexport const MaterializedViewExecutor = {\n async refresh(\n reg: RegisteredMV,\n accessor: MVExecutorAccessor,\n ): Promise<RefreshResult> {\n const spec = reg.spec\n const outputColl = accessor.getCollection(reg.outputCollection)\n const maxRows = spec.maxRows ?? DEFAULT_MAX_ROWS\n const onEmpty = spec.onEmpty ?? 'delete'\n const strict = spec.strict ?? false\n\n // 1. Materialize the query (branches on terminal shape). If the\n // MV declared predicates, wrap the query context the same way\n // the registry did at registration time so `.wherePredicate()`\n // calls resolve to the registered functions.\n const baseCtx = accessor.getQueryContext()\n const ctxForQuery: MVQueryContext = spec.predicates\n ? wrapDbWithPredicates(baseCtx, spec.predicates)\n : baseCtx\n // UNION-form strategies: read every arm, map to the unified\n // row shape, concatenate, then optionally groupBy + aggregate. The\n // single-source `query()` path is untouched.\n let rows: ReadonlyArray<Record<string, unknown>>\n if (spec.unionSources) {\n rows = await materializeUnionResult(spec, ctxForQuery)\n } else {\n const q = spec.query!(ctxForQuery)\n rows = await materializeQueryResult(q, spec.name, spec.i18nLocale, spec.i18nFields)\n }\n\n // #777 — exclude this MV's OWN previously-stamped output rows from the\n // input scan. A same-collection Query-form MV copies the whole source row\n // verbatim into its output, so a stale output row can itself satisfy the\n // MV's own input filter (when the filter is on a field disjoint from the\n // partition field) and get re-selected as if it were live source — the\n // row self-perpetuates after its true source is gone. Prior output is\n // never this MV's own source.\n rows = rows.filter((row) => {\n const stamp = row._materializedFrom as { mvName?: string } | undefined\n return stamp?.mvName !== spec.name\n })\n\n // 2. Cost ceiling check BEFORE any writes — keeps the rollback\n // clean if the source-write is wrapped in a transaction.\n if (rows.length > maxRows) {\n throw new MaterializedViewTooLargeError(spec.name, rows.length, maxRows)\n }\n\n const txCtx = accessor.getActiveTxContext()\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const adapter = (outputColl as any).adapter as {\n get(v: string, c: string, i: string): Promise<EncryptedEnvelope | null>\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const vaultName = (outputColl as any).vault as string\n\n // 3. Compute the post-refresh id set so we can diff against the\n // prior-emitted id set for tombstoning (when onEmpty === 'delete').\n const newIds = new Set<string>()\n const enrichedRows: Array<{ id: string; record: Record<string, unknown> }> = []\n for (const row of rows) {\n const id = spec.rowKey(row)\n newIds.add(id)\n const meta: MaterializedFromMeta = {\n mvName: spec.name,\n queryHash: reg.queryHash,\n sourceVersions: {},\n materializedAt: new Date().toISOString(),\n }\n enrichedRows.push({ id, record: { ...row, _materializedFrom: meta } })\n }\n\n // 4. Write the new rows.\n let written = 0\n let failed = 0\n for (const { id, record } of enrichedRows) {\n try {\n if (txCtx !== null) {\n const prior = await adapter.get(vaultName, reg.outputCollection, id)\n txCtx._executed.push({\n op: { type: 'put', vaultName, collectionName: reg.outputCollection, id },\n priorEnvelope: prior,\n })\n }\n if (accessor.dispatchCtx) {\n if (await putDerivedOutput(outputColl, id, record, accessor.dispatchCtx) === 'written') written++\n } else {\n await outputColl.put(id, record)\n written++\n }\n } catch (err) {\n failed++\n if (strict) throw err\n \n console.warn(`[mv] \"${spec.name}\" row write failed:`, err)\n }\n }\n\n // 5. Tombstone rows that existed before but don't appear now.\n // `onEmpty: 'keep'` skips this pass entirely. Uses\n // `_internalDelete` so a user-registered `onDelete` on the\n // output collection does NOT fire on housekeeping (composition fix).\n let deleted = 0\n const residueUndecodable: string[] = []\n const residueDeclined: string[] = []\n if (onEmpty === 'delete') {\n const priorIds = await listOutputIds(outputColl)\n for (const priorId of priorIds) {\n if (newIds.has(priorId)) continue\n // #762 — a same-collection partition MV (`output: { collection: <source>, partition }`,\n // the DERIV-PP30-001 shape) writes INTO its own source collection, so `listOutputIds`\n // also returns untouched USER source rows here. Decode each candidate and only\n // tombstone rows THIS MV stamped via `_materializedFrom.mvName` — the exact discipline\n // `invalidateMVAtRest` uses (stale.ts:151-162). An unstamped row, or one stamped by a\n // different MV, is never this MV's to delete.\n const priorEnvelope = await adapter.get(vaultName, reg.outputCollection, priorId)\n if (priorEnvelope === null) continue\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const priorDecoded = await (outputColl as any)._decodeEnvelope(priorEnvelope, priorId)\n if (priorDecoded === null) {\n // #782 part a — ownership unknown (undecodable, e.g. elevated above tier 0 on a\n // tiered output collection): can't rule out this being THIS MV's own stamped row.\n // Surface it rather than silently skip (mirrors invalidateMVAtRest's #776 posture —\n // previously this leg had NO residue channel at all).\n residueUndecodable.push(`${reg.outputCollection}:${priorId}`)\n continue\n }\n const priorStampedBy =\n typeof priorDecoded === 'object'\n ? (priorDecoded as Record<string, unknown>)._materializedFrom as { mvName?: string } | undefined\n : undefined\n if (priorStampedBy?.mvName !== spec.name) continue\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const outAny = outputColl as any\n if (typeof outAny._internalDelete === 'function') {\n // #776 part b — gate on the boolean, matching invalidateMVAtRest's discipline\n // (stale.ts): `_internalDelete` returns `false` for a #718 elevated-skip (no\n // erasure happened), and that must not inflate the tombstone count.\n if (await outAny._internalDelete(priorId, txCtx)) {\n deleted++\n } else {\n // #782 part b — decoded AND stamp-owned, but erasure was declined (#718\n // tier-elevation gate). Ownership IS confirmed here — a real silent survival,\n // not a legit stamp-mismatch skip. Surface it too.\n residueDeclined.push(`${reg.outputCollection}:${priorId}`)\n }\n } else {\n // Defensive fallback — should never hit in real flow since\n // every Collection has `_internalDelete`.\n await outputColl.delete(priorId)\n deleted++\n }\n } catch (err) {\n failed++\n if (strict) throw err\n\n console.warn(`[mv] \"${spec.name}\" tombstone failed for id=\"${priorId}\":`, err)\n }\n }\n }\n\n return { written, deleted, failed, residueUndecodable, residueDeclined }\n },\n}\n\n/**\n * List ids currently present in the MV's output collection via the\n * adapter directly (avoids triggering the lazy resolve-on-read path\n * we're INSIDE). Returns an empty array if the collection doesn't\n * exist or the adapter doesn't surface a list method.\n *\n * @internal\n */\nasync function listOutputIds(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n outputColl: Collection<any>,\n): Promise<string[]> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const cAny = outputColl as any\n const adapter = cAny.adapter as { list?: (v: string, c: string) => Promise<readonly string[]> }\n const vault = cAny.vault as string\n const name = cAny.name as string\n if (typeof adapter?.list !== 'function') return []\n try {\n const ids = await adapter.list(vault, name)\n return [...ids]\n } catch {\n return []\n }\n}\n"],"mappings":"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","names":[]}