@noy-db/hub 0.8.0-pre.0 → 0.8.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 (624) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/api-OS747DS4.js +18 -0
  3. package/dist/as/index.js +11 -12
  4. package/dist/as/index.js.map +1 -1
  5. package/dist/at/index.js +9 -10
  6. package/dist/attestation/index.js +39 -28
  7. package/dist/attestation/index.js.map +1 -1
  8. package/dist/{backup-7TFRVFLH.js → backup-YE2VQ26S.js} +15 -16
  9. package/dist/backup-YE2VQ26S.js.map +1 -0
  10. package/dist/blobs/index.js +13 -14
  11. package/dist/blobs/index.js.map +1 -1
  12. package/dist/broker/index.js +12 -13
  13. package/dist/broker/index.js.map +1 -1
  14. package/dist/by/index.js +2 -2
  15. package/dist/capsule/contract.d.ts +159 -0
  16. package/dist/capsule/contract.js +95 -0
  17. package/dist/capsule/enclave-aes/bound.d.ts +52 -0
  18. package/dist/capsule/enclave-aes/capabilities.d.ts +2 -0
  19. package/dist/capsule/enclave-aes/classify/compare.d.ts +20 -0
  20. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/find.d.ts +1 -1
  21. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/reveal.d.ts +1 -1
  22. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/verify.d.ts +2 -2
  23. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/write.d.ts +1 -1
  24. package/dist/{kernel/enclave → capsule/enclave-aes}/crypto.d.ts +33 -129
  25. package/dist/capsule/enclave-aes/index.d.ts +113 -0
  26. package/dist/capsule/enclave-aes/index.js +335 -0
  27. package/dist/capsule/enclave-aes/index.js.map +1 -0
  28. package/dist/capsule/enclave-aes/primitives.d.ts +29 -0
  29. package/dist/capsule/index.d.ts +27 -0
  30. package/dist/capsule/plumbing/ct-equal.d.ts +11 -0
  31. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/deterministic.d.ts +8 -21
  32. package/dist/capsule/plumbing/digest.d.ts +122 -0
  33. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/envelope-body.d.ts +44 -48
  34. package/dist/capsule/plumbing/index.d.ts +100 -0
  35. package/dist/capsule/plumbing/lifecycle.d.ts +50 -0
  36. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/record-codec.d.ts +55 -20
  37. package/dist/{kernel/enclave → capsule/plumbing}/record-envelope.d.ts +1 -1
  38. package/dist/capsule/plumbing/rekey-blob.d.ts +33 -0
  39. package/dist/capsule/plumbing/rekey.d.ts +44 -0
  40. package/dist/capsule/plumbing/sealed-slot.d.ts +22 -0
  41. package/dist/capsule/plumbing/sealed-slots.d.ts +37 -0
  42. package/dist/capsule/plumbing/sealing.d.ts +29 -0
  43. package/dist/capsule/plumbing/sign.d.ts +9 -0
  44. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/tombstone.d.ts +2 -2
  45. package/dist/capsule-VOLX3QRQ.js +209 -0
  46. package/dist/cargo/index.js +36 -36
  47. package/dist/{chunk-DF73H4UN.js → chunk-23D6M6VM.js} +5 -5
  48. package/dist/chunk-23D6M6VM.js.map +1 -0
  49. package/dist/{chunk-FH2L2E34.js → chunk-244QBK7Z.js} +4 -4
  50. package/dist/{chunk-FH2L2E34.js.map → chunk-244QBK7Z.js.map} +1 -1
  51. package/dist/{chunk-YSK2CU2Q.js → chunk-2AFX33YA.js} +2 -2
  52. package/dist/{chunk-TKUOEU2F.js → chunk-2GHDLTPE.js} +2 -2
  53. package/dist/{chunk-GYGLAVCX.js → chunk-2MG3SW7Q.js} +6 -2
  54. package/dist/chunk-2MG3SW7Q.js.map +1 -0
  55. package/dist/{chunk-6OWXYZK2.js → chunk-2ODVB6LZ.js} +2 -2
  56. package/dist/{chunk-X25NWUZB.js → chunk-2PVXF5CS.js} +3 -3
  57. package/dist/{chunk-IEX7NK2K.js → chunk-2RH777HH.js} +21 -20
  58. package/dist/{chunk-IEX7NK2K.js.map → chunk-2RH777HH.js.map} +1 -1
  59. package/dist/{chunk-244VXYPG.js → chunk-37EPTBBR.js} +14 -1
  60. package/dist/chunk-37EPTBBR.js.map +1 -0
  61. package/dist/{chunk-NRT6QZQB.js → chunk-3A5DUZ56.js} +6 -6
  62. package/dist/chunk-3A5DUZ56.js.map +1 -0
  63. package/dist/{chunk-ORDURZBU.js → chunk-3EWS67C7.js} +2 -2
  64. package/dist/{chunk-3H5KLDZN.js → chunk-3L2NBKDR.js} +4 -8
  65. package/dist/chunk-3L2NBKDR.js.map +1 -0
  66. package/dist/{chunk-ENAG3IY7.js → chunk-3XJQBBZD.js} +26 -28
  67. package/dist/chunk-3XJQBBZD.js.map +1 -0
  68. package/dist/{chunk-KJFW62P4.js → chunk-42ALPZ2B.js} +652 -662
  69. package/dist/chunk-42ALPZ2B.js.map +1 -0
  70. package/dist/{chunk-FXQBGFOX.js → chunk-4CIQDWRB.js} +8 -8
  71. package/dist/{chunk-HT2AHGQI.js → chunk-4JPMZTJY.js} +2 -2
  72. package/dist/{chunk-FQ5VRHIB.js → chunk-4T3SE27M.js} +5 -9
  73. package/dist/chunk-4T3SE27M.js.map +1 -0
  74. package/dist/{chunk-MKXXMNJH.js → chunk-4V3Q35WG.js} +2 -2
  75. package/dist/{chunk-A6IJU4TK.js → chunk-52MGURXO.js} +2 -2
  76. package/dist/{chunk-IUWCVHQ7.js → chunk-5A4345TG.js} +4 -4
  77. package/dist/{chunk-IUWCVHQ7.js.map → chunk-5A4345TG.js.map} +1 -1
  78. package/dist/{chunk-ISZQJBML.js → chunk-5FE75J4S.js} +6 -6
  79. package/dist/chunk-5FE75J4S.js.map +1 -0
  80. package/dist/chunk-5KC7QYEC.js +565 -0
  81. package/dist/chunk-5KC7QYEC.js.map +1 -0
  82. package/dist/{chunk-NNARWO72.js → chunk-5UMK7GRI.js} +13 -12
  83. package/dist/chunk-5UMK7GRI.js.map +1 -0
  84. package/dist/{chunk-HLEK5VAN.js → chunk-5WWDGFOS.js} +2 -2
  85. package/dist/{chunk-CKNGS5MH.js → chunk-6QVKPHBF.js} +3 -5
  86. package/dist/chunk-6QVKPHBF.js.map +1 -0
  87. package/dist/{chunk-PSIV7CJV.js → chunk-747G6CT3.js} +2 -2
  88. package/dist/chunk-747G6CT3.js.map +1 -0
  89. package/dist/{chunk-LGRLME4M.js → chunk-7R2Q7CPL.js} +16 -16
  90. package/dist/chunk-7R2Q7CPL.js.map +1 -0
  91. package/dist/{chunk-SOIQ4MM4.js → chunk-7XMHMVTS.js} +3 -3
  92. package/dist/{chunk-BUDAGWL5.js → chunk-A4XMIZT3.js} +6 -6
  93. package/dist/{chunk-BUDAGWL5.js.map → chunk-A4XMIZT3.js.map} +1 -1
  94. package/dist/{chunk-4MXI3ODD.js → chunk-AEQGFRU4.js} +3 -3
  95. package/dist/{chunk-7JE7BDYJ.js → chunk-AL6ML4LA.js} +5 -5
  96. package/dist/{chunk-ZUVBC32X.js → chunk-AUKKUPVB.js} +2 -2
  97. package/dist/chunk-BNWA3WG5.js +22 -0
  98. package/dist/chunk-BNWA3WG5.js.map +1 -0
  99. package/dist/{chunk-WY2XXTET.js → chunk-BPWYQ25R.js} +4 -4
  100. package/dist/{chunk-EOVMT6TB.js → chunk-BVCUTXE3.js} +2 -2
  101. package/dist/{chunk-UPD5NWI5.js → chunk-DHHGL42E.js} +13 -16
  102. package/dist/chunk-DHHGL42E.js.map +1 -0
  103. package/dist/{chunk-3JWHAR6E.js → chunk-DIZLQVAA.js} +2 -2
  104. package/dist/{chunk-QWFHIOD2.js → chunk-DLD5LGDS.js} +11 -12
  105. package/dist/chunk-DLD5LGDS.js.map +1 -0
  106. package/dist/{chunk-ZE4CHIED.js → chunk-DQESF6RP.js} +17 -28
  107. package/dist/chunk-DQESF6RP.js.map +1 -0
  108. package/dist/{chunk-IT22PWKV.js → chunk-E2N2T5ZO.js} +2 -2
  109. package/dist/{chunk-3ELRMVJK.js → chunk-EGVJ7TM4.js} +31 -34
  110. package/dist/chunk-EGVJ7TM4.js.map +1 -0
  111. package/dist/{chunk-WNJ4AVGE.js → chunk-EJLV62NI.js} +2 -2
  112. package/dist/{chunk-QBBUZ3I3.js → chunk-EYG2CY2X.js} +11 -11
  113. package/dist/{enclave-27SMF6GF.js → chunk-EZGV6F47.js} +83 -56
  114. package/dist/chunk-EZGV6F47.js.map +1 -0
  115. package/dist/{chunk-LQ4ZHOKP.js → chunk-EZOAKKTB.js} +36 -27
  116. package/dist/chunk-EZOAKKTB.js.map +1 -0
  117. package/dist/{chunk-BJ2IXQ5Q.js → chunk-FFJFE4T7.js} +2 -2
  118. package/dist/{chunk-JCN44NCY.js → chunk-FLPCETP6.js} +2 -2
  119. package/dist/{chunk-5O7D6ZTW.js → chunk-FOEDQATO.js} +2 -2
  120. package/dist/{chunk-VNNRUGMM.js → chunk-FVH5QYY2.js} +2 -2
  121. package/dist/{chunk-ANWPSTZU.js → chunk-G2MVBDFP.js} +23 -26
  122. package/dist/chunk-G2MVBDFP.js.map +1 -0
  123. package/dist/chunk-GMKGGY7V.js +234 -0
  124. package/dist/chunk-GMKGGY7V.js.map +1 -0
  125. package/dist/{chunk-EIPVYUEP.js → chunk-GQ4MF3CM.js} +3 -3
  126. package/dist/chunk-GQ4MF3CM.js.map +1 -0
  127. package/dist/{chunk-PEOZ34EM.js → chunk-GSAK56VF.js} +1 -1
  128. package/dist/chunk-GSAK56VF.js.map +1 -0
  129. package/dist/{chunk-QOOUUR4A.js → chunk-GUZET4SG.js} +10 -4
  130. package/dist/chunk-GUZET4SG.js.map +1 -0
  131. package/dist/{chunk-4AF3TUMP.js → chunk-GZT3NQRA.js} +2 -2
  132. package/dist/{chunk-N5DNADLQ.js → chunk-H23XHRTT.js} +19 -34
  133. package/dist/chunk-H23XHRTT.js.map +1 -0
  134. package/dist/{chunk-MVZ2TPVP.js → chunk-H427H63K.js} +2 -2
  135. package/dist/{chunk-6B3VNR4P.js → chunk-H7JBY4P7.js} +23 -32
  136. package/dist/chunk-H7JBY4P7.js.map +1 -0
  137. package/dist/{chunk-MLPHLJIQ.js → chunk-HS554XAF.js} +2 -2
  138. package/dist/{chunk-HK67PX4J.js → chunk-HW7CMKUG.js} +1 -1
  139. package/dist/chunk-HW7CMKUG.js.map +1 -0
  140. package/dist/{chunk-S57HTZ6X.js → chunk-IMQKWR46.js} +5 -7
  141. package/dist/chunk-IMQKWR46.js.map +1 -0
  142. package/dist/{chunk-FCKWQE2H.js → chunk-IUBMZPHM.js} +7 -7
  143. package/dist/chunk-IUBMZPHM.js.map +1 -0
  144. package/dist/{chunk-UD5EVGZ7.js → chunk-IUT3GWP3.js} +49 -13
  145. package/dist/chunk-IUT3GWP3.js.map +1 -0
  146. package/dist/{chunk-J6UOHJY5.js → chunk-JDEYF3PJ.js} +4 -8
  147. package/dist/{chunk-J6UOHJY5.js.map → chunk-JDEYF3PJ.js.map} +1 -1
  148. package/dist/{chunk-NWHBJQAL.js → chunk-JIBUPZHA.js} +4 -4
  149. package/dist/chunk-JIBUPZHA.js.map +1 -0
  150. package/dist/{chunk-TROR3ALR.js → chunk-JOSPH3E5.js} +2 -2
  151. package/dist/{chunk-F24ZZTHQ.js → chunk-JQYKPKG6.js} +4 -4
  152. package/dist/{chunk-GF5BZ7D4.js → chunk-JSMTXZR3.js} +8 -12
  153. package/dist/chunk-JSMTXZR3.js.map +1 -0
  154. package/dist/chunk-JWWYRR4Q.js +12 -0
  155. package/dist/chunk-JWWYRR4Q.js.map +1 -0
  156. package/dist/{chunk-HBPPGWUM.js → chunk-KZXDLBY6.js} +14 -9
  157. package/dist/chunk-KZXDLBY6.js.map +1 -0
  158. package/dist/{chunk-7VCXZDA4.js → chunk-LIOVX5TJ.js} +7 -11
  159. package/dist/chunk-LIOVX5TJ.js.map +1 -0
  160. package/dist/{chunk-QWGU66MP.js → chunk-LNSVGRHO.js} +5 -7
  161. package/dist/chunk-LNSVGRHO.js.map +1 -0
  162. package/dist/{chunk-MDWKM2XC.js → chunk-M5B2W6YY.js} +3 -3
  163. package/dist/{chunk-F2TDU7MQ.js → chunk-MBMTND6D.js} +2 -2
  164. package/dist/{chunk-K6W7BVZ6.js → chunk-MILI22XQ.js} +3 -3
  165. package/dist/{chunk-YNIUIERI.js → chunk-MXKI4ABF.js} +18 -59
  166. package/dist/chunk-MXKI4ABF.js.map +1 -0
  167. package/dist/{chunk-T2LBQ5DV.js → chunk-MXOQ5JCG.js} +252 -214
  168. package/dist/chunk-MXOQ5JCG.js.map +1 -0
  169. package/dist/{chunk-ZXABPVM6.js → chunk-N7EVQD5S.js} +9 -13
  170. package/dist/chunk-N7EVQD5S.js.map +1 -0
  171. package/dist/{chunk-CF5UU4S7.js → chunk-N7UDP5VU.js} +7 -7
  172. package/dist/{chunk-FQ54DWUQ.js → chunk-NBQ3KK6X.js} +4 -4
  173. package/dist/{chunk-4I522ERM.js → chunk-NCO25JZO.js} +4 -4
  174. package/dist/chunk-NH2UOV3B.js +18 -0
  175. package/dist/chunk-NH2UOV3B.js.map +1 -0
  176. package/dist/{chunk-SI3V7D4G.js → chunk-O2IMQSNA.js} +2 -2
  177. package/dist/{chunk-T52GX4SA.js → chunk-O46CHQT7.js} +4 -4
  178. package/dist/chunk-OIEO4WDL.js +27 -0
  179. package/dist/chunk-OIEO4WDL.js.map +1 -0
  180. package/dist/{chunk-GHLTDZRS.js → chunk-OKSTSITU.js} +25 -4
  181. package/dist/chunk-OKSTSITU.js.map +1 -0
  182. package/dist/{chunk-TOPYQ6GA.js → chunk-OYW3I5M4.js} +6 -6
  183. package/dist/{chunk-X4773RBR.js → chunk-PD64727P.js} +2 -2
  184. package/dist/chunk-PD64727P.js.map +1 -0
  185. package/dist/{chunk-BP6DARYA.js → chunk-PP3K67AJ.js} +4 -4
  186. package/dist/chunk-PP3K67AJ.js.map +1 -0
  187. package/dist/chunk-Q2XRFWOI.js +40 -0
  188. package/dist/chunk-Q2XRFWOI.js.map +1 -0
  189. package/dist/{chunk-AOWGXU6D.js → chunk-Q4UHJ5SN.js} +2 -2
  190. package/dist/{chunk-FW22ZCXT.js → chunk-QBLT6POL.js} +2 -2
  191. package/dist/chunk-QMWY2FDS.js +157 -0
  192. package/dist/chunk-QMWY2FDS.js.map +1 -0
  193. package/dist/{chunk-I3JDRCMW.js → chunk-RB4DOPGZ.js} +10 -10
  194. package/dist/{chunk-32YV3TJT.js → chunk-SMV2HRDU.js} +2 -2
  195. package/dist/{chunk-ODVPTDPK.js → chunk-SV2PM2OY.js} +1 -1
  196. package/dist/chunk-SV2PM2OY.js.map +1 -0
  197. package/dist/{chunk-Z5HTYMMQ.js → chunk-SYNY3VB5.js} +2 -2
  198. package/dist/{chunk-SYVZW77S.js → chunk-TEHHROZL.js} +6 -8
  199. package/dist/chunk-TEHHROZL.js.map +1 -0
  200. package/dist/{chunk-ZSX4S5XB.js → chunk-TKVUE4JF.js} +9 -9
  201. package/dist/{chunk-B57AFBY2.js → chunk-TZZBXPZK.js} +2 -2
  202. package/dist/{chunk-VNPEWFPD.js → chunk-U2FZNEWJ.js} +4 -4
  203. package/dist/chunk-U2FZNEWJ.js.map +1 -0
  204. package/dist/{chunk-5FDLC6G7.js → chunk-UA372JHH.js} +3 -3
  205. package/dist/{chunk-PDCG6PR5.js → chunk-UEFOQH74.js} +2 -2
  206. package/dist/chunk-UEFOQH74.js.map +1 -0
  207. package/dist/{chunk-HVSCEIYL.js → chunk-UIYESRD6.js} +9 -11
  208. package/dist/chunk-UIYESRD6.js.map +1 -0
  209. package/dist/{chunk-B6SJIBQK.js → chunk-V3RWOOSO.js} +4 -4
  210. package/dist/{chunk-HBRLNOHL.js → chunk-V7ADQF5O.js} +4 -4
  211. package/dist/{chunk-L7DOS3TE.js → chunk-VAORSCSU.js} +3 -3
  212. package/dist/chunk-VAORSCSU.js.map +1 -0
  213. package/dist/{chunk-M522UYZ2.js → chunk-VFHCKG5M.js} +6 -10
  214. package/dist/chunk-VFHCKG5M.js.map +1 -0
  215. package/dist/{chunk-DC5QVPV7.js → chunk-VHBKYN2J.js} +3 -3
  216. package/dist/chunk-VHBKYN2J.js.map +1 -0
  217. package/dist/{chunk-WX5ADGTZ.js → chunk-VKI2EB3N.js} +3 -3
  218. package/dist/chunk-VKI2EB3N.js.map +1 -0
  219. package/dist/{chunk-MTL7MXGR.js → chunk-VMV5SBE4.js} +16 -17
  220. package/dist/chunk-VMV5SBE4.js.map +1 -0
  221. package/dist/{chunk-F7XGHL5O.js → chunk-VQDZYB2B.js} +3 -3
  222. package/dist/{chunk-BK2HHQGN.js → chunk-VVOLDVIR.js} +2 -2
  223. package/dist/{chunk-6LMQ5R26.js → chunk-WPOYWFSE.js} +7 -7
  224. package/dist/{chunk-6LMQ5R26.js.map → chunk-WPOYWFSE.js.map} +1 -1
  225. package/dist/{chunk-3F7GLNAE.js → chunk-WXE4W2U6.js} +7 -11
  226. package/dist/chunk-WXE4W2U6.js.map +1 -0
  227. package/dist/{chunk-6K7DPM7D.js → chunk-XQRFSKYM.js} +2 -2
  228. package/dist/{chunk-M7NI3IWE.js → chunk-XSPFDUDG.js} +22 -50
  229. package/dist/chunk-XSPFDUDG.js.map +1 -0
  230. package/dist/{chunk-ID6Z6E66.js → chunk-Y2RKVFKR.js} +2 -2
  231. package/dist/{chunk-6LNL42F5.js → chunk-Y3LPTBG7.js} +12 -12
  232. package/dist/chunk-Y3LPTBG7.js.map +1 -0
  233. package/dist/{chunk-FN5EBVTQ.js → chunk-YRXFFWG4.js} +2 -2
  234. package/dist/classified/index.js +5 -5
  235. package/dist/{classified-marker-W3USLL5M.js → classified-marker-SIYKUU5L.js} +3 -3
  236. package/dist/classified-marker-SIYKUU5L.js.map +1 -0
  237. package/dist/collection-facade-CS7CA2XO.js +47 -0
  238. package/dist/{computed-TPFBX7U4.js → computed-FAD7SED4.js} +3 -3
  239. package/dist/consent/index.js +10 -11
  240. package/dist/consent/index.js.map +1 -1
  241. package/dist/cover/index.js +9 -10
  242. package/dist/custody/index.js +12 -13
  243. package/dist/{dead-filter-U2UYKSZL.js → dead-filter-4F3LWYKH.js} +11 -3
  244. package/dist/dead-filter-4F3LWYKH.js.map +1 -0
  245. package/dist/debug/index.js +11 -2
  246. package/dist/debug/index.js.map +1 -1
  247. package/dist/{delegation-S3AEPW2T.js → delegation-NANZDG4N.js} +13 -16
  248. package/dist/delegation-NANZDG4N.js.map +1 -0
  249. package/dist/derivations/index.js +23 -24
  250. package/dist/derive-XLOJVXPU.js +20 -0
  251. package/dist/directory/index.js +10 -11
  252. package/dist/{dispatch-3CQDJV5Q.js → dispatch-H4HV32AM.js} +5 -5
  253. package/dist/{dispatch-RH4LHKBH.js → dispatch-JDZNGEB3.js} +21 -22
  254. package/dist/dispatch-JDZNGEB3.js.map +1 -0
  255. package/dist/executor-FMPVN6DT.js +35 -0
  256. package/dist/executor-LBBBRTN5.js +8 -0
  257. package/dist/executor-OSRZST2U.js +8 -0
  258. package/dist/export-accessible-RAOJAAU5.js +22 -0
  259. package/dist/extract-partition-JD5CUSTG.js +37 -0
  260. package/dist/{fanout-sidecar-OT6RYLOL.js → fanout-sidecar-L7NHPIOC.js} +13 -15
  261. package/dist/fanout-sidecar-L7NHPIOC.js.map +1 -0
  262. package/dist/find-JRN4RNQ5.js +10 -0
  263. package/dist/forget/index.js +9 -10
  264. package/dist/forget/index.js.map +1 -1
  265. package/dist/guards/index.js +4 -4
  266. package/dist/history/index.js +13 -14
  267. package/dist/history/index.js.map +1 -1
  268. package/dist/i18n/index.js +14 -15
  269. package/dist/i18n/index.js.map +1 -1
  270. package/dist/index.d.ts +8 -8
  271. package/dist/index.js +183 -184
  272. package/dist/index.js.map +1 -1
  273. package/dist/indexing/index.js +5 -5
  274. package/dist/introspection/index.js +11 -12
  275. package/dist/issue-YIVBZUTH.js +19 -0
  276. package/dist/kernel/attestation-types.d.ts +62 -0
  277. package/dist/kernel/collection-config.d.ts +1 -48
  278. package/dist/kernel/collection.d.ts +1 -1
  279. package/dist/kernel/cutover-transform.d.ts +1 -1
  280. package/dist/kernel/errors.d.ts +30 -2
  281. package/dist/kernel/merge-authority.d.ts +1 -1
  282. package/dist/kernel/paths.d.ts +29 -5
  283. package/dist/kernel/prepared-write.d.ts +1 -1
  284. package/dist/kernel/secret-mode.d.ts +1 -1
  285. package/dist/kernel/types.d.ts +42 -5
  286. package/dist/kernel/vault.d.ts +2 -2
  287. package/dist/kernel/write-queue.d.ts +17 -0
  288. package/dist/{ledger-KZPCMVZY.js → ledger-5ZMGE44K.js} +11 -12
  289. package/dist/liberate-WZSZM2W6.js +24 -0
  290. package/dist/link-set-MFXC24VK.js +29 -0
  291. package/dist/{managed-secret-S3MQ6SH6.js → managed-secret-MFTFT22X.js} +10 -11
  292. package/dist/materialized-views/index.js +26 -27
  293. package/dist/{migrate-cek-V7GKJZ7O.js → migrate-cek-DZRFFOHW.js} +2 -2
  294. package/dist/money/index.js +5 -5
  295. package/dist/noydb-ITIGNKUF.js +96 -0
  296. package/dist/overlay-views/index.js +4 -4
  297. package/dist/periods/index.js +12 -13
  298. package/dist/pod/index.js +72 -72
  299. package/dist/{pod-handle-WGDFB4C2.js → pod-handle-A3LET7NO.js} +10 -11
  300. package/dist/pod-handle-A3LET7NO.js.map +1 -0
  301. package/dist/policy/index.js +10 -11
  302. package/dist/port/on/index.d.ts +1 -1
  303. package/dist/port/to/index.d.ts +1 -1
  304. package/dist/port/with/blob-strategy.d.ts +1 -1
  305. package/dist/port/with/classified-strategy.d.ts +1 -1
  306. package/dist/port/with/collection-options.d.ts +1 -1
  307. package/dist/port/with/collection-registries.d.ts +1 -1
  308. package/dist/port/with/lookup-strategy.d.ts +2 -2
  309. package/dist/portability/index.js +8 -8
  310. package/dist/{post-register-SLSXIPBV.js → post-register-7ND27CF2.js} +6 -6
  311. package/dist/post-register-7ND27CF2.js.map +1 -0
  312. package/dist/query/all/index.js +15 -15
  313. package/dist/query/index.js +7 -7
  314. package/dist/query/live/index.js +7 -7
  315. package/dist/query/reduce/index.js +10 -10
  316. package/dist/query/relate/index.js +9 -9
  317. package/dist/reduce/index.js +8 -8
  318. package/dist/register-DT5HZ2LZ.js +30 -0
  319. package/dist/registry-BHRZGZHF.js +8 -0
  320. package/dist/registry-ET6ZFFWA.js +34 -0
  321. package/dist/registry-HXFGLGTG.js +8 -0
  322. package/dist/registry-THCPDEOD.js +23 -0
  323. package/dist/request-withdrawal-AV2MJJKY.js +30 -0
  324. package/dist/{reveal-4BCKPEC2.js → reveal-B2TLP4JQ.js} +10 -9
  325. package/dist/reveal-B2TLP4JQ.js.map +1 -0
  326. package/dist/revoke-6DWIEOCV.js +24 -0
  327. package/dist/satellites/index.js +1 -1
  328. package/dist/schema-update/index.js +2 -2
  329. package/dist/sealed-record/index.js +16 -18
  330. package/dist/sealed-record/index.js.map +1 -1
  331. package/dist/search/index.js +4 -4
  332. package/dist/{seed-4JS2RJ4A.js → seed-XNSE4U3S.js} +15 -18
  333. package/dist/seed-XNSE4U3S.js.map +1 -0
  334. package/dist/sequence/index.js +9 -10
  335. package/dist/session/index.js +9 -10
  336. package/dist/session/index.js.map +1 -1
  337. package/dist/shadow/index.js +2 -2
  338. package/dist/signer-MHGW3OIL.js +23 -0
  339. package/dist/snapshots/index.js +11 -11
  340. package/dist/stale-6GCFMOOR.js +28 -0
  341. package/dist/storage-BYN5LO6Z.js +21 -0
  342. package/dist/store/index.js +1 -1
  343. package/dist/{store-coordination-provider-2O2VUHKW.js → store-coordination-provider-C4BPT5XF.js} +11 -12
  344. package/dist/store-coordination-provider-C4BPT5XF.js.map +1 -0
  345. package/dist/sync/index.js +10 -11
  346. package/dist/sync/index.js.map +1 -1
  347. package/dist/team/index.js +15 -16
  348. package/dist/tiers/index.js +11 -12
  349. package/dist/to/index.js +2 -2
  350. package/dist/transactions/index.js +3 -3
  351. package/dist/util/index.js +1 -1
  352. package/dist/vault-head/index.js +11 -14
  353. package/dist/vault-head/index.js.map +1 -1
  354. package/dist/{verify-RVT67IMH.js → verify-UKALW7C2.js} +14 -12
  355. package/dist/verify-UKALW7C2.js.map +1 -0
  356. package/dist/via/classified/binding.d.ts +2 -2
  357. package/dist/via/classified/config-drift.d.ts +2 -2
  358. package/dist/via/classified/errors.d.ts +1 -1
  359. package/dist/via/lookup/handle.d.ts +4 -4
  360. package/dist/walk-4VPK7AS4.js +20 -0
  361. package/dist/with-audit/attestation/index.d.ts +2 -1
  362. package/dist/with-audit/attestation/issue.d.ts +2 -2
  363. package/dist/with-audit/attestation/revoke.d.ts +2 -2
  364. package/dist/with-audit/attestation/scheme.d.ts +7 -0
  365. package/dist/with-audit/attestation/signer.d.ts +1 -1
  366. package/dist/with-audit/attestation/strategy.d.ts +2 -2
  367. package/dist/with-audit/attestation/types.d.ts +10 -0
  368. package/dist/with-audit/attestation/vault-facade.d.ts +2 -2
  369. package/dist/with-audit/attestation/verifier.d.ts +6 -0
  370. package/dist/with-audit/consent/consent.d.ts +1 -1
  371. package/dist/with-audit/consent/strategy.d.ts +1 -1
  372. package/dist/with-audit/forget/subject-index.d.ts +1 -1
  373. package/dist/with-audit/periods/vault-facade.d.ts +1 -1
  374. package/dist/with-audit/sealed-record/index.d.ts +2 -2
  375. package/dist/with-audit/sealed-record/strategy.d.ts +1 -1
  376. package/dist/with-audit/tiers/index.d.ts +2 -2
  377. package/dist/with-cargo/adopt-partition.d.ts +1 -1
  378. package/dist/with-cargo/extract-partition.d.ts +1 -1
  379. package/dist/with-cargo/floor.d.ts +1 -1
  380. package/dist/with-commit/history/history.d.ts +2 -2
  381. package/dist/with-commit/history/index.d.ts +1 -1
  382. package/dist/with-commit/history/ledger/store.d.ts +1 -1
  383. package/dist/with-commit/history/strategy.d.ts +1 -1
  384. package/dist/with-commit/history/time-machine.d.ts +1 -1
  385. package/dist/with-commit/numbering/index.d.ts +1 -1
  386. package/dist/with-commit/sequence/index.d.ts +1 -1
  387. package/dist/with-commit/vault-head/head.d.ts +1 -1
  388. package/dist/with-commit/vault-head/index.d.ts +1 -1
  389. package/dist/with-commit/vault-head/strategy.d.ts +1 -1
  390. package/dist/with-commit/vault-head/verify.d.ts +1 -1
  391. package/dist/with-formula/derivations/dispatch.d.ts +1 -1
  392. package/dist/with-formula/derivations/fanout-sidecar.d.ts +1 -1
  393. package/dist/with-lookup/indexing/collection-facade.d.ts +1 -1
  394. package/dist/with-lookup/search/collection-facade.d.ts +1 -1
  395. package/dist/with-party/broker/index.d.ts +3 -3
  396. package/dist/with-party/directory/user-envelope/storage.d.ts +1 -1
  397. package/dist/with-party/team/delegation.d.ts +1 -1
  398. package/dist/with-party/team/echo-secret.d.ts +2 -2
  399. package/dist/with-party/team/index.d.ts +1 -1
  400. package/dist/with-party/team/keyring.d.ts +1 -1
  401. package/dist/with-party/team/magic-link-grant.d.ts +1 -1
  402. package/dist/with-party/team/recovery.d.ts +1 -1
  403. package/dist/with-party/team/roster-tag.d.ts +1 -1
  404. package/dist/with-party/team/rotate-recover.d.ts +1 -1
  405. package/dist/with-party/team/wrapped-deks.d.ts +1 -1
  406. package/dist/with-pod/index.d.ts +1 -1
  407. package/dist/with-shape/blobs/blob-compaction.d.ts +1 -1
  408. package/dist/with-shape/blobs/blob-intent.d.ts +1 -1
  409. package/dist/with-shape/blobs/blob-set.d.ts +2 -2
  410. package/dist/with-shape/blobs/legacy-sweep.d.ts +1 -1
  411. package/dist/with-shape/introspection/describe.d.ts +1 -1
  412. package/dist/with-shape/introspection/walk.d.ts +1 -1
  413. package/dist/with-shape/links/lazy-handle.d.ts +1 -1
  414. package/dist/with-shape/links/link-set.d.ts +1 -1
  415. package/dist/with-shape/manifest/derive.d.ts +1 -1
  416. package/dist/with-shape/manifest/storage.d.ts +1 -1
  417. package/dist/with-shape/persisted-schemas/register.d.ts +1 -1
  418. package/dist/with-shape/persisted-schemas/storage.d.ts +1 -1
  419. package/dist/with-shape/satellites/dead-filter.d.ts +1 -1
  420. package/dist/with-shape/satellites/declare.d.ts +1 -1
  421. package/dist/with-shape/satellites/marker.d.ts +1 -1
  422. package/dist/with-shape/satellites/post-register.d.ts +1 -1
  423. package/dist/with-sync/presence.d.ts +5 -10
  424. package/dist/withdraw-accessible-HL7GACUE.js +27 -0
  425. package/dist/withdraw-accessible-HL7GACUE.js.map +1 -0
  426. package/package.json +14 -5
  427. package/dist/api-GVYEKSJE.js +0 -19
  428. package/dist/backup-7TFRVFLH.js.map +0 -1
  429. package/dist/chunk-244VXYPG.js.map +0 -1
  430. package/dist/chunk-3ELRMVJK.js.map +0 -1
  431. package/dist/chunk-3F7GLNAE.js.map +0 -1
  432. package/dist/chunk-3H5KLDZN.js.map +0 -1
  433. package/dist/chunk-6B3VNR4P.js.map +0 -1
  434. package/dist/chunk-6LNL42F5.js.map +0 -1
  435. package/dist/chunk-6QSQQLSE.js +0 -95
  436. package/dist/chunk-6QSQQLSE.js.map +0 -1
  437. package/dist/chunk-7VCXZDA4.js.map +0 -1
  438. package/dist/chunk-ANWPSTZU.js.map +0 -1
  439. package/dist/chunk-BP6DARYA.js.map +0 -1
  440. package/dist/chunk-CKNGS5MH.js.map +0 -1
  441. package/dist/chunk-DC5QVPV7.js.map +0 -1
  442. package/dist/chunk-DE4DH2RZ.js +0 -29
  443. package/dist/chunk-DE4DH2RZ.js.map +0 -1
  444. package/dist/chunk-DF73H4UN.js.map +0 -1
  445. package/dist/chunk-EIPVYUEP.js.map +0 -1
  446. package/dist/chunk-ENAG3IY7.js.map +0 -1
  447. package/dist/chunk-FCKWQE2H.js.map +0 -1
  448. package/dist/chunk-FQ5VRHIB.js.map +0 -1
  449. package/dist/chunk-GF5BZ7D4.js.map +0 -1
  450. package/dist/chunk-GHLTDZRS.js.map +0 -1
  451. package/dist/chunk-GYGLAVCX.js.map +0 -1
  452. package/dist/chunk-HBPPGWUM.js.map +0 -1
  453. package/dist/chunk-HK67PX4J.js.map +0 -1
  454. package/dist/chunk-HVSCEIYL.js.map +0 -1
  455. package/dist/chunk-ISZQJBML.js.map +0 -1
  456. package/dist/chunk-KJFW62P4.js.map +0 -1
  457. package/dist/chunk-L7DOS3TE.js.map +0 -1
  458. package/dist/chunk-LGRLME4M.js.map +0 -1
  459. package/dist/chunk-LP7BEXCT.js +0 -32
  460. package/dist/chunk-LP7BEXCT.js.map +0 -1
  461. package/dist/chunk-LQ4ZHOKP.js.map +0 -1
  462. package/dist/chunk-M522UYZ2.js.map +0 -1
  463. package/dist/chunk-M7NI3IWE.js.map +0 -1
  464. package/dist/chunk-MTL7MXGR.js.map +0 -1
  465. package/dist/chunk-N5DNADLQ.js.map +0 -1
  466. package/dist/chunk-NNARWO72.js.map +0 -1
  467. package/dist/chunk-NRT6QZQB.js.map +0 -1
  468. package/dist/chunk-NWHBJQAL.js.map +0 -1
  469. package/dist/chunk-ODVPTDPK.js.map +0 -1
  470. package/dist/chunk-PDCG6PR5.js.map +0 -1
  471. package/dist/chunk-PEOZ34EM.js.map +0 -1
  472. package/dist/chunk-PSIV7CJV.js.map +0 -1
  473. package/dist/chunk-QOOUUR4A.js.map +0 -1
  474. package/dist/chunk-QWFHIOD2.js.map +0 -1
  475. package/dist/chunk-QWGU66MP.js.map +0 -1
  476. package/dist/chunk-S57HTZ6X.js.map +0 -1
  477. package/dist/chunk-SEE332EV.js +0 -66
  478. package/dist/chunk-SEE332EV.js.map +0 -1
  479. package/dist/chunk-SYVZW77S.js.map +0 -1
  480. package/dist/chunk-T2LBQ5DV.js.map +0 -1
  481. package/dist/chunk-UD5EVGZ7.js.map +0 -1
  482. package/dist/chunk-UPD5NWI5.js.map +0 -1
  483. package/dist/chunk-UYB234ZR.js +0 -479
  484. package/dist/chunk-UYB234ZR.js.map +0 -1
  485. package/dist/chunk-VNPEWFPD.js.map +0 -1
  486. package/dist/chunk-WX5ADGTZ.js.map +0 -1
  487. package/dist/chunk-X4773RBR.js.map +0 -1
  488. package/dist/chunk-YNIUIERI.js.map +0 -1
  489. package/dist/chunk-ZE4CHIED.js.map +0 -1
  490. package/dist/chunk-ZXABPVM6.js.map +0 -1
  491. package/dist/classified-marker-W3USLL5M.js.map +0 -1
  492. package/dist/collection-facade-QU6IJBN3.js +0 -48
  493. package/dist/dead-filter-U2UYKSZL.js.map +0 -1
  494. package/dist/delegation-S3AEPW2T.js.map +0 -1
  495. package/dist/derive-OX7F6TGG.js +0 -21
  496. package/dist/dispatch-RH4LHKBH.js.map +0 -1
  497. package/dist/executor-B6SRO2QS.js +0 -36
  498. package/dist/executor-BI5CHWRY.js +0 -8
  499. package/dist/executor-RBKM5W5K.js +0 -8
  500. package/dist/export-accessible-VMA3YM5V.js +0 -22
  501. package/dist/extract-partition-NF7PXGPP.js +0 -37
  502. package/dist/fanout-sidecar-OT6RYLOL.js.map +0 -1
  503. package/dist/find-LZMP26KF.js +0 -10
  504. package/dist/issue-IHARXWW5.js +0 -18
  505. package/dist/kernel/enclave/classify/compare.d.ts +0 -10
  506. package/dist/kernel/enclave/index.d.ts +0 -87
  507. package/dist/kernel/enclave/record-keys/lifecycle.d.ts +0 -124
  508. package/dist/kernel/enclave/record-keys/rekey-blob.d.ts +0 -88
  509. package/dist/kernel/enclave/record-keys/rekey.d.ts +0 -91
  510. package/dist/kernel/enclave/record-keys/sealed-slot.d.ts +0 -14
  511. package/dist/kernel/enclave/record-keys/sealed-slots.d.ts +0 -123
  512. package/dist/kernel/enclave/record-keys/sealing.d.ts +0 -70
  513. package/dist/liberate-GRLCZMW4.js +0 -24
  514. package/dist/link-set-JCMJ7ULP.js +0 -30
  515. package/dist/noydb-SWQJ3MZS.js +0 -97
  516. package/dist/pod-handle-WGDFB4C2.js.map +0 -1
  517. package/dist/post-register-SLSXIPBV.js.map +0 -1
  518. package/dist/register-F3OYE7MJ.js +0 -31
  519. package/dist/registry-CILQFXFK.js +0 -8
  520. package/dist/registry-XNNYQVKV.js +0 -8
  521. package/dist/registry-YCDZV3VW.js +0 -35
  522. package/dist/registry-ZGYS33TE.js +0 -24
  523. package/dist/request-withdrawal-Z5NC6TXT.js +0 -30
  524. package/dist/reveal-4BCKPEC2.js.map +0 -1
  525. package/dist/revoke-ZL62WC2D.js +0 -23
  526. package/dist/seed-4JS2RJ4A.js.map +0 -1
  527. package/dist/signer-MBZZMXTC.js +0 -24
  528. package/dist/stale-TJ3DYZMQ.js +0 -29
  529. package/dist/storage-SVMKRGCW.js +0 -22
  530. package/dist/store-coordination-provider-2O2VUHKW.js.map +0 -1
  531. package/dist/verify-RVT67IMH.js.map +0 -1
  532. package/dist/walk-HJ42EISE.js +0 -21
  533. package/dist/withdraw-accessible-5NZUPAVA.js +0 -27
  534. /package/dist/{api-GVYEKSJE.js.map → api-OS747DS4.js.map} +0 -0
  535. /package/dist/{computed-TPFBX7U4.js.map → capsule/contract.js.map} +0 -0
  536. /package/dist/{kernel/enclave → capsule/enclave-aes}/broker/proof.d.ts +0 -0
  537. /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/bidx.d.ts +0 -0
  538. /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/digest.d.ts +0 -0
  539. /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/vdig.d.ts +0 -0
  540. /package/dist/{kernel/enclave/classify → capsule/plumbing}/kofn.d.ts +0 -0
  541. /package/dist/{kernel/enclave/classify → capsule/plumbing}/normalize.d.ts +0 -0
  542. /package/dist/{kernel/enclave → capsule/plumbing}/record-aad.d.ts +0 -0
  543. /package/dist/{collection-facade-QU6IJBN3.js.map → capsule-VOLX3QRQ.js.map} +0 -0
  544. /package/dist/{chunk-YSK2CU2Q.js.map → chunk-2AFX33YA.js.map} +0 -0
  545. /package/dist/{chunk-TKUOEU2F.js.map → chunk-2GHDLTPE.js.map} +0 -0
  546. /package/dist/{chunk-6OWXYZK2.js.map → chunk-2ODVB6LZ.js.map} +0 -0
  547. /package/dist/{chunk-X25NWUZB.js.map → chunk-2PVXF5CS.js.map} +0 -0
  548. /package/dist/{chunk-ORDURZBU.js.map → chunk-3EWS67C7.js.map} +0 -0
  549. /package/dist/{chunk-FXQBGFOX.js.map → chunk-4CIQDWRB.js.map} +0 -0
  550. /package/dist/{chunk-HT2AHGQI.js.map → chunk-4JPMZTJY.js.map} +0 -0
  551. /package/dist/{chunk-MKXXMNJH.js.map → chunk-4V3Q35WG.js.map} +0 -0
  552. /package/dist/{chunk-A6IJU4TK.js.map → chunk-52MGURXO.js.map} +0 -0
  553. /package/dist/{chunk-HLEK5VAN.js.map → chunk-5WWDGFOS.js.map} +0 -0
  554. /package/dist/{chunk-SOIQ4MM4.js.map → chunk-7XMHMVTS.js.map} +0 -0
  555. /package/dist/{chunk-4MXI3ODD.js.map → chunk-AEQGFRU4.js.map} +0 -0
  556. /package/dist/{chunk-7JE7BDYJ.js.map → chunk-AL6ML4LA.js.map} +0 -0
  557. /package/dist/{chunk-ZUVBC32X.js.map → chunk-AUKKUPVB.js.map} +0 -0
  558. /package/dist/{chunk-WY2XXTET.js.map → chunk-BPWYQ25R.js.map} +0 -0
  559. /package/dist/{chunk-EOVMT6TB.js.map → chunk-BVCUTXE3.js.map} +0 -0
  560. /package/dist/{chunk-3JWHAR6E.js.map → chunk-DIZLQVAA.js.map} +0 -0
  561. /package/dist/{chunk-IT22PWKV.js.map → chunk-E2N2T5ZO.js.map} +0 -0
  562. /package/dist/{chunk-WNJ4AVGE.js.map → chunk-EJLV62NI.js.map} +0 -0
  563. /package/dist/{chunk-QBBUZ3I3.js.map → chunk-EYG2CY2X.js.map} +0 -0
  564. /package/dist/{chunk-BJ2IXQ5Q.js.map → chunk-FFJFE4T7.js.map} +0 -0
  565. /package/dist/{chunk-JCN44NCY.js.map → chunk-FLPCETP6.js.map} +0 -0
  566. /package/dist/{chunk-5O7D6ZTW.js.map → chunk-FOEDQATO.js.map} +0 -0
  567. /package/dist/{chunk-VNNRUGMM.js.map → chunk-FVH5QYY2.js.map} +0 -0
  568. /package/dist/{chunk-4AF3TUMP.js.map → chunk-GZT3NQRA.js.map} +0 -0
  569. /package/dist/{chunk-MVZ2TPVP.js.map → chunk-H427H63K.js.map} +0 -0
  570. /package/dist/{chunk-MLPHLJIQ.js.map → chunk-HS554XAF.js.map} +0 -0
  571. /package/dist/{chunk-TROR3ALR.js.map → chunk-JOSPH3E5.js.map} +0 -0
  572. /package/dist/{chunk-F24ZZTHQ.js.map → chunk-JQYKPKG6.js.map} +0 -0
  573. /package/dist/{chunk-MDWKM2XC.js.map → chunk-M5B2W6YY.js.map} +0 -0
  574. /package/dist/{chunk-F2TDU7MQ.js.map → chunk-MBMTND6D.js.map} +0 -0
  575. /package/dist/{chunk-K6W7BVZ6.js.map → chunk-MILI22XQ.js.map} +0 -0
  576. /package/dist/{chunk-CF5UU4S7.js.map → chunk-N7UDP5VU.js.map} +0 -0
  577. /package/dist/{chunk-FQ54DWUQ.js.map → chunk-NBQ3KK6X.js.map} +0 -0
  578. /package/dist/{chunk-4I522ERM.js.map → chunk-NCO25JZO.js.map} +0 -0
  579. /package/dist/{chunk-SI3V7D4G.js.map → chunk-O2IMQSNA.js.map} +0 -0
  580. /package/dist/{chunk-T52GX4SA.js.map → chunk-O46CHQT7.js.map} +0 -0
  581. /package/dist/{chunk-TOPYQ6GA.js.map → chunk-OYW3I5M4.js.map} +0 -0
  582. /package/dist/{chunk-AOWGXU6D.js.map → chunk-Q4UHJ5SN.js.map} +0 -0
  583. /package/dist/{chunk-FW22ZCXT.js.map → chunk-QBLT6POL.js.map} +0 -0
  584. /package/dist/{chunk-I3JDRCMW.js.map → chunk-RB4DOPGZ.js.map} +0 -0
  585. /package/dist/{chunk-32YV3TJT.js.map → chunk-SMV2HRDU.js.map} +0 -0
  586. /package/dist/{chunk-Z5HTYMMQ.js.map → chunk-SYNY3VB5.js.map} +0 -0
  587. /package/dist/{chunk-ZSX4S5XB.js.map → chunk-TKVUE4JF.js.map} +0 -0
  588. /package/dist/{chunk-B57AFBY2.js.map → chunk-TZZBXPZK.js.map} +0 -0
  589. /package/dist/{chunk-5FDLC6G7.js.map → chunk-UA372JHH.js.map} +0 -0
  590. /package/dist/{chunk-B6SJIBQK.js.map → chunk-V3RWOOSO.js.map} +0 -0
  591. /package/dist/{chunk-HBRLNOHL.js.map → chunk-V7ADQF5O.js.map} +0 -0
  592. /package/dist/{chunk-F7XGHL5O.js.map → chunk-VQDZYB2B.js.map} +0 -0
  593. /package/dist/{chunk-BK2HHQGN.js.map → chunk-VVOLDVIR.js.map} +0 -0
  594. /package/dist/{chunk-6K7DPM7D.js.map → chunk-XQRFSKYM.js.map} +0 -0
  595. /package/dist/{chunk-ID6Z6E66.js.map → chunk-Y2RKVFKR.js.map} +0 -0
  596. /package/dist/{chunk-FN5EBVTQ.js.map → chunk-YRXFFWG4.js.map} +0 -0
  597. /package/dist/{derive-OX7F6TGG.js.map → collection-facade-CS7CA2XO.js.map} +0 -0
  598. /package/dist/{enclave-27SMF6GF.js.map → computed-FAD7SED4.js.map} +0 -0
  599. /package/dist/{executor-B6SRO2QS.js.map → derive-XLOJVXPU.js.map} +0 -0
  600. /package/dist/{dispatch-3CQDJV5Q.js.map → dispatch-H4HV32AM.js.map} +0 -0
  601. /package/dist/{executor-BI5CHWRY.js.map → executor-FMPVN6DT.js.map} +0 -0
  602. /package/dist/{executor-RBKM5W5K.js.map → executor-LBBBRTN5.js.map} +0 -0
  603. /package/dist/{export-accessible-VMA3YM5V.js.map → executor-OSRZST2U.js.map} +0 -0
  604. /package/dist/{extract-partition-NF7PXGPP.js.map → export-accessible-RAOJAAU5.js.map} +0 -0
  605. /package/dist/{find-LZMP26KF.js.map → extract-partition-JD5CUSTG.js.map} +0 -0
  606. /package/dist/{issue-IHARXWW5.js.map → find-JRN4RNQ5.js.map} +0 -0
  607. /package/dist/{ledger-KZPCMVZY.js.map → issue-YIVBZUTH.js.map} +0 -0
  608. /package/dist/{liberate-GRLCZMW4.js.map → ledger-5ZMGE44K.js.map} +0 -0
  609. /package/dist/{link-set-JCMJ7ULP.js.map → liberate-WZSZM2W6.js.map} +0 -0
  610. /package/dist/{managed-secret-S3MQ6SH6.js.map → link-set-MFXC24VK.js.map} +0 -0
  611. /package/dist/{noydb-SWQJ3MZS.js.map → managed-secret-MFTFT22X.js.map} +0 -0
  612. /package/dist/{migrate-cek-V7GKJZ7O.js.map → migrate-cek-DZRFFOHW.js.map} +0 -0
  613. /package/dist/{register-F3OYE7MJ.js.map → noydb-ITIGNKUF.js.map} +0 -0
  614. /package/dist/{registry-CILQFXFK.js.map → register-DT5HZ2LZ.js.map} +0 -0
  615. /package/dist/{registry-XNNYQVKV.js.map → registry-BHRZGZHF.js.map} +0 -0
  616. /package/dist/{registry-YCDZV3VW.js.map → registry-ET6ZFFWA.js.map} +0 -0
  617. /package/dist/{registry-ZGYS33TE.js.map → registry-HXFGLGTG.js.map} +0 -0
  618. /package/dist/{request-withdrawal-Z5NC6TXT.js.map → registry-THCPDEOD.js.map} +0 -0
  619. /package/dist/{revoke-ZL62WC2D.js.map → request-withdrawal-AV2MJJKY.js.map} +0 -0
  620. /package/dist/{signer-MBZZMXTC.js.map → revoke-6DWIEOCV.js.map} +0 -0
  621. /package/dist/{stale-TJ3DYZMQ.js.map → signer-MHGW3OIL.js.map} +0 -0
  622. /package/dist/{storage-SVMKRGCW.js.map → stale-6GCFMOOR.js.map} +0 -0
  623. /package/dist/{walk-HJ42EISE.js.map → storage-BYN5LO6Z.js.map} +0 -0
  624. /package/dist/{withdraw-accessible-5NZUPAVA.js.map → walk-4VPK7AS4.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-formula/materialized-views/stale.ts"],"sourcesContent":["import type { Collection } from '../../kernel/collection.js'\nimport type { MaterializedViewExecutor as MVExecutorType } from './executor.js'\nimport type { TxContext } from '../../with-commit/tx/transaction.js'\nimport type { PutDerivedOutputCtx } from '../../kernel/via/dispatch.js'\nimport type { MaterializedViewRegistry, RegisteredMV } from './registry.js'\n// Type-only — runtime class loaded via dynamic import in\n// `resolveStaleMVOnRead` only when a stale flag actually fires.\n// Keeps the executor chunk out of the floor bundle (mirrors v1 floor-bundle isolation).\nimport type { MVQueryContext } from './types.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport { RecordCodec } from '../../capsule/index.js'\n\nimport { lazy } from '../../kernel/lazy.js'\n\n/** #846c — one memoized binding instead of a cast at the call site. */\nconst loadMVExecutor = lazy(() => import('./executor.js'))\n\n/**\n * Accessor shape passed in from the owning Vault. Provides the\n * registry (used as a stable WeakMap key + to look up MVs by output\n * collection) and the runtime context the lazy refresh needs.\n * Mirrors v1's `DerivationStaleAccessor`.\n */\nexport interface MVStaleAccessor {\n registry(): MaterializedViewRegistry\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n getCollection(name: string): Collection<any>\n getActiveTxContext(): TxContext | null\n getQueryContext(): MVQueryContext\n}\n\n/**\n * In-memory stale map keyed by `MaterializedViewRegistry` instance\n * (stable per vault). Each registry maps to a set of MV names that\n * have at least one pending source-change requiring a re-materialize.\n *\n * Persistence across vault close is NOT implemented in this iteration\n * (concern flagged in the v2 spec, mirrors v1 derivation behavior).\n * On vault re-open, the unset stale flag is interpreted as \"fresh\" —\n * `vault.refreshView(name)` is the explicit recompute escape hatch.\n *\n * @internal\n */\nconst _staleByRegistry = new WeakMap<MaterializedViewRegistry, Set<string>>()\n\n/**\n * Mark an MV as stale. Called from `Collection.dispatchMaterializedViews`\n * when a source-write fires for a `refresh: 'lazy'` MV.\n *\n * @internal\n */\nexport function markMVStale(registry: MaterializedViewRegistry, mvName: string): void {\n let set = _staleByRegistry.get(registry)\n if (!set) {\n set = new Set()\n _staleByRegistry.set(registry, set)\n }\n set.add(mvName)\n}\n\n/**\n * Test-only: check whether a given MV name is currently flagged stale\n * against a registry. Exported so the regression suite can pin the\n * stale-bit lifecycle without touching the internal `WeakMap`.\n *\n * @internal\n */\nexport function isMVStale(registry: MaterializedViewRegistry, mvName: string): boolean {\n return _staleByRegistry.get(registry)?.has(mvName) ?? false\n}\n\n/**\n * Reserved collection holding CONTENT-FREE lazy-stale markers (record id =\n * MV name, plaintext `_iv: ''` envelope, no payload) — mirrors the\n * `_meta`/schema-fence marker envelope shape. Written only from the\n * delete/forget/elevate dispatcher (#736); ordinary source writes stay\n * in-memory-only (the cheap path). Read via the adapter directly, never\n * through `vault.collection()` — same reserved-collection convention as\n * `_subject_index`/`_meta`.\n */\nexport const MV_STALE_COLLECTION = '_mv_stale'\n\n/**\n * Once-per-registry hydrate guard for `resolveStaleMVOnRead`'s persisted-marker fold-in.\n * Stores the in-flight/settled PROMISE (not a boolean) — a concurrent cold first read that\n * arrives while the hydrate is still awaiting `adapter.list` must await the SAME promise\n * rather than observing \"already hydrating\" and reading an empty pending set (#736\n * whole-branch review).\n */\nconst _hydratedByRegistry = new WeakMap<MaterializedViewRegistry, Promise<void>>()\n\n/** The adapter + vault name backing any collection in this vault (all collections share both). */\nfunction storeOf(accessor: MVStaleAccessor, collectionName: string): { adapter: NoydbStore; vault: string } {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const c = accessor.getCollection(collectionName) as any\n return { adapter: c.adapter as NoydbStore, vault: c.vault as string }\n}\n\nasync function writeMVStaleMarker(adapter: NoydbStore, vault: string, mvName: string): Promise<void> {\n // Content-free — the marker's only payload is its key (the MV name).\n const env = RecordCodec.buildPlaintextEnvelope({ collection: MV_STALE_COLLECTION, id: mvName, version: 1 }, { data: '{}' })\n await adapter.put(vault, MV_STALE_COLLECTION, mvName, env)\n}\n\nasync function deleteMVStaleMarker(adapter: NoydbStore, vault: string, mvName: string): Promise<void> {\n await adapter.delete(vault, MV_STALE_COLLECTION, mvName)\n}\n\n/**\n * On first `resolveStaleMVOnRead` call for a (cold-reopened) registry, fold every\n * persisted `_mv_stale` marker into the in-memory pending set — otherwise a cold\n * session's empty `WeakMap` entry reads as \"fresh\" and serves the emptied output\n * collection forever.\n */\nasync function hydratePersistedStaleMarkers(accessor: MVStaleAccessor, registry: MaterializedViewRegistry): Promise<void> {\n const mvs = registry.all()\n if (mvs.length === 0) return\n const { adapter, vault } = storeOf(accessor, mvs[0]!.outputCollection)\n // No try/catch: a genuine store failure here must surface, not be read as\n // \"nothing pending\" — silently treating it as fresh is the exact leak class\n // this module closes (mirrors `subject-index.ts`'s bare `adapter.list` reads).\n for (const name of await adapter.list(vault, MV_STALE_COLLECTION)) markMVStale(registry, name)\n}\n\n/**\n * Invalidate an MV from the delete/forget/elevate dispatcher (#736) — the ordinary\n * source-write path (`dispatchMaterializedViews`) never calls this. Deletes EVERY\n * persisted row in `reg.outputCollection` via `_internalDelete` (the at-rest law: a\n * stale mark alone leaves the elevated/forgotten source's plaintext sitting in the\n * output row) using the adapter directly so the read-path's `resolveStaleMVOnRead`\n * isn't triggered mid-invalidation. `mode: 'lazy'` also persists the stale mark so a\n * cold session recomputes on next read; `'manual'` gets the purge only — the MV\n * serves empty until an explicit `vault.refreshView()` (erasure wins). Deletion is\n * stamp-scoped (`_materializedFrom.mvName === reg.spec.name` — see the loop below),\n * so another registered MV sharing this output collection keeps its own rows intact:\n * no cross-MV re-marking is needed here, only `reg` itself is marked stale.\n *\n * Returns the count of rows actually `_internalDelete`d (#761 item 1) — folded by the\n * caller into `dispatchMaterializedViewsOnDelete`'s `deleted` so `ForgetResult.derivedRecordsErased`\n * counts lazy/manual purges too, not just the eager executor's tombstone leg — plus\n * `residueUndecodable`, the bare ids whose ownership stamp could NOT be decoded (#776): a\n * candidate row that fails to decode under the collection's default DEK (e.g. elevated above\n * tier 0 on a tiered output collection) has UNKNOWN ownership — it might be this MV's own\n * output, or (same-collection shape) a plain user record — so it is never erased, but it must\n * be SURFACED rather than silently skipped (the #724 posture), since it may still hold the\n * forgotten/pre-elevation contribution, decryptable by tier-holders — and `residueDeclined`\n * (#785), the bare ids that DID decode and stamp-match but whose `_internalDelete` declined\n * (ownership CONFIRMED, erasure declined).\n *\n * @internal\n */\nexport async function invalidateMVAtRest(\n accessor: MVStaleAccessor,\n reg: RegisteredMV,\n mode: 'lazy' | 'manual',\n): Promise<{ deleted: number; residueUndecodable: string[]; residueDeclined: string[] }> {\n const outputColl = accessor.getCollection(reg.outputCollection)\n const txCtx = accessor.getActiveTxContext()\n const { adapter, vault } = storeOf(accessor, reg.outputCollection)\n let deleted = 0\n const residueUndecodable: string[] = []\n const residueDeclined: string[] = []\n for (const id of await adapter.list(vault, reg.outputCollection)) {\n // A same-collection partition MV (`output: { collection: <source>, partition }`,\n // the DERIV-PP30-001 shape) writes INTO its own source collection — `adapter.list`\n // over `reg.outputCollection` then also returns untouched user source records.\n // Decode each candidate row (the invalidation path is rare; this cost is fine\n // here) and only erase rows THIS MV stamped via `_materializedFrom.mvName` —\n // an unstamped row, or one stamped by a different MV, is never this MV's to\n // delete (#736 whole-branch review, Critical).\n const envelope = await adapter.get(vault, reg.outputCollection, id)\n if (envelope === null) continue\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const decoded = await (outputColl as any)._decodeEnvelope(envelope, id)\n if (decoded === null) { residueUndecodable.push(id); continue } // #776 — ownership unknown, surfaced not skipped\n const stampedBy = (decoded as Record<string, unknown>)._materializedFrom as { mvName?: string } | undefined\n if (stampedBy?.mvName !== reg.spec.name) continue\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (await (outputColl as any)._internalDelete(id, txCtx)) {\n deleted++\n } else {\n // #782 part b — decoded AND stamp-owned, but erasure declined (#718 tier-elevation\n // gate). Ownership IS confirmed — a real silent survival, not a legit stamp-mismatch\n // skip. Surface it too, in its own channel (#785 — distinct from the decode-null case).\n residueDeclined.push(id)\n }\n }\n\n // `mode: 'manual'` gets the purge only (erasure wins, no auto-recompute promise);\n // `'lazy'` also marks + persists ITS OWN stale bit for cold-session recompute. No\n // sibling MV on the same output collection is touched here — the stamp-scoped\n // deletion above never erased sibling rows in the first place (#736 re-review).\n if (mode === 'lazy') {\n const registry = accessor.registry()\n markMVStale(registry, reg.spec.name)\n await writeMVStaleMarker(adapter, vault, reg.spec.name)\n }\n return { deleted, residueUndecodable, residueDeclined }\n}\n\n/**\n * Called from `Collection.get` (and any reader that materializes the\n * MV's output collection). If any MV producing `outputCollection` is\n * flagged stale, runs the executor against the live source state\n * before returning. No-op when there is no pending work — keeps the\n * read fast path negligible.\n *\n * `dispatchCtx` (#641): threaded from the calling `Collection`'s\n * `#dispatchCtx({ collection: outputCollection, id: 'resolve-on-read' })` — a read has no\n * real \"reacting write\" record, so `'resolve-on-read'` is the sentinel id, mirroring\n * `Vault.refreshView()`'s `'refreshView'` sentinel for the same reason. Passed straight\n * through to the executor so a stale row whose output lands in a closed period follows the\n * frozen-output rule (skip + `derivation:skipped-frozen`, #637) instead of throwing\n * `PeriodClosedError` out of a read.\n *\n * Dynamic-imports the executor only when a stale flag actually fires\n * (the floor-bundle isolation pattern v1 derivations established in\n * floor-bundle isolation pattern).\n */\nexport async function resolveStaleMVOnRead(\n accessor: MVStaleAccessor,\n outputCollection: string,\n dispatchCtx?: PutDerivedOutputCtx,\n): Promise<void> {\n const registry = accessor.registry()\n // Cold session: the in-memory WeakMap has no entry for this (freshly\n // constructed) registry yet — fold any persisted `_mv_stale` markers in\n // before reading the pending set, once per registry (#736). Memoize the\n // PROMISE, not a boolean: a concurrent first read arriving while the\n // hydrate is still awaiting `adapter.list` must await the SAME promise —\n // otherwise it observes \"already hydrating\", skips straight to an empty\n // pending set, and serves the purged/stale view as fresh (#736\n // whole-branch review, hydrate-once race). On rejection, evict the entry\n // BEFORE rethrowing — a cached rejected promise would otherwise poison the\n // MV read surface for the rest of the session (every later read re-awaits\n // the same rejection instead of retrying the store call) (#736 re-review).\n let hydrate = _hydratedByRegistry.get(registry)\n if (!hydrate) {\n hydrate = hydratePersistedStaleMarkers(accessor, registry).catch((err: unknown) => {\n _hydratedByRegistry.delete(registry)\n throw err\n })\n _hydratedByRegistry.set(registry, hydrate)\n }\n await hydrate\n const pending = _staleByRegistry.get(registry)\n if (!pending || pending.size === 0) return\n\n const { adapter, vault } = storeOf(accessor, outputCollection)\n\n // Clean up persisted markers for MV names no longer registered (renamed or\n // removed since the marker was written). Without this sweep an orphaned name\n // can never become a `candidate` below — `candidates` is built from\n // `registry.all()`, which no longer lists it — so both the in-memory pending\n // entry and the persisted `_mv_stale` row would linger forever, re-hydrating\n // on every cold session (#736 whole-branch review).\n for (const name of pending) {\n if (registry.byName(name)) continue\n pending.delete(name)\n await deleteMVStaleMarker(adapter, vault, name)\n }\n if (pending.size === 0) return\n\n // Find every MV that writes to this output collection AND is\n // currently flagged stale. Multiple MVs CAN share an output\n // collection in theory; in practice the registration validation +\n // cycle detection make this unusual.\n const candidates: string[] = []\n for (const mv of registry.all()) {\n if (mv.outputCollection !== outputCollection) continue\n if (!pending.has(mv.spec.name)) continue\n candidates.push(mv.spec.name)\n }\n if (candidates.length === 0) return\n\n let executor: typeof MVExecutorType | null = null\n for (const name of candidates) {\n const reg = registry.byName(name)\n if (!reg) {\n // Unreachable in practice — `candidates` is drawn from `registry.all()`,\n // and the orphan sweep above already cleared any name absent from it.\n // Kept as a defensive fallback; also clears the persisted marker so it\n // can't linger if this ever does fire.\n pending.delete(name)\n await deleteMVStaleMarker(adapter, vault, name)\n continue\n }\n executor ??= (await loadMVExecutor()).MaterializedViewExecutor\n // A cold-hydrated stale flag (unlike an in-session one) can fire with\n // NONE of the MV's dependency collections touched yet this session —\n // the sync `Query.toArray()` `spec.query()` runs reads straight off\n // `Collection`'s in-memory cache, populated only by a prior async\n // touch (`ensureHydrated()`, called from `get`/`list`/`put`, never\n // from `query()` itself). Warm every dependency first so the recompute\n // sees the live persisted state instead of an empty cache (#736).\n for (const dep of reg.dependencies) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await (accessor.getCollection(dep) as any).ensureHydrated()\n }\n await executor.refresh(reg, {\n getCollection: (n) => accessor.getCollection(n),\n getActiveTxContext: () => accessor.getActiveTxContext(),\n getQueryContext: () => accessor.getQueryContext(),\n ...(dispatchCtx !== undefined ? { dispatchCtx } : {}),\n })\n pending.delete(name)\n // Runs even when no marker was ever persisted for this name (an in-session-only\n // stale bit — e.g. the cheap ordinary-write path never writes one, see the\n // \"cheap-path guarantee\" test). `adapter.delete` on an absent key is a harmless\n // void — keeping this unconditional avoids branching on marker origin here.\n await deleteMVStaleMarker(adapter, vault, name)\n }\n}\n\n/**\n * Drop every stale flag for a registry. Used after a manual\n * `vault.refreshView(name)` runs the executor explicitly — the\n * post-refresh state matches the registered strategies, so\n * lingering stale bits would force a redundant refresh on the next\n * read.\n *\n * @internal\n */\nexport function clearMVStale(registry: MaterializedViewRegistry, mvName: string): void {\n _staleByRegistry.get(registry)?.delete(mvName)\n}\n\n/**\n * `clearMVStale` plus the persisted marker (#736) — `Vault.refreshView()`'s\n * completion path. A lingering persisted marker after a manual refresh would\n * make the NEXT cold session recompute redundantly.\n *\n * @internal\n */\nexport async function clearMVStaleFully(\n adapter: NoydbStore,\n vault: string,\n registry: MaterializedViewRegistry,\n mvName: string,\n): Promise<void> {\n clearMVStale(registry, mvName)\n await deleteMVStaleMarker(adapter, vault, mvName)\n}\n"],"mappings":"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","names":[]}
@@ -1,30 +1,29 @@
1
+ import {
2
+ ENCLAVE_SCHEME
3
+ } from "./chunk-JWWYRR4Q.js";
1
4
  import {
2
5
  ATTESTATIONS_COLLECTION,
3
6
  loadOrCreateSigner
4
- } from "./chunk-QWFHIOD2.js";
7
+ } from "./chunk-DLD5LGDS.js";
8
+ import {
9
+ bytesToB64url,
10
+ computeFieldHashes,
11
+ encodeQr,
12
+ signPayloadCore
13
+ } from "./chunk-GMKGGY7V.js";
5
14
  import {
6
15
  generateULID
7
16
  } from "./chunk-4AOERLTV.js";
8
17
  import {
9
- buildRecordAad
10
- } from "./chunk-KJFW62P4.js";
11
- import {
12
- buildRecordEnvelope
13
- } from "./chunk-SEE332EV.js";
14
- import {
18
+ buildRecordAad,
19
+ buildRecordEnvelope,
15
20
  encrypt
16
- } from "./chunk-UYB234ZR.js";
21
+ } from "./chunk-EZGV6F47.js";
17
22
  import {
18
23
  AttestationError
19
- } from "./chunk-244VXYPG.js";
24
+ } from "./chunk-37EPTBBR.js";
20
25
 
21
26
  // src/with-audit/attestation/issue.ts
22
- import {
23
- computeFieldHashes,
24
- signPayloadCore,
25
- encodeQr,
26
- bytesToB64url
27
- } from "@noy-db/attestation";
28
27
  async function issueAttestationCore(ctx, args) {
29
28
  if (ctx.role !== "owner") {
30
29
  throw new AttestationError(`issueAttestation requires the 'owner' role; caller is '${ctx.role}'. Issuing a signed attestation is the firm's identity operation.`);
@@ -41,7 +40,7 @@ async function issueAttestationCore(ctx, args) {
41
40
  throw new AttestationError(`issueAttestation: ${e.message}`);
42
41
  }
43
42
  const docId = generateULID();
44
- const sig = await signPayloadCore({ v: 1, docId, salt: saltB64, keyId: signer.keyId, fieldHashes }, signer.privateKeyPkcs8B64);
43
+ const sig = await signPayloadCore({ v: 1, docId, salt: saltB64, keyId: signer.keyId, fieldHashes }, signer.privateKeyPkcs8B64, ENCLAVE_SCHEME);
45
44
  const payload = { v: 1, docId, salt: saltB64, alg: "ed25519", keyId: signer.keyId, fieldHashes, sig };
46
45
  const index = {
47
46
  docId,
@@ -63,4 +62,4 @@ async function issueAttestationCore(ctx, args) {
63
62
  export {
64
63
  issueAttestationCore
65
64
  };
66
- //# sourceMappingURL=chunk-MTL7MXGR.js.map
65
+ //# sourceMappingURL=chunk-VMV5SBE4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-audit/attestation/issue.ts"],"sourcesContent":["import type { NoydbStore } from '../../kernel/types.js'\nimport { buildRecordAad, buildRecordEnvelope, encrypt, type EnclaveKey } from '../../capsule/index.js'\nimport { AttestationError } from '../../kernel/errors.js'\nimport { generateULID } from '../../with-pod/ulid.js'\nimport { loadOrCreateSigner, ATTESTATIONS_COLLECTION } from './signer.js'\nimport { ENCLAVE_SCHEME } from './scheme.js'\nimport { computeFieldHashes, signPayloadCore, encodeQr, bytesToB64url } from '@noy-db/attestation'\nimport type { AttestationFieldSchema, QrPayload } from './types.js'\n\n/** Everything issueAttestationCore needs from the Vault, injected for testability. */\nexport interface IssueContext {\n readonly store: NoydbStore\n readonly vault: string\n readonly role: string\n /** The _attestations collection DEK (AES-KW-wrapped under KEK by the keyring). */\n getDEK(): Promise<EnclaveKey>\n /** Decrypted source record + its envelope version, or null if absent. */\n readRecord(collection: string, id: string): Promise<{ record: Record<string, unknown>; version: number } | null>\n}\n\nexport interface IssueArgs {\n readonly collection: string\n readonly id: string\n readonly fieldSchema: AttestationFieldSchema\n}\nexport interface IssueResult {\n readonly docId: string\n readonly qr: string\n readonly payload: QrPayload\n readonly keyId: string\n readonly publicKeyB64: string\n}\n\nexport async function issueAttestationCore(ctx: IssueContext, args: IssueArgs): Promise<IssueResult> {\n if (ctx.role !== 'owner') {\n throw new AttestationError(`issueAttestation requires the 'owner' role; caller is '${ctx.role}'. Issuing a signed attestation is the firm's identity operation.`)\n }\n const src = await ctx.readRecord(args.collection, args.id)\n if (!src) throw new AttestationError(`issueAttestation: source record '${args.collection}/${args.id}' not found.`)\n\n const dek = await ctx.getDEK()\n // ONE signer implementation, from signer.ts. Lazily minted + persisted.\n const signer = await loadOrCreateSigner(ctx.store, ctx.vault, () => Promise.resolve(dek))\n\n const saltB64 = bytesToB64url(crypto.getRandomValues(new Uint8Array(16)))\n let fieldHashes: string[]\n try {\n fieldHashes = await computeFieldHashes(saltB64, args.fieldSchema, src.record)\n } catch (e) {\n throw new AttestationError(`issueAttestation: ${(e as Error).message}`)\n }\n const docId = generateULID()\n\n const sig = await signPayloadCore({ v: 1, docId, salt: saltB64, keyId: signer.keyId, fieldHashes }, signer.privateKeyPkcs8B64, ENCLAVE_SCHEME)\n const payload: QrPayload = { v: 1, docId, salt: saltB64, alg: 'ed25519', keyId: signer.keyId, fieldHashes, sig }\n\n const index = {\n docId, issuedAt: new Date().toISOString(), keyId: signer.keyId,\n fieldPaths: args.fieldSchema.fields.map((f) => f.path),\n sourceRefs: [{ collection: args.collection, id: args.id, version: src.version }],\n }\n const identity = { collection: ATTESTATIONS_COLLECTION, id: docId, version: 1 }\n const { iv, data } = await encrypt(JSON.stringify(index), dek, buildRecordAad(identity))\n const env = buildRecordEnvelope(\n identity,\n { ts: index.issuedAt, iv, data },\n )\n await ctx.store.put(ctx.vault, ATTESTATIONS_COLLECTION, docId, env)\n\n return { docId, qr: encodeQr(payload), payload, keyId: signer.keyId, publicKeyB64: signer.publicKeyB64 }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,eAAsB,qBAAqB,KAAmB,MAAuC;AACnG,MAAI,IAAI,SAAS,SAAS;AACxB,UAAM,IAAI,iBAAiB,0DAA0D,IAAI,IAAI,mEAAmE;AAAA,EAClK;AACA,QAAM,MAAM,MAAM,IAAI,WAAW,KAAK,YAAY,KAAK,EAAE;AACzD,MAAI,CAAC,IAAK,OAAM,IAAI,iBAAiB,oCAAoC,KAAK,UAAU,IAAI,KAAK,EAAE,cAAc;AAEjH,QAAM,MAAM,MAAM,IAAI,OAAO;AAE7B,QAAM,SAAS,MAAM,mBAAmB,IAAI,OAAO,IAAI,OAAO,MAAM,QAAQ,QAAQ,GAAG,CAAC;AAExF,QAAM,UAAU,cAAc,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC,CAAC;AACxE,MAAI;AACJ,MAAI;AACF,kBAAc,MAAM,mBAAmB,SAAS,KAAK,aAAa,IAAI,MAAM;AAAA,EAC9E,SAAS,GAAG;AACV,UAAM,IAAI,iBAAiB,qBAAsB,EAAY,OAAO,EAAE;AAAA,EACxE;AACA,QAAM,QAAQ,aAAa;AAE3B,QAAM,MAAM,MAAM,gBAAgB,EAAE,GAAG,GAAG,OAAO,MAAM,SAAS,OAAO,OAAO,OAAO,YAAY,GAAG,OAAO,oBAAoB,cAAc;AAC7I,QAAM,UAAqB,EAAE,GAAG,GAAG,OAAO,MAAM,SAAS,KAAK,WAAW,OAAO,OAAO,OAAO,aAAa,IAAI;AAE/G,QAAM,QAAQ;AAAA,IACZ;AAAA,IAAO,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,IAAG,OAAO,OAAO;AAAA,IACzD,YAAY,KAAK,YAAY,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,IACrD,YAAY,CAAC,EAAE,YAAY,KAAK,YAAY,IAAI,KAAK,IAAI,SAAS,IAAI,QAAQ,CAAC;AAAA,EACjF;AACA,QAAM,WAAW,EAAE,YAAY,yBAAyB,IAAI,OAAO,SAAS,EAAE;AAC9E,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,KAAK,UAAU,KAAK,GAAG,KAAK,eAAe,QAAQ,CAAC;AACvF,QAAM,MAAM;AAAA,IACV;AAAA,IACA,EAAE,IAAI,MAAM,UAAU,IAAI,KAAK;AAAA,EACjC;AACA,QAAM,IAAI,MAAM,IAAI,IAAI,OAAO,yBAAyB,OAAO,GAAG;AAElE,SAAO,EAAE,OAAO,IAAI,SAAS,OAAO,GAAG,SAAS,OAAO,OAAO,OAAO,cAAc,OAAO,aAAa;AACzG;","names":[]}
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  loadVaultPolicy,
3
3
  saveVaultPolicy
4
- } from "./chunk-DC5QVPV7.js";
4
+ } from "./chunk-VHBKYN2J.js";
5
5
  import {
6
6
  PolicyDeniedError,
7
7
  ValidationError
8
- } from "./chunk-244VXYPG.js";
8
+ } from "./chunk-37EPTBBR.js";
9
9
 
10
10
  // src/with-party/policy/presets.ts
11
11
  var PERSONAL_POLICY = Object.freeze({
@@ -364,4 +364,4 @@ export {
364
364
  describeGate,
365
365
  createNoydbPolicy
366
366
  };
367
- //# sourceMappingURL=chunk-F7XGHL5O.js.map
367
+ //# sourceMappingURL=chunk-VQDZYB2B.js.map
@@ -2,7 +2,7 @@ import {
2
2
  OverlayBaseIsVirtualError,
3
3
  OverlayCollectionUnavailableError,
4
4
  OverlayNameCollisionError
5
- } from "./chunk-244VXYPG.js";
5
+ } from "./chunk-37EPTBBR.js";
6
6
 
7
7
  // src/with-formula/overlay-views/registry.ts
8
8
  var OverlayedViewRegistry = class {
@@ -68,4 +68,4 @@ var OverlayedViewRegistry = class {
68
68
  export {
69
69
  OverlayedViewRegistry
70
70
  };
71
- //# sourceMappingURL=chunk-BK2HHQGN.js.map
71
+ //# sourceMappingURL=chunk-VVOLDVIR.js.map
@@ -7,28 +7,28 @@ import {
7
7
  deriveSchemaManifest,
8
8
  loadSchemaManifestEntry,
9
9
  saveSchemaManifest
10
- } from "./chunk-SYVZW77S.js";
10
+ } from "./chunk-TEHHROZL.js";
11
11
  import {
12
12
  MANIFEST_COLLECTION
13
13
  } from "./chunk-EPFF7LOX.js";
14
14
  import {
15
15
  loadFence,
16
16
  saveFence
17
- } from "./chunk-L7DOS3TE.js";
17
+ } from "./chunk-VAORSCSU.js";
18
18
  import {
19
19
  derivePersistedSchema
20
- } from "./chunk-PSIV7CJV.js";
20
+ } from "./chunk-747G6CT3.js";
21
21
  import {
22
22
  loadPersistedSchemaEntry,
23
23
  savePersistedSchema
24
- } from "./chunk-3H5KLDZN.js";
24
+ } from "./chunk-3L2NBKDR.js";
25
25
  import {
26
26
  envelopePayloadHash
27
- } from "./chunk-CKNGS5MH.js";
27
+ } from "./chunk-6QVKPHBF.js";
28
28
  import {
29
29
  ManifestConflictError,
30
30
  isConflictError
31
- } from "./chunk-244VXYPG.js";
31
+ } from "./chunk-37EPTBBR.js";
32
32
 
33
33
  // src/with-shape/persisted-schemas/field-ids.ts
34
34
  var BASE32URL_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
@@ -257,4 +257,4 @@ export {
257
257
  persistClassifiedMarker,
258
258
  persistSatelliteMarker
259
259
  };
260
- //# sourceMappingURL=chunk-6LMQ5R26.js.map
260
+ //# sourceMappingURL=chunk-WPOYWFSE.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/with-shape/persisted-schemas/field-ids.ts","../src/with-shape/manifest/writer.ts","../src/with-shape/manifest/sync.ts","../src/with-shape/persisted-schemas/register.ts"],"sourcesContent":["/**\n * Stable field-identifier minting (#946).\n *\n * A field's `id` is opaque and permanent — minted once, from randomness\n * only, with no timestamp and no name-derivation. A name-derived id would\n * change when the field is renamed, defeating the whole point (identity\n * must survive a rename). See `PersistedSchemaEnvelope.fieldIds`.\n *\n * @module\n */\n\nconst BASE32URL_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'\n\n/** Unpadded RFC 4648 §7 base32url encoding of an arbitrary byte string. */\nfunction encodeBase32Url(bytes: Uint8Array): string {\n let bits = 0\n let value = 0\n let out = ''\n for (const byte of bytes) {\n value = (value << 8) | byte\n bits += 8\n while (bits >= 5) {\n out += BASE32URL_ALPHABET[(value >>> (bits - 5)) & 31]\n bits -= 5\n }\n }\n if (bits > 0) {\n out += BASE32URL_ALPHABET[(value << (5 - bits)) & 31]\n }\n return out\n}\n\n/**\n * Mint a fresh opaque field id: 12 random bytes (96 bits) from\n * `crypto.getRandomValues`, encoded as a 20-character base32url token.\n * Not exported — Task 2/3 (#946) may need to export this later; keep it\n * module-internal until an external consumer exists.\n */\nfunction mintFieldId(): string {\n const bytes = new Uint8Array(12)\n crypto.getRandomValues(bytes)\n return encodeBase32Url(bytes)\n}\n\n/** Top-level property names of a Draft 2020-12 object JSON Schema, or `[]` when not an object schema. */\nfunction topLevelFieldNames(jsonSchema: object | null): readonly string[] {\n if (jsonSchema === null) return []\n const props = (jsonSchema as { properties?: unknown }).properties\n if (props === null || typeof props !== 'object') return []\n return Object.keys(props as Record<string, unknown>)\n}\n\n/**\n * Resolve the `fieldIds` map for a freshly-derived schema against the\n * previously-stored envelope's map (if any). Existing ids are preserved by\n * name — a field keeps its id across re-derivation as long as its name is\n * unchanged; a genuinely new field name mints a fresh id. Returns\n * `undefined` when the fresh schema has no derivable top-level field set\n * (e.g. a stub envelope, or a non-object root schema).\n *\n * `renamed` (#946 Task 2) — unambiguous rename pairs from\n * `computeSchemaDelta`'s `SchemaDelta.renamed`. When the fresh name is a\n * rename target (`to`), the id is looked up under its OLD name (`from`)\n * instead of the fresh name itself — the whole point of a stable field id\n * is that it survives exactly this case; without it, a rename would mint a\n * fresh id for the new name and silently drop the old one.\n */\nexport function resolveFieldIds(\n freshJsonSchema: object | null,\n priorFieldIds: Record<string, string> | undefined,\n renamed?: readonly { readonly from: string; readonly to: string }[],\n): Record<string, string> | undefined {\n const names = topLevelFieldNames(freshJsonSchema)\n if (names.length === 0) return undefined\n const priorNameFor = new Map<string, string>()\n if (renamed) for (const { from, to } of renamed) priorNameFor.set(to, from)\n const out: Record<string, string> = {}\n for (const name of names) {\n const priorName = priorNameFor.get(name) ?? name\n out[name] = priorFieldIds?.[priorName] ?? mintFieldId()\n }\n return out\n}\n","/**\n * Strict-CAS schema-manifest writer (#941, AC #1).\n *\n * Every other reserved-collection writer in the hub (`persisted-schemas`'s\n * `savePersistedSchema` callers, the classified/satellite marker writers)\n * retries on a CAS conflict: re-read, re-apply, try again. The schema\n * manifest deliberately does NOT — two concurrent *direct* edits to the\n * manifest must be refused and surfaced, never silently merged or\n * retry-resolved, because a manifest write encodes a specific point-in-time\n * claim about the pod's schema generation/content-hash set. A caller that\n * lost the race must re-derive against the fresh state and decide what to\n * do, not have its stale write quietly re-applied on top of someone else's.\n *\n * Ledger audit: this writer does not have a `LedgerStore` handle at this\n * seam (mirroring `persisted-schemas`, whose `_schemas/<collection>` writes\n * are likewise not ledger-audited today). Wiring `LedgerStore.append({ op:\n * 'migration', ... })` around a manifest write belongs at the call site\n * where a ledger handle is already in scope (the open/write integration —\n * #941 Task 3/4), not here.\n *\n * @module\n */\n\nimport { isConflictError, ManifestConflictError } from '../../kernel/errors.js'\nimport { saveSchemaManifest, type GetManifestDEK } from './storage.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { SchemaManifest } from './types.js'\n\n/**\n * Write the schema manifest as a strict CAS: `expectedVersion` must match\n * the stored envelope's `_v` (0 when no manifest exists yet). On a\n * conflict, throws {@link ManifestConflictError} — this function never\n * retries.\n */\nexport async function writeSchemaManifest(\n store: NoydbStore,\n vault: string,\n manifest: SchemaManifest,\n expectedVersion: number,\n getDEK: GetManifestDEK,\n): Promise<void> {\n try {\n await saveSchemaManifest(store, vault, manifest, expectedVersion, getDEK)\n } catch (err) {\n if (isConflictError(err)) {\n throw new ManifestConflictError(err.version, expectedVersion)\n }\n throw err\n }\n}\n","/**\n * Keep the persisted `_manifest/schema` record in sync with the\n * per-collection `_schemas/<collection>` source of truth (#941 Task 3).\n *\n * Called from `persisted-schemas/register.ts`'s `persistSchemaIfNeeded`\n * right after a `_schemas/<collection>` write succeeds.\n *\n * ## Reconciling strict-CAS-refuse with the derived nature of the manifest\n *\n * `writer.ts`'s `writeSchemaManifest` is deliberately strict-CAS: a\n * concurrent *direct* edit to the manifest is refused, never retried (AC\n * #1). But this call site is not a direct edit — it is a re-derivation\n * from the source of truth, triggered as a side effect of a `_schemas`\n * write that has ALREADY succeeded. When N collections are declared\n * together (one `openVault()` call registering several `persistJsonSchema:\n * true` collections), their `persistSchemaIfNeeded` calls run CONCURRENTLY\n * — each one's own `_schemas/<collection>` write lands independently, and\n * each then calls this function. A single derive-then-write with no\n * feedback loop is NOT enough here: a call whose `deriveSchemaManifest`\n * snapshot ran before a sibling's `_schemas` write landed would persist a\n * manifest that's missing that sibling forever (nothing re-triggers it) —\n * this was a REAL bug (not a test-isolation artifact — reproduced with 0\n * shared state, purely from concurrent `_schemas` writes racing the derive\n * snapshot; see #941 flake fix commit). `syncSchemaManifest` therefore\n * loops (bounded by {@link MAX_SYNC_ATTEMPTS}):\n *\n * 1. Derive fresh, compare to what's persisted. Already converged → done.\n * 2. Write via strict-CAS. `ManifestConflictError` (another writer moved\n * the manifest first) → loop: re-derive against the NEW state and\n * retry, exactly like a normal optimistic-CAS retry.\n * 3. On a successful write, derive ONE more time. If a sibling's\n * `_schemas` write landed while we were deriving/writing, the state\n * has already moved past what we just wrote — loop again to catch it\n * up. If the re-derive matches what we wrote, we're the last writer\n * and the manifest is provably converged: return.\n *\n * This guarantees that whichever collection's `_schemas` write is the LAST\n * to land (in real wall-clock order — there is always exactly one, even\n * though no single caller knows which) ends up running a sync whose own\n * derive sees every sibling's write already committed, and whose\n * recheck-after-write confirms nothing moved further — so it converges the\n * manifest deterministically, regardless of interleaving. If attempts are\n * exhausted under pathological contention, the loop gives up — with a\n * `console.warn` (see cap-exhaustion note below), not silently — leaving\n * the last successful write in place; the manifest is a derivable cache\n * (not a correctness invariant), so a next schema write or `open()`'s\n * re-derive still converges it, matching how `persistSchemaIfNeeded`\n * already treats its own write failures.\n * Any error OTHER than `ManifestConflictError` (store failure, DEK\n * resolution failure, etc.) is NOT swallowed — it propagates to the\n * caller immediately, matching how `persistSchemaIfNeeded` already treats\n * its own write failures as a visible (not silently discarded) fingerprint\n * failure.\n *\n * ## CRITICAL fix (#941 review): a scoped principal must never write a\n * partial manifest\n *\n * `deriveSchemaManifest` returns `undecodableCollections` for any\n * `_schemas/<collection>` entry this principal's `lookupDEK` couldn't\n * resolve — e.g. a member `grant()`-scoped to one collection can decrypt\n * `_manifest` itself (system-prefixed DEKs propagate to every role) but\n * NOT a sibling data collection's `_schemas/<sibling>` entry. Before this\n * fix, `deriveSchemaManifest` silently treated \"can't decrypt\" the same as\n * \"no derivable content\" and omitted the sibling — so a scoped member's\n * OWN schema declare would derive a manifest missing every collection they\n * can't see, load the real (complete) persisted manifest as a valid CAS\n * token, observe a hash mismatch, and OVERWRITE it — permanently dropping\n * the siblings, silently, ledger-audited as a legitimate migration. The\n * converging recheck loop above made this WORSE, not better: re-deriving\n * from the same restricted view just re-confirms the same partial result\n * and declares it \"stable.\"\n *\n * Fix: if `undecodableCollections` is non-empty, `syncSchemaManifest`\n * `SKIP`s entirely — no read-compare, no write, no audit, no loop. It does\n * NOT try to merge-preserve the undecodable entries from what's currently\n * persisted: an `aggregateHash` computed over entries this principal can't\n * verify would be a hash asserting something it never actually checked,\n * which is worse than not writing at all. This is safe because:\n *\n * - The source of truth, `_schemas/<their-collection>`, was ALREADY\n * updated by their declare — nothing about their own change is lost.\n * - The pod-wide `_manifest` cache simply isn't rewritten by a principal\n * who'd corrupt it by omission — it stays whatever a full-visibility\n * principal (owner/admin/custodian) last wrote.\n * - `open()` re-derives from `_schemas` directly (not from the cached\n * manifest) for whoever opens it, so a stale cache is never load-bearing.\n * - The NEXT full-visibility principal's declare (or any subsequent sync\n * by someone with complete access) naturally refreshes the cache,\n * including the scoped member's own just-landed change.\n *\n * ## Ledger audit (AC #5)\n *\n * Schema-generation transitions are not ledger-audited anywhere else today\n * (neither the `_schemas/<collection>` write in `register.ts` nor the fence\n * bump in `schema-update/fence-controller.ts#runCutover`). This is the\n * first ledger-audited signal for a schema change: every manifest write\n * that actually happens (i.e. every time the derived manifest differs from\n * what's stored) appends an `op: 'migration'` entry via the optional\n * `getLedgerOrNull` callback — `null`/absent when the history strategy\n * isn't opted in, matching every other optional-ledger call site\n * (`vault._getLedgerOrNull()?.append(...)`, see `liberate.ts`,\n * `with-audit/periods/vault-facade.ts`). No audit fires on the no-op path\n * (derived manifest unchanged), on a swallowed conflict (the winning\n * writer's own sync already audits its write), or on the scoped-principal\n * SKIP path above (nothing was written).\n *\n * @module\n */\n\nimport type { LedgerStore } from '../../with-commit/history/ledger/store.js'\nimport { envelopePayloadHash } from '../../with-commit/history/ledger/hash.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport { ManifestConflictError } from '../../kernel/errors.js'\nimport { deriveSchemaManifest, type LookupDEK } from './derive.js'\nimport { loadSchemaManifestEntry, type GetManifestDEK } from './storage.js'\nimport { writeSchemaManifest } from './writer.js'\nimport { MANIFEST_COLLECTION } from './reserved-collections.js'\nimport { MANIFEST_SCHEMA_RECORD_ID, type SchemaManifest } from './types.js'\n\nexport interface ManifestSyncDeps {\n /** Resolver for `_manifest`'s OWN DEK — mints if absent (this principal's declare is about to write it). */\n readonly getDEK: GetManifestDEK\n /**\n * NON-MINTING lookup for every OTHER (sibling data) collection's DEK,\n * used by `deriveSchemaManifest` to build the index — see `derive.ts`'s\n * `LookupDEK` doc for why minting here would be actively harmful.\n */\n readonly lookupDEK: LookupDEK\n /** Optional ledger handle — absent when the history strategy isn't opted in (no-op audit). */\n readonly getLedgerOrNull?: () => LedgerStore | null\n}\n\n/**\n * Bound on the derive→write→recheck loop (see module doc). Headroom above\n * what any realistic `openVault()` batch (a handful of collections) needs —\n * a 40-way concurrent-declare stress repro converged within single digits.\n */\nconst MAX_SYNC_ATTEMPTS = 16\n\n/**\n * Re-derive the schema manifest and write it if it changed, looping until\n * the write is provably stable against concurrent sibling `_schemas`\n * writes (see module doc). SKIPS entirely (no write, no audit) when this\n * principal's view is incomplete (`undecodableCollections` non-empty) —\n * see the module doc's CRITICAL fix section. Swallows a\n * {@link ManifestConflictError} by retrying against the fresher state; any\n * other error propagates. Ledger-audits (`op: 'migration'`) once per\n * successful write.\n */\nexport async function syncSchemaManifest(\n store: NoydbStore,\n vault: string,\n deps: ManifestSyncDeps,\n): Promise<void> {\n for (let attempt = 0; attempt < MAX_SYNC_ATTEMPTS; attempt++) {\n const { manifest: derived, undecodableCollections } = await deriveSchemaManifest(store, vault, deps.lookupDEK)\n if (undecodableCollections.length > 0) {\n // Scoped principal — see module doc's CRITICAL fix. Never write a\n // manifest we know is missing collections we couldn't decrypt.\n return\n }\n\n const existing = await loadSchemaManifestEntry(store, vault, deps.getDEK)\n\n if (\n existing &&\n existing.manifest.generation === derived.generation &&\n existing.manifest.aggregateHash === derived.aggregateHash\n ) {\n return // already converged — no write, no audit\n }\n\n const expectedVersion = existing?.version ?? 0\n try {\n await writeSchemaManifest(store, vault, derived, expectedVersion, deps.getDEK)\n } catch (err) {\n if (err instanceof ManifestConflictError) continue // another writer moved it first — re-derive + retry\n throw err\n }\n\n await auditManifestWrite(store, vault, deps, derived, expectedVersion)\n\n // A sibling's `_schemas` write can land while WE were deriving/writing\n // (#941 flake fix — see module doc). Re-derive once more: if the truth\n // has already moved past what we just wrote, loop again to catch it up\n // instead of leaving a permanently-partial manifest. (A principal with\n // full visibility at the top of this iteration cannot lose that\n // visibility mid-call — the keyring doesn't change during one sync —\n // so this recheck is never itself subject to the undecodable-skip; the\n // `for` loop's own guard covers it defensively regardless.)\n const recheck = await deriveSchemaManifest(store, vault, deps.lookupDEK)\n if (recheck.undecodableCollections.length > 0) return // defensive — see comment above\n if (recheck.manifest.generation === derived.generation && recheck.manifest.aggregateHash === derived.aggregateHash) {\n return // stable — we were the last writer\n }\n }\n // Attempts exhausted under pathological contention — see module doc:\n // the manifest is a derivable cache, not a correctness invariant, so a\n // later schema write or open()'s re-derive still converges it. Warn\n // rather than fail silently — a silent give-up on a completeness\n // invariant is exactly what hid the original concurrent-declare race.\n console.warn(\n `[noy-db] schema-manifest sync did not converge after ${MAX_SYNC_ATTEMPTS} attempts; ` +\n 'the persisted manifest may be stale until the next schema write or open()\\'s re-derive.',\n )\n}\n\nasync function auditManifestWrite(\n store: NoydbStore,\n vault: string,\n deps: ManifestSyncDeps,\n derived: SchemaManifest,\n expectedVersion: number,\n): Promise<void> {\n const ledger = deps.getLedgerOrNull?.()\n if (!ledger) return\n // `_manifest/schema` has a real, individually-addressable envelope (unlike\n // a `lifecycle` entry's empty collection/id) — `verifyBackupIntegrity`'s\n // data cross-check (`with-pod/backup.ts`) treats any non-amendment,\n // non-lifecycle entry's `collection`/`id` as \"the latest write to this\n // record\" and recomputes+compares its ciphertext hash, so `payloadHash`\n // must be the real `envelopePayloadHash` of the just-written envelope\n // (ciphertext domain), NOT `derived.aggregateHash` (plaintext domain) —\n // using the wrong domain here trips a false-positive tamper failure on\n // restore.\n const envelope = await store.get(vault, MANIFEST_COLLECTION, MANIFEST_SCHEMA_RECORD_ID)\n await ledger.append({\n op: 'migration',\n collection: MANIFEST_COLLECTION,\n id: MANIFEST_SCHEMA_RECORD_ID,\n version: expectedVersion + 1,\n actor: '',\n payloadHash: await envelopePayloadHash(envelope),\n reason: `schema-manifest-sync:generation=${derived.generation}`,\n })\n}\n","/**\n * Orchestrate the derive → hash → skip-or-write cycle for a collection's\n * persisted JSON Schema. Called by the Vault at collection-registration\n * time when the developer opts in via `collection({ persistJsonSchema:\n * true })`.\n *\n * Skip semantics:\n *\n * - Zod validators: skip when the new hash equals the stored hash.\n * - Non-Zod (stub envelopes have hash=null): skip when the stored\n * envelope's `kind` matches the freshly-detected kind (since there's\n * no body to compare yet — a kind change is the only signal).\n *\n * @module\n */\n\nimport { derivePersistedSchema } from './derive.js'\nimport { loadPersistedSchemaEntry, savePersistedSchema } from './storage.js'\nimport { resolveFieldIds } from './field-ids.js'\nimport { computeSchemaDelta } from '../schema-update/delta.js'\nimport { evaluateStrategies } from '../schema-update/dispatch.js'\nimport { loadFence, saveFence } from '../schema-update/fence.js'\nimport { isConflictError } from '../../kernel/errors.js'\nimport { syncSchemaManifest, type ManifestSyncDeps } from '../manifest/sync.js'\nimport type { SchemaUpdateStrategy, UpdateDecision, SchemaDelta } from '../schema-update/types.js'\nimport type { NoydbStore, ClassifiedMarker } from '../../kernel/types.js'\nimport type { PersistedSchemaEnvelope } from './types.js'\nimport type { PairingMarker } from '../satellites/types.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\n\n/**\n * Max optimistic-CAS retries for the shared `_schemas/<collection>` record\n * (#583). Only two writers ever contend (the JSON-Schema writer and the\n * classified-marker writer), so one retry suffices; the headroom covers a\n * pathological re-order. Exhausting it rethrows the {@link ConflictError}\n * rather than silently losing a write.\n */\nconst MAX_SCHEMA_CAS_RETRIES = 5\n\nexport interface PersistSchemaResult {\n /** True when a fresh envelope was written to storage. */\n readonly written: boolean\n /** True when an existing envelope matched and the write was skipped. */\n readonly skipped: boolean\n /** The envelope that was either written or matched. */\n readonly envelope: PersistedSchemaEnvelope\n /** The update-strategy decision, present when strategies ran. */\n readonly decision?: UpdateDecision\n}\n\nexport async function persistSchemaIfNeeded(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly validator: unknown\n readonly dek: EnclaveKey\n readonly strategies?: readonly SchemaUpdateStrategy[]\n /** #941 Task 3: re-derive + sync the `_manifest/schema` INDEX after a write. Omitted → no sync (e.g. tests that don't care). */\n readonly manifestSync?: ManifestSyncDeps\n}): Promise<PersistSchemaResult> {\n const fresh = await derivePersistedSchema(opts.validator)\n\n // The `_schemas/<collection>` record is shared with the classified-marker\n // writer, so a plain load→save loses whichever writer put first (#583). Read\n // the version at load time and CAS on it; on conflict re-read (picking up the\n // other writer's field), re-evaluate, and retry.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n\n if (stored && isEquivalent(stored, fresh)) {\n return { written: false, skipped: true, envelope: stored, decision: { action: 'allow' } }\n }\n\n // Changed (or first registration). Compute the delta whenever we have a\n // comparable JSON-Schema baseline — independent of whether strategies\n // were registered (#946: the rename pairing it carries feeds the\n // fieldIds id-carry below even on a bare re-declare with no\n // `schemaUpdate` strategies configured). Strategies only run when\n // registered.\n let decision: UpdateDecision = { action: 'allow' }\n const strategies = opts.strategies ?? []\n let delta: SchemaDelta | undefined\n if (\n stored &&\n stored.kind === fresh.kind &&\n isPlainObject(stored.jsonSchema) &&\n isPlainObject(fresh.jsonSchema)\n ) {\n delta = computeSchemaDelta(stored.jsonSchema, fresh.jsonSchema, opts.collectionName)\n if (strategies.length > 0) {\n decision = await evaluateStrategies(delta, strategies, { collection: opts.collectionName })\n }\n }\n\n if (decision.action !== 'allow') {\n // reject (or cutover): do NOT overwrite the baseline — the\n // old schema stays the source of truth until the change is resolved.\n return { written: false, skipped: false, envelope: stored ?? fresh, decision }\n }\n\n // Preserve a previously-persisted classified marker (C-A / R10) and\n // satellite pairing marker (R-S9) — the schema derivation knows nothing\n // about them, so a naive overwrite here would drop the config-drift\n // guards' cross-session signals.\n let toSave: PersistedSchemaEnvelope = fresh\n if (stored?.classified !== undefined) toSave = { ...toSave, classified: stored.classified }\n if (stored?.satellite !== undefined) toSave = { ...toSave, satellite: stored.satellite }\n\n // #946: mint/preserve stable per-field ids (by name, carrying a\n // detected rename's id from its old name — see `delta.renamed`) and\n // stamp the vault-wide schema-fence generation this write happened at —\n // binds \"generation N\" to this envelope's content hash\n // (schemaFenceState() + loadPersistedSchema agree).\n const fieldIds = resolveFieldIds(fresh.jsonSchema, stored?.fieldIds, delta?.renamed)\n if (fieldIds !== undefined) toSave = { ...toSave, fieldIds }\n const fence = await loadFence(opts.store, opts.vault)\n toSave = { ...toSave, generation: fence.currentSchemaVersion }\n\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, toSave, version)\n if (toSave.hash !== null) {\n // This is the FIRST non-barrier writer to `_meta/schema-fence` —\n // previously only SchemaFenceController.#setState wrote it, and only\n // sequentially, under the drain barrier. There is no CAS on the fence\n // doc (out of scope to add here), so re-read it immediately before\n // writing rather than reusing the `fence` snapshot captured above\n // (before the potentially-slow `savePersistedSchema` call): spreading\n // a stale snapshot here would roll back `currentSchemaVersion`/\n // `fenceState` if a concurrent cutover (on a different collection)\n // advanced the fence in between — corrupting the vault-wide gate the\n // whole barrier + MigrationRequiredError check depends on. Re-reading\n // narrows (does not eliminate) that window to just this one overlay.\n const freshFence = await loadFence(opts.store, opts.vault)\n await saveFence(opts.store, opts.vault, { ...freshFence, schemaHash: toSave.hash })\n }\n // #941 Task 3: keep the `_manifest/schema` INDEX in sync with the\n // `_schemas/<collection>` write that just landed. Opt-in via\n // `opts.manifestSync` (threaded from vault.ts, which is the only\n // caller with a general-purpose `getDEK` resolver + ledger handle).\n if (opts.manifestSync) {\n await syncSchemaManifest(opts.store, opts.vault, opts.manifestSync)\n }\n return { written: true, skipped: false, envelope: toSave, decision }\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n/**\n * Persist (or refresh) the C-A / R10 classified marker into the collection's\n * `_schemas/<collection>` record, preserving any existing derived JSON-Schema\n * body. Idempotent — a no-op when an equivalent marker is already stored.\n * Independent of `persistJsonSchema`: called on the first classified write so a\n * later naive handle can detect the drift cross-session.\n */\nexport async function persistClassifiedMarker(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly dek: EnclaveKey\n readonly marker: ClassifiedMarker\n}): Promise<void> {\n // Shares the `_schemas/<collection>` record with the JSON-Schema writer;\n // CAS on the loaded version so a concurrent schema (re)registration can't\n // silently drop the marker (and vice-versa) — see #583.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n if (stored?.classified !== undefined && markersEqual(stored.classified, opts.marker)) return\n const payload: PersistedSchemaEnvelope =\n stored !== undefined\n ? { ...stored, classified: opts.marker }\n : {\n _noydb_schema: 1,\n kind: 'Unknown',\n jsonSchema: null,\n hash: null,\n derivedAt: new Date().toISOString(),\n classified: opts.marker,\n }\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, payload, version)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n/**\n * Persist (or refresh) the R-S9 satellite pairing marker into the collection's\n * `_schemas/<collection>` record, preserving any existing derived JSON-Schema\n * body. Idempotent — a no-op when an equivalent marker is already stored.\n * Independent of `persistJsonSchema`: called on satellite declaration so a\n * later divergent re-declaration can be detected cross-session.\n */\nexport async function persistSatelliteMarker(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly dek: EnclaveKey\n readonly marker: PairingMarker\n}): Promise<void> {\n // Shares the `_schemas/<collection>` record with the JSON-Schema writer;\n // CAS on the loaded version so a concurrent schema (re)registration can't\n // silently drop the marker (and vice-versa) — see #583.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n if (stored?.satellite !== undefined && satelliteMarkersEqual(stored.satellite, opts.marker)) return\n const payload: PersistedSchemaEnvelope =\n stored !== undefined\n ? { ...stored, satellite: opts.marker }\n : {\n _noydb_schema: 1,\n kind: 'Unknown',\n jsonSchema: null,\n hash: null,\n derivedAt: new Date().toISOString(),\n satellite: opts.marker,\n }\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, payload, version)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n// #597: both equality checks below deliberately do NOT compare `epoch`. A\n// live collection re-opening itself computes a fresh epoch candidate on\n// every declare (marker.ts / config-drift.ts) that must still be treated as\n// \"the same marker\" so the no-op fast path fires and the already-persisted\n// epoch is left untouched — that's what makes epoch stable across re-opens.\n// Comparing epoch here would break that fast path today, with no\n// corresponding benefit: there is no delete-collection API yet, so a\n// mismatched epoch can't (yet) mean \"this name was reused for a genuinely\n// new collection.\" Wiring an epoch-mismatch REJECTION is a deferred\n// follow-up for whenever collection deletion ships.\n\nfunction satelliteMarkersEqual(a: PairingMarker, b: PairingMarker): boolean {\n return a.base === b.base && a.fieldsHash === b.fieldsHash && (a.joined ?? null) === (b.joined ?? null)\n}\n\nfunction markersEqual(a: ClassifiedMarker, b: ClassifiedMarker): boolean {\n return sameSet(a.digestOnly, b.digestOnly) && sameSet(a.equatable, b.equatable)\n}\n\nfunction sameSet(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false\n const s = new Set(a)\n return b.every((x) => s.has(x))\n}\n\nfunction isPlainObject(v: unknown): v is object {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction isEquivalent(a: PersistedSchemaEnvelope, b: PersistedSchemaEnvelope): boolean {\n if (a.kind !== b.kind) return false\n // Zod path: real hashes — compare directly.\n if (a.hash && b.hash) return a.hash === b.hash\n // Stub path: both have hash=null. Kind equality is the only signal we have.\n if (a.hash === null && b.hash === null) return true\n // Mixed (one has a hash, the other doesn't) — treat as changed.\n return false\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/with-shape/persisted-schemas/field-ids.ts","../src/with-shape/manifest/writer.ts","../src/with-shape/manifest/sync.ts","../src/with-shape/persisted-schemas/register.ts"],"sourcesContent":["/**\n * Stable field-identifier minting (#946).\n *\n * A field's `id` is opaque and permanent — minted once, from randomness\n * only, with no timestamp and no name-derivation. A name-derived id would\n * change when the field is renamed, defeating the whole point (identity\n * must survive a rename). See `PersistedSchemaEnvelope.fieldIds`.\n *\n * @module\n */\n\nconst BASE32URL_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'\n\n/** Unpadded RFC 4648 §7 base32url encoding of an arbitrary byte string. */\nfunction encodeBase32Url(bytes: Uint8Array): string {\n let bits = 0\n let value = 0\n let out = ''\n for (const byte of bytes) {\n value = (value << 8) | byte\n bits += 8\n while (bits >= 5) {\n out += BASE32URL_ALPHABET[(value >>> (bits - 5)) & 31]\n bits -= 5\n }\n }\n if (bits > 0) {\n out += BASE32URL_ALPHABET[(value << (5 - bits)) & 31]\n }\n return out\n}\n\n/**\n * Mint a fresh opaque field id: 12 random bytes (96 bits) from\n * `crypto.getRandomValues`, encoded as a 20-character base32url token.\n * Not exported — Task 2/3 (#946) may need to export this later; keep it\n * module-internal until an external consumer exists.\n */\nfunction mintFieldId(): string {\n const bytes = new Uint8Array(12)\n crypto.getRandomValues(bytes)\n return encodeBase32Url(bytes)\n}\n\n/** Top-level property names of a Draft 2020-12 object JSON Schema, or `[]` when not an object schema. */\nfunction topLevelFieldNames(jsonSchema: object | null): readonly string[] {\n if (jsonSchema === null) return []\n const props = (jsonSchema as { properties?: unknown }).properties\n if (props === null || typeof props !== 'object') return []\n return Object.keys(props as Record<string, unknown>)\n}\n\n/**\n * Resolve the `fieldIds` map for a freshly-derived schema against the\n * previously-stored envelope's map (if any). Existing ids are preserved by\n * name — a field keeps its id across re-derivation as long as its name is\n * unchanged; a genuinely new field name mints a fresh id. Returns\n * `undefined` when the fresh schema has no derivable top-level field set\n * (e.g. a stub envelope, or a non-object root schema).\n *\n * `renamed` (#946 Task 2) — unambiguous rename pairs from\n * `computeSchemaDelta`'s `SchemaDelta.renamed`. When the fresh name is a\n * rename target (`to`), the id is looked up under its OLD name (`from`)\n * instead of the fresh name itself — the whole point of a stable field id\n * is that it survives exactly this case; without it, a rename would mint a\n * fresh id for the new name and silently drop the old one.\n */\nexport function resolveFieldIds(\n freshJsonSchema: object | null,\n priorFieldIds: Record<string, string> | undefined,\n renamed?: readonly { readonly from: string; readonly to: string }[],\n): Record<string, string> | undefined {\n const names = topLevelFieldNames(freshJsonSchema)\n if (names.length === 0) return undefined\n const priorNameFor = new Map<string, string>()\n if (renamed) for (const { from, to } of renamed) priorNameFor.set(to, from)\n const out: Record<string, string> = {}\n for (const name of names) {\n const priorName = priorNameFor.get(name) ?? name\n out[name] = priorFieldIds?.[priorName] ?? mintFieldId()\n }\n return out\n}\n","/**\n * Strict-CAS schema-manifest writer (#941, AC #1).\n *\n * Every other reserved-collection writer in the hub (`persisted-schemas`'s\n * `savePersistedSchema` callers, the classified/satellite marker writers)\n * retries on a CAS conflict: re-read, re-apply, try again. The schema\n * manifest deliberately does NOT — two concurrent *direct* edits to the\n * manifest must be refused and surfaced, never silently merged or\n * retry-resolved, because a manifest write encodes a specific point-in-time\n * claim about the pod's schema generation/content-hash set. A caller that\n * lost the race must re-derive against the fresh state and decide what to\n * do, not have its stale write quietly re-applied on top of someone else's.\n *\n * Ledger audit: this writer does not have a `LedgerStore` handle at this\n * seam (mirroring `persisted-schemas`, whose `_schemas/<collection>` writes\n * are likewise not ledger-audited today). Wiring `LedgerStore.append({ op:\n * 'migration', ... })` around a manifest write belongs at the call site\n * where a ledger handle is already in scope (the open/write integration —\n * #941 Task 3/4), not here.\n *\n * @module\n */\n\nimport { isConflictError, ManifestConflictError } from '../../kernel/errors.js'\nimport { saveSchemaManifest, type GetManifestDEK } from './storage.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { SchemaManifest } from './types.js'\n\n/**\n * Write the schema manifest as a strict CAS: `expectedVersion` must match\n * the stored envelope's `_v` (0 when no manifest exists yet). On a\n * conflict, throws {@link ManifestConflictError} — this function never\n * retries.\n */\nexport async function writeSchemaManifest(\n store: NoydbStore,\n vault: string,\n manifest: SchemaManifest,\n expectedVersion: number,\n getDEK: GetManifestDEK,\n): Promise<void> {\n try {\n await saveSchemaManifest(store, vault, manifest, expectedVersion, getDEK)\n } catch (err) {\n if (isConflictError(err)) {\n throw new ManifestConflictError(err.version, expectedVersion)\n }\n throw err\n }\n}\n","/**\n * Keep the persisted `_manifest/schema` record in sync with the\n * per-collection `_schemas/<collection>` source of truth (#941 Task 3).\n *\n * Called from `persisted-schemas/register.ts`'s `persistSchemaIfNeeded`\n * right after a `_schemas/<collection>` write succeeds.\n *\n * ## Reconciling strict-CAS-refuse with the derived nature of the manifest\n *\n * `writer.ts`'s `writeSchemaManifest` is deliberately strict-CAS: a\n * concurrent *direct* edit to the manifest is refused, never retried (AC\n * #1). But this call site is not a direct edit — it is a re-derivation\n * from the source of truth, triggered as a side effect of a `_schemas`\n * write that has ALREADY succeeded. When N collections are declared\n * together (one `openVault()` call registering several `persistJsonSchema:\n * true` collections), their `persistSchemaIfNeeded` calls run CONCURRENTLY\n * — each one's own `_schemas/<collection>` write lands independently, and\n * each then calls this function. A single derive-then-write with no\n * feedback loop is NOT enough here: a call whose `deriveSchemaManifest`\n * snapshot ran before a sibling's `_schemas` write landed would persist a\n * manifest that's missing that sibling forever (nothing re-triggers it) —\n * this was a REAL bug (not a test-isolation artifact — reproduced with 0\n * shared state, purely from concurrent `_schemas` writes racing the derive\n * snapshot; see #941 flake fix commit). `syncSchemaManifest` therefore\n * loops (bounded by {@link MAX_SYNC_ATTEMPTS}):\n *\n * 1. Derive fresh, compare to what's persisted. Already converged → done.\n * 2. Write via strict-CAS. `ManifestConflictError` (another writer moved\n * the manifest first) → loop: re-derive against the NEW state and\n * retry, exactly like a normal optimistic-CAS retry.\n * 3. On a successful write, derive ONE more time. If a sibling's\n * `_schemas` write landed while we were deriving/writing, the state\n * has already moved past what we just wrote — loop again to catch it\n * up. If the re-derive matches what we wrote, we're the last writer\n * and the manifest is provably converged: return.\n *\n * This guarantees that whichever collection's `_schemas` write is the LAST\n * to land (in real wall-clock order — there is always exactly one, even\n * though no single caller knows which) ends up running a sync whose own\n * derive sees every sibling's write already committed, and whose\n * recheck-after-write confirms nothing moved further — so it converges the\n * manifest deterministically, regardless of interleaving. If attempts are\n * exhausted under pathological contention, the loop gives up — with a\n * `console.warn` (see cap-exhaustion note below), not silently — leaving\n * the last successful write in place; the manifest is a derivable cache\n * (not a correctness invariant), so a next schema write or `open()`'s\n * re-derive still converges it, matching how `persistSchemaIfNeeded`\n * already treats its own write failures.\n * Any error OTHER than `ManifestConflictError` (store failure, DEK\n * resolution failure, etc.) is NOT swallowed — it propagates to the\n * caller immediately, matching how `persistSchemaIfNeeded` already treats\n * its own write failures as a visible (not silently discarded) fingerprint\n * failure.\n *\n * ## CRITICAL fix (#941 review): a scoped principal must never write a\n * partial manifest\n *\n * `deriveSchemaManifest` returns `undecodableCollections` for any\n * `_schemas/<collection>` entry this principal's `lookupDEK` couldn't\n * resolve — e.g. a member `grant()`-scoped to one collection can decrypt\n * `_manifest` itself (system-prefixed DEKs propagate to every role) but\n * NOT a sibling data collection's `_schemas/<sibling>` entry. Before this\n * fix, `deriveSchemaManifest` silently treated \"can't decrypt\" the same as\n * \"no derivable content\" and omitted the sibling — so a scoped member's\n * OWN schema declare would derive a manifest missing every collection they\n * can't see, load the real (complete) persisted manifest as a valid CAS\n * token, observe a hash mismatch, and OVERWRITE it — permanently dropping\n * the siblings, silently, ledger-audited as a legitimate migration. The\n * converging recheck loop above made this WORSE, not better: re-deriving\n * from the same restricted view just re-confirms the same partial result\n * and declares it \"stable.\"\n *\n * Fix: if `undecodableCollections` is non-empty, `syncSchemaManifest`\n * `SKIP`s entirely — no read-compare, no write, no audit, no loop. It does\n * NOT try to merge-preserve the undecodable entries from what's currently\n * persisted: an `aggregateHash` computed over entries this principal can't\n * verify would be a hash asserting something it never actually checked,\n * which is worse than not writing at all. This is safe because:\n *\n * - The source of truth, `_schemas/<their-collection>`, was ALREADY\n * updated by their declare — nothing about their own change is lost.\n * - The pod-wide `_manifest` cache simply isn't rewritten by a principal\n * who'd corrupt it by omission — it stays whatever a full-visibility\n * principal (owner/admin/custodian) last wrote.\n * - `open()` re-derives from `_schemas` directly (not from the cached\n * manifest) for whoever opens it, so a stale cache is never load-bearing.\n * - The NEXT full-visibility principal's declare (or any subsequent sync\n * by someone with complete access) naturally refreshes the cache,\n * including the scoped member's own just-landed change.\n *\n * ## Ledger audit (AC #5)\n *\n * Schema-generation transitions are not ledger-audited anywhere else today\n * (neither the `_schemas/<collection>` write in `register.ts` nor the fence\n * bump in `schema-update/fence-controller.ts#runCutover`). This is the\n * first ledger-audited signal for a schema change: every manifest write\n * that actually happens (i.e. every time the derived manifest differs from\n * what's stored) appends an `op: 'migration'` entry via the optional\n * `getLedgerOrNull` callback — `null`/absent when the history strategy\n * isn't opted in, matching every other optional-ledger call site\n * (`vault._getLedgerOrNull()?.append(...)`, see `liberate.ts`,\n * `with-audit/periods/vault-facade.ts`). No audit fires on the no-op path\n * (derived manifest unchanged), on a swallowed conflict (the winning\n * writer's own sync already audits its write), or on the scoped-principal\n * SKIP path above (nothing was written).\n *\n * @module\n */\n\nimport type { LedgerStore } from '../../with-commit/history/ledger/store.js'\nimport { envelopePayloadHash } from '../../with-commit/history/ledger/hash.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport { ManifestConflictError } from '../../kernel/errors.js'\nimport { deriveSchemaManifest, type LookupDEK } from './derive.js'\nimport { loadSchemaManifestEntry, type GetManifestDEK } from './storage.js'\nimport { writeSchemaManifest } from './writer.js'\nimport { MANIFEST_COLLECTION } from './reserved-collections.js'\nimport { MANIFEST_SCHEMA_RECORD_ID, type SchemaManifest } from './types.js'\n\nexport interface ManifestSyncDeps {\n /** Resolver for `_manifest`'s OWN DEK — mints if absent (this principal's declare is about to write it). */\n readonly getDEK: GetManifestDEK\n /**\n * NON-MINTING lookup for every OTHER (sibling data) collection's DEK,\n * used by `deriveSchemaManifest` to build the index — see `derive.ts`'s\n * `LookupDEK` doc for why minting here would be actively harmful.\n */\n readonly lookupDEK: LookupDEK\n /** Optional ledger handle — absent when the history strategy isn't opted in (no-op audit). */\n readonly getLedgerOrNull?: () => LedgerStore | null\n}\n\n/**\n * Bound on the derive→write→recheck loop (see module doc). Headroom above\n * what any realistic `openVault()` batch (a handful of collections) needs —\n * a 40-way concurrent-declare stress repro converged within single digits.\n */\nconst MAX_SYNC_ATTEMPTS = 16\n\n/**\n * Re-derive the schema manifest and write it if it changed, looping until\n * the write is provably stable against concurrent sibling `_schemas`\n * writes (see module doc). SKIPS entirely (no write, no audit) when this\n * principal's view is incomplete (`undecodableCollections` non-empty) —\n * see the module doc's CRITICAL fix section. Swallows a\n * {@link ManifestConflictError} by retrying against the fresher state; any\n * other error propagates. Ledger-audits (`op: 'migration'`) once per\n * successful write.\n */\nexport async function syncSchemaManifest(\n store: NoydbStore,\n vault: string,\n deps: ManifestSyncDeps,\n): Promise<void> {\n for (let attempt = 0; attempt < MAX_SYNC_ATTEMPTS; attempt++) {\n const { manifest: derived, undecodableCollections } = await deriveSchemaManifest(store, vault, deps.lookupDEK)\n if (undecodableCollections.length > 0) {\n // Scoped principal — see module doc's CRITICAL fix. Never write a\n // manifest we know is missing collections we couldn't decrypt.\n return\n }\n\n const existing = await loadSchemaManifestEntry(store, vault, deps.getDEK)\n\n if (\n existing &&\n existing.manifest.generation === derived.generation &&\n existing.manifest.aggregateHash === derived.aggregateHash\n ) {\n return // already converged — no write, no audit\n }\n\n const expectedVersion = existing?.version ?? 0\n try {\n await writeSchemaManifest(store, vault, derived, expectedVersion, deps.getDEK)\n } catch (err) {\n if (err instanceof ManifestConflictError) continue // another writer moved it first — re-derive + retry\n throw err\n }\n\n await auditManifestWrite(store, vault, deps, derived, expectedVersion)\n\n // A sibling's `_schemas` write can land while WE were deriving/writing\n // (#941 flake fix — see module doc). Re-derive once more: if the truth\n // has already moved past what we just wrote, loop again to catch it up\n // instead of leaving a permanently-partial manifest. (A principal with\n // full visibility at the top of this iteration cannot lose that\n // visibility mid-call — the keyring doesn't change during one sync —\n // so this recheck is never itself subject to the undecodable-skip; the\n // `for` loop's own guard covers it defensively regardless.)\n const recheck = await deriveSchemaManifest(store, vault, deps.lookupDEK)\n if (recheck.undecodableCollections.length > 0) return // defensive — see comment above\n if (recheck.manifest.generation === derived.generation && recheck.manifest.aggregateHash === derived.aggregateHash) {\n return // stable — we were the last writer\n }\n }\n // Attempts exhausted under pathological contention — see module doc:\n // the manifest is a derivable cache, not a correctness invariant, so a\n // later schema write or open()'s re-derive still converges it. Warn\n // rather than fail silently — a silent give-up on a completeness\n // invariant is exactly what hid the original concurrent-declare race.\n console.warn(\n `[noy-db] schema-manifest sync did not converge after ${MAX_SYNC_ATTEMPTS} attempts; ` +\n 'the persisted manifest may be stale until the next schema write or open()\\'s re-derive.',\n )\n}\n\nasync function auditManifestWrite(\n store: NoydbStore,\n vault: string,\n deps: ManifestSyncDeps,\n derived: SchemaManifest,\n expectedVersion: number,\n): Promise<void> {\n const ledger = deps.getLedgerOrNull?.()\n if (!ledger) return\n // `_manifest/schema` has a real, individually-addressable envelope (unlike\n // a `lifecycle` entry's empty collection/id) — `verifyBackupIntegrity`'s\n // data cross-check (`with-pod/backup.ts`) treats any non-amendment,\n // non-lifecycle entry's `collection`/`id` as \"the latest write to this\n // record\" and recomputes+compares its ciphertext hash, so `payloadHash`\n // must be the real `envelopePayloadHash` of the just-written envelope\n // (ciphertext domain), NOT `derived.aggregateHash` (plaintext domain) —\n // using the wrong domain here trips a false-positive tamper failure on\n // restore.\n const envelope = await store.get(vault, MANIFEST_COLLECTION, MANIFEST_SCHEMA_RECORD_ID)\n await ledger.append({\n op: 'migration',\n collection: MANIFEST_COLLECTION,\n id: MANIFEST_SCHEMA_RECORD_ID,\n version: expectedVersion + 1,\n actor: '',\n payloadHash: await envelopePayloadHash(envelope),\n reason: `schema-manifest-sync:generation=${derived.generation}`,\n })\n}\n","/**\n * Orchestrate the derive → hash → skip-or-write cycle for a collection's\n * persisted JSON Schema. Called by the Vault at collection-registration\n * time when the developer opts in via `collection({ persistJsonSchema:\n * true })`.\n *\n * Skip semantics:\n *\n * - Zod validators: skip when the new hash equals the stored hash.\n * - Non-Zod (stub envelopes have hash=null): skip when the stored\n * envelope's `kind` matches the freshly-detected kind (since there's\n * no body to compare yet — a kind change is the only signal).\n *\n * @module\n */\n\nimport { derivePersistedSchema } from './derive.js'\nimport { loadPersistedSchemaEntry, savePersistedSchema } from './storage.js'\nimport { resolveFieldIds } from './field-ids.js'\nimport { computeSchemaDelta } from '../schema-update/delta.js'\nimport { evaluateStrategies } from '../schema-update/dispatch.js'\nimport { loadFence, saveFence } from '../schema-update/fence.js'\nimport { isConflictError } from '../../kernel/errors.js'\nimport { syncSchemaManifest, type ManifestSyncDeps } from '../manifest/sync.js'\nimport type { SchemaUpdateStrategy, UpdateDecision, SchemaDelta } from '../schema-update/types.js'\nimport type { NoydbStore, ClassifiedMarker } from '../../kernel/types.js'\nimport type { PersistedSchemaEnvelope } from './types.js'\nimport type { PairingMarker } from '../satellites/types.js'\nimport type { EnclaveKey } from '../../capsule/index.js'\n\n/**\n * Max optimistic-CAS retries for the shared `_schemas/<collection>` record\n * (#583). Only two writers ever contend (the JSON-Schema writer and the\n * classified-marker writer), so one retry suffices; the headroom covers a\n * pathological re-order. Exhausting it rethrows the {@link ConflictError}\n * rather than silently losing a write.\n */\nconst MAX_SCHEMA_CAS_RETRIES = 5\n\nexport interface PersistSchemaResult {\n /** True when a fresh envelope was written to storage. */\n readonly written: boolean\n /** True when an existing envelope matched and the write was skipped. */\n readonly skipped: boolean\n /** The envelope that was either written or matched. */\n readonly envelope: PersistedSchemaEnvelope\n /** The update-strategy decision, present when strategies ran. */\n readonly decision?: UpdateDecision\n}\n\nexport async function persistSchemaIfNeeded(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly validator: unknown\n readonly dek: EnclaveKey\n readonly strategies?: readonly SchemaUpdateStrategy[]\n /** #941 Task 3: re-derive + sync the `_manifest/schema` INDEX after a write. Omitted → no sync (e.g. tests that don't care). */\n readonly manifestSync?: ManifestSyncDeps\n}): Promise<PersistSchemaResult> {\n const fresh = await derivePersistedSchema(opts.validator)\n\n // The `_schemas/<collection>` record is shared with the classified-marker\n // writer, so a plain load→save loses whichever writer put first (#583). Read\n // the version at load time and CAS on it; on conflict re-read (picking up the\n // other writer's field), re-evaluate, and retry.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n\n if (stored && isEquivalent(stored, fresh)) {\n return { written: false, skipped: true, envelope: stored, decision: { action: 'allow' } }\n }\n\n // Changed (or first registration). Compute the delta whenever we have a\n // comparable JSON-Schema baseline — independent of whether strategies\n // were registered (#946: the rename pairing it carries feeds the\n // fieldIds id-carry below even on a bare re-declare with no\n // `schemaUpdate` strategies configured). Strategies only run when\n // registered.\n let decision: UpdateDecision = { action: 'allow' }\n const strategies = opts.strategies ?? []\n let delta: SchemaDelta | undefined\n if (\n stored &&\n stored.kind === fresh.kind &&\n isPlainObject(stored.jsonSchema) &&\n isPlainObject(fresh.jsonSchema)\n ) {\n delta = computeSchemaDelta(stored.jsonSchema, fresh.jsonSchema, opts.collectionName)\n if (strategies.length > 0) {\n decision = await evaluateStrategies(delta, strategies, { collection: opts.collectionName })\n }\n }\n\n if (decision.action !== 'allow') {\n // reject (or cutover): do NOT overwrite the baseline — the\n // old schema stays the source of truth until the change is resolved.\n return { written: false, skipped: false, envelope: stored ?? fresh, decision }\n }\n\n // Preserve a previously-persisted classified marker (C-A / R10) and\n // satellite pairing marker (R-S9) — the schema derivation knows nothing\n // about them, so a naive overwrite here would drop the config-drift\n // guards' cross-session signals.\n let toSave: PersistedSchemaEnvelope = fresh\n if (stored?.classified !== undefined) toSave = { ...toSave, classified: stored.classified }\n if (stored?.satellite !== undefined) toSave = { ...toSave, satellite: stored.satellite }\n\n // #946: mint/preserve stable per-field ids (by name, carrying a\n // detected rename's id from its old name — see `delta.renamed`) and\n // stamp the vault-wide schema-fence generation this write happened at —\n // binds \"generation N\" to this envelope's content hash\n // (schemaFenceState() + loadPersistedSchema agree).\n const fieldIds = resolveFieldIds(fresh.jsonSchema, stored?.fieldIds, delta?.renamed)\n if (fieldIds !== undefined) toSave = { ...toSave, fieldIds }\n const fence = await loadFence(opts.store, opts.vault)\n toSave = { ...toSave, generation: fence.currentSchemaVersion }\n\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, toSave, version)\n if (toSave.hash !== null) {\n // This is the FIRST non-barrier writer to `_meta/schema-fence` —\n // previously only SchemaFenceController.#setState wrote it, and only\n // sequentially, under the drain barrier. There is no CAS on the fence\n // doc (out of scope to add here), so re-read it immediately before\n // writing rather than reusing the `fence` snapshot captured above\n // (before the potentially-slow `savePersistedSchema` call): spreading\n // a stale snapshot here would roll back `currentSchemaVersion`/\n // `fenceState` if a concurrent cutover (on a different collection)\n // advanced the fence in between — corrupting the vault-wide gate the\n // whole barrier + MigrationRequiredError check depends on. Re-reading\n // narrows (does not eliminate) that window to just this one overlay.\n const freshFence = await loadFence(opts.store, opts.vault)\n await saveFence(opts.store, opts.vault, { ...freshFence, schemaHash: toSave.hash })\n }\n // #941 Task 3: keep the `_manifest/schema` INDEX in sync with the\n // `_schemas/<collection>` write that just landed. Opt-in via\n // `opts.manifestSync` (threaded from vault.ts, which is the only\n // caller with a general-purpose `getDEK` resolver + ledger handle).\n if (opts.manifestSync) {\n await syncSchemaManifest(opts.store, opts.vault, opts.manifestSync)\n }\n return { written: true, skipped: false, envelope: toSave, decision }\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n/**\n * Persist (or refresh) the C-A / R10 classified marker into the collection's\n * `_schemas/<collection>` record, preserving any existing derived JSON-Schema\n * body. Idempotent — a no-op when an equivalent marker is already stored.\n * Independent of `persistJsonSchema`: called on the first classified write so a\n * later naive handle can detect the drift cross-session.\n */\nexport async function persistClassifiedMarker(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly dek: EnclaveKey\n readonly marker: ClassifiedMarker\n}): Promise<void> {\n // Shares the `_schemas/<collection>` record with the JSON-Schema writer;\n // CAS on the loaded version so a concurrent schema (re)registration can't\n // silently drop the marker (and vice-versa) — see #583.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n if (stored?.classified !== undefined && markersEqual(stored.classified, opts.marker)) return\n const payload: PersistedSchemaEnvelope =\n stored !== undefined\n ? { ...stored, classified: opts.marker }\n : {\n _noydb_schema: 1,\n kind: 'Unknown',\n jsonSchema: null,\n hash: null,\n derivedAt: new Date().toISOString(),\n classified: opts.marker,\n }\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, payload, version)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n/**\n * Persist (or refresh) the R-S9 satellite pairing marker into the collection's\n * `_schemas/<collection>` record, preserving any existing derived JSON-Schema\n * body. Idempotent — a no-op when an equivalent marker is already stored.\n * Independent of `persistJsonSchema`: called on satellite declaration so a\n * later divergent re-declaration can be detected cross-session.\n */\nexport async function persistSatelliteMarker(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly dek: EnclaveKey\n readonly marker: PairingMarker\n}): Promise<void> {\n // Shares the `_schemas/<collection>` record with the JSON-Schema writer;\n // CAS on the loaded version so a concurrent schema (re)registration can't\n // silently drop the marker (and vice-versa) — see #583.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n if (stored?.satellite !== undefined && satelliteMarkersEqual(stored.satellite, opts.marker)) return\n const payload: PersistedSchemaEnvelope =\n stored !== undefined\n ? { ...stored, satellite: opts.marker }\n : {\n _noydb_schema: 1,\n kind: 'Unknown',\n jsonSchema: null,\n hash: null,\n derivedAt: new Date().toISOString(),\n satellite: opts.marker,\n }\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, payload, version)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n// #597: both equality checks below deliberately do NOT compare `epoch`. A\n// live collection re-opening itself computes a fresh epoch candidate on\n// every declare (marker.ts / config-drift.ts) that must still be treated as\n// \"the same marker\" so the no-op fast path fires and the already-persisted\n// epoch is left untouched — that's what makes epoch stable across re-opens.\n// Comparing epoch here would break that fast path today, with no\n// corresponding benefit: there is no delete-collection API yet, so a\n// mismatched epoch can't (yet) mean \"this name was reused for a genuinely\n// new collection.\" Wiring an epoch-mismatch REJECTION is a deferred\n// follow-up for whenever collection deletion ships.\n\nfunction satelliteMarkersEqual(a: PairingMarker, b: PairingMarker): boolean {\n return a.base === b.base && a.fieldsHash === b.fieldsHash && (a.joined ?? null) === (b.joined ?? null)\n}\n\nfunction markersEqual(a: ClassifiedMarker, b: ClassifiedMarker): boolean {\n return sameSet(a.digestOnly, b.digestOnly) && sameSet(a.equatable, b.equatable)\n}\n\nfunction sameSet(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false\n const s = new Set(a)\n return b.every((x) => s.has(x))\n}\n\nfunction isPlainObject(v: unknown): v is object {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction isEquivalent(a: PersistedSchemaEnvelope, b: PersistedSchemaEnvelope): boolean {\n if (a.kind !== b.kind) return false\n // Zod path: real hashes — compare directly.\n if (a.hash && b.hash) return a.hash === b.hash\n // Stub path: both have hash=null. Kind equality is the only signal we have.\n if (a.hash === null && b.hash === null) return true\n // Mixed (one has a hash, the other doesn't) — treat as changed.\n return false\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,IAAM,qBAAqB;AAG3B,SAAS,gBAAgB,OAA2B;AAClD,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO;AACxB,YAAS,SAAS,IAAK;AACvB,YAAQ;AACR,WAAO,QAAQ,GAAG;AAChB,aAAO,mBAAoB,UAAW,OAAO,IAAM,EAAE;AACrD,cAAQ;AAAA,IACV;AAAA,EACF;AACA,MAAI,OAAO,GAAG;AACZ,WAAO,mBAAoB,SAAU,IAAI,OAAS,EAAE;AAAA,EACtD;AACA,SAAO;AACT;AAQA,SAAS,cAAsB;AAC7B,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,gBAAgB,KAAK;AAC5B,SAAO,gBAAgB,KAAK;AAC9B;AAGA,SAAS,mBAAmB,YAA8C;AACxE,MAAI,eAAe,KAAM,QAAO,CAAC;AACjC,QAAM,QAAS,WAAwC;AACvD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO,CAAC;AACzD,SAAO,OAAO,KAAK,KAAgC;AACrD;AAiBO,SAAS,gBACd,iBACA,eACA,SACoC;AACpC,QAAM,QAAQ,mBAAmB,eAAe;AAChD,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,eAAe,oBAAI,IAAoB;AAC7C,MAAI,QAAS,YAAW,EAAE,MAAM,GAAG,KAAK,QAAS,cAAa,IAAI,IAAI,IAAI;AAC1E,QAAM,MAA8B,CAAC;AACrC,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,aAAa,IAAI,IAAI,KAAK;AAC5C,QAAI,IAAI,IAAI,gBAAgB,SAAS,KAAK,YAAY;AAAA,EACxD;AACA,SAAO;AACT;;;AChDA,eAAsB,oBACpB,OACA,OACA,UACA,iBACA,QACe;AACf,MAAI;AACF,UAAM,mBAAmB,OAAO,OAAO,UAAU,iBAAiB,MAAM;AAAA,EAC1E,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,YAAM,IAAI,sBAAsB,IAAI,SAAS,eAAe;AAAA,IAC9D;AACA,UAAM;AAAA,EACR;AACF;;;ACwFA,IAAM,oBAAoB;AAY1B,eAAsB,mBACpB,OACA,OACA,MACe;AACf,WAAS,UAAU,GAAG,UAAU,mBAAmB,WAAW;AAC5D,UAAM,EAAE,UAAU,SAAS,uBAAuB,IAAI,MAAM,qBAAqB,OAAO,OAAO,KAAK,SAAS;AAC7G,QAAI,uBAAuB,SAAS,GAAG;AAGrC;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,wBAAwB,OAAO,OAAO,KAAK,MAAM;AAExE,QACE,YACA,SAAS,SAAS,eAAe,QAAQ,cACzC,SAAS,SAAS,kBAAkB,QAAQ,eAC5C;AACA;AAAA,IACF;AAEA,UAAM,kBAAkB,UAAU,WAAW;AAC7C,QAAI;AACF,YAAM,oBAAoB,OAAO,OAAO,SAAS,iBAAiB,KAAK,MAAM;AAAA,IAC/E,SAAS,KAAK;AACZ,UAAI,eAAe,sBAAuB;AAC1C,YAAM;AAAA,IACR;AAEA,UAAM,mBAAmB,OAAO,OAAO,MAAM,SAAS,eAAe;AAUrE,UAAM,UAAU,MAAM,qBAAqB,OAAO,OAAO,KAAK,SAAS;AACvE,QAAI,QAAQ,uBAAuB,SAAS,EAAG;AAC/C,QAAI,QAAQ,SAAS,eAAe,QAAQ,cAAc,QAAQ,SAAS,kBAAkB,QAAQ,eAAe;AAClH;AAAA,IACF;AAAA,EACF;AAMA,UAAQ;AAAA,IACN,wDAAwD,iBAAiB;AAAA,EAE3E;AACF;AAEA,eAAe,mBACb,OACA,OACA,MACA,SACA,iBACe;AACf,QAAM,SAAS,KAAK,kBAAkB;AACtC,MAAI,CAAC,OAAQ;AAUb,QAAM,WAAW,MAAM,MAAM,IAAI,OAAO,qBAAqB,yBAAyB;AACtF,QAAM,OAAO,OAAO;AAAA,IAClB,IAAI;AAAA,IACJ,YAAY;AAAA,IACZ,IAAI;AAAA,IACJ,SAAS,kBAAkB;AAAA,IAC3B,OAAO;AAAA,IACP,aAAa,MAAM,oBAAoB,QAAQ;AAAA,IAC/C,QAAQ,mCAAmC,QAAQ,UAAU;AAAA,EAC/D,CAAC;AACH;;;ACtMA,IAAM,yBAAyB;AAa/B,eAAsB,sBAAsB,MASX;AAC/B,QAAM,QAAQ,MAAM,sBAAsB,KAAK,SAAS;AAMxD,WAAS,UAAU,KAAK,WAAW;AACjC,UAAM,EAAE,SAAS,SAAS,OAAO,IAAI,MAAM;AAAA,MACzC,KAAK;AAAA,MAAO,KAAK;AAAA,MAAO,KAAK;AAAA,MAAgB,KAAK;AAAA,IACpD;AAEA,QAAI,UAAU,aAAa,QAAQ,KAAK,GAAG;AACzC,aAAO,EAAE,SAAS,OAAO,SAAS,MAAM,UAAU,QAAQ,UAAU,EAAE,QAAQ,QAAQ,EAAE;AAAA,IAC1F;AAQA,QAAI,WAA2B,EAAE,QAAQ,QAAQ;AACjD,UAAM,aAAa,KAAK,cAAc,CAAC;AACvC,QAAI;AACJ,QACE,UACA,OAAO,SAAS,MAAM,QACtB,cAAc,OAAO,UAAU,KAC/B,cAAc,MAAM,UAAU,GAC9B;AACA,cAAQ,mBAAmB,OAAO,YAAY,MAAM,YAAY,KAAK,cAAc;AACnF,UAAI,WAAW,SAAS,GAAG;AACzB,mBAAW,MAAM,mBAAmB,OAAO,YAAY,EAAE,YAAY,KAAK,eAAe,CAAC;AAAA,MAC5F;AAAA,IACF;AAEA,QAAI,SAAS,WAAW,SAAS;AAG/B,aAAO,EAAE,SAAS,OAAO,SAAS,OAAO,UAAU,UAAU,OAAO,SAAS;AAAA,IAC/E;AAMA,QAAI,SAAkC;AACtC,QAAI,QAAQ,eAAe,OAAW,UAAS,EAAE,GAAG,QAAQ,YAAY,OAAO,WAAW;AAC1F,QAAI,QAAQ,cAAc,OAAW,UAAS,EAAE,GAAG,QAAQ,WAAW,OAAO,UAAU;AAOvF,UAAM,WAAW,gBAAgB,MAAM,YAAY,QAAQ,UAAU,OAAO,OAAO;AACnF,QAAI,aAAa,OAAW,UAAS,EAAE,GAAG,QAAQ,SAAS;AAC3D,UAAM,QAAQ,MAAM,UAAU,KAAK,OAAO,KAAK,KAAK;AACpD,aAAS,EAAE,GAAG,QAAQ,YAAY,MAAM,qBAAqB;AAE7D,QAAI;AACF,YAAM,oBAAoB,KAAK,OAAO,KAAK,OAAO,KAAK,gBAAgB,KAAK,KAAK,QAAQ,OAAO;AAChG,UAAI,OAAO,SAAS,MAAM;AAYxB,cAAM,aAAa,MAAM,UAAU,KAAK,OAAO,KAAK,KAAK;AACzD,cAAM,UAAU,KAAK,OAAO,KAAK,OAAO,EAAE,GAAG,YAAY,YAAY,OAAO,KAAK,CAAC;AAAA,MACpF;AAKA,UAAI,KAAK,cAAc;AACrB,cAAM,mBAAmB,KAAK,OAAO,KAAK,OAAO,KAAK,YAAY;AAAA,MACpE;AACA,aAAO,EAAE,SAAS,MAAM,SAAS,OAAO,UAAU,QAAQ,SAAS;AAAA,IACrE,SAAS,KAAK;AACZ,UAAI,gBAAgB,GAAG,KAAK,UAAU,uBAAwB;AAC9D,YAAM;AAAA,IACR;AAAA,EACF;AACF;AASA,eAAsB,wBAAwB,MAM5B;AAIhB,WAAS,UAAU,KAAK,WAAW;AACjC,UAAM,EAAE,SAAS,SAAS,OAAO,IAAI,MAAM;AAAA,MACzC,KAAK;AAAA,MAAO,KAAK;AAAA,MAAO,KAAK;AAAA,MAAgB,KAAK;AAAA,IACpD;AACA,QAAI,QAAQ,eAAe,UAAa,aAAa,OAAO,YAAY,KAAK,MAAM,EAAG;AACtF,UAAM,UACJ,WAAW,SACP,EAAE,GAAG,QAAQ,YAAY,KAAK,OAAO,IACrC;AAAA,MACE,eAAe;AAAA,MACf,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,YAAY,KAAK;AAAA,IACnB;AACN,QAAI;AACF,YAAM,oBAAoB,KAAK,OAAO,KAAK,OAAO,KAAK,gBAAgB,KAAK,KAAK,SAAS,OAAO;AACjG;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,gBAAgB,GAAG,KAAK,UAAU,uBAAwB;AAC9D,YAAM;AAAA,IACR;AAAA,EACF;AACF;AASA,eAAsB,uBAAuB,MAM3B;AAIhB,WAAS,UAAU,KAAK,WAAW;AACjC,UAAM,EAAE,SAAS,SAAS,OAAO,IAAI,MAAM;AAAA,MACzC,KAAK;AAAA,MAAO,KAAK;AAAA,MAAO,KAAK;AAAA,MAAgB,KAAK;AAAA,IACpD;AACA,QAAI,QAAQ,cAAc,UAAa,sBAAsB,OAAO,WAAW,KAAK,MAAM,EAAG;AAC7F,UAAM,UACJ,WAAW,SACP,EAAE,GAAG,QAAQ,WAAW,KAAK,OAAO,IACpC;AAAA,MACE,eAAe;AAAA,MACf,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,WAAW,KAAK;AAAA,IAClB;AACN,QAAI;AACF,YAAM,oBAAoB,KAAK,OAAO,KAAK,OAAO,KAAK,gBAAgB,KAAK,KAAK,SAAS,OAAO;AACjG;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,gBAAgB,GAAG,KAAK,UAAU,uBAAwB;AAC9D,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAaA,SAAS,sBAAsB,GAAkB,GAA2B;AAC1E,SAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,WAAW,EAAE,UAAU;AACnG;AAEA,SAAS,aAAa,GAAqB,GAA8B;AACvE,SAAO,QAAQ,EAAE,YAAY,EAAE,UAAU,KAAK,QAAQ,EAAE,WAAW,EAAE,SAAS;AAChF;AAEA,SAAS,QAAQ,GAAsB,GAA+B;AACpE,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,QAAM,IAAI,IAAI,IAAI,CAAC;AACnB,SAAO,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAChC;AAEA,SAAS,cAAc,GAAyB;AAC9C,SAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC;AAChE;AAEA,SAAS,aAAa,GAA4B,GAAqC;AACrF,MAAI,EAAE,SAAS,EAAE,KAAM,QAAO;AAE9B,MAAI,EAAE,QAAQ,EAAE,KAAM,QAAO,EAAE,SAAS,EAAE;AAE1C,MAAI,EAAE,SAAS,QAAQ,EAAE,SAAS,KAAM,QAAO;AAE/C,SAAO;AACT;","names":[]}
@@ -1,20 +1,16 @@
1
1
  import {
2
2
  linkCollectionName,
3
3
  linkRowKey
4
- } from "./chunk-AOWGXU6D.js";
4
+ } from "./chunk-Q4UHJ5SN.js";
5
5
  import {
6
6
  buildRecordAad,
7
+ buildRecordEnvelope,
8
+ encrypt,
7
9
  openEnvelopeJson
8
- } from "./chunk-KJFW62P4.js";
9
- import {
10
- buildRecordEnvelope
11
- } from "./chunk-SEE332EV.js";
12
- import {
13
- encrypt
14
- } from "./chunk-UYB234ZR.js";
10
+ } from "./chunk-EZGV6F47.js";
15
11
  import {
16
12
  NoydbError
17
- } from "./chunk-244VXYPG.js";
13
+ } from "./chunk-37EPTBBR.js";
18
14
 
19
15
  // src/with-shape/links/link-set.ts
20
16
  var LinkSet = class {
@@ -55,7 +51,7 @@ var LinkSet = class {
55
51
  return buildRecordEnvelope(identity, { iv, data });
56
52
  }
57
53
  async decryptEntry(key, env) {
58
- const json = this.encrypted ? await openEnvelopeJson({ collection: this.collName, id: key }, env, await this.dek()) : env._data;
54
+ const json = this.encrypted ? await openEnvelopeJson({ collection: this.collName, id: key }, env, await this.dek()) : env._data ?? "";
59
55
  return JSON.parse(json);
60
56
  }
61
57
  async connect(aId, bId, meta) {
@@ -130,4 +126,4 @@ export {
130
126
  LinkSet,
131
127
  LinkEndpointError
132
128
  };
133
- //# sourceMappingURL=chunk-3F7GLNAE.js.map
129
+ //# sourceMappingURL=chunk-WXE4W2U6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-shape/links/link-set.ts"],"sourcesContent":["/**\n * `_links_*` reserved collections — managed bidirectional many-to-many\n * junctions (design B).\n *\n * Where `refArray` (design A) stores an id-array on one owning record,\n * `vault.link()` creates a first-class junction: a dedicated encrypted\n * `_links_<name>` collection whose rows are `{ a, b, meta? }` link tuples.\n * It is queryable from BOTH sides (`of(id)`), carries optional per-link\n * metadata, and cascades on endpoint delete.\n *\n * Each link is slot-typed: `a` is an id in the `a` endpoint collection,\n * `b` an id in the `b` endpoint collection (they may be the same\n * collection for self-links). A link's identity is the ordered pair\n * `(a, b)`; `of(id)` matches either slot.\n *\n * Rows are encrypted under a dedicated `_links_<name>` DEK — same\n * zero-knowledge stack as every other collection (the store sees only\n * ciphertext). Backup/restore rides the normal `loadAll` path.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport type { NoydbEventEmitter } from '../../kernel/events.js'\nimport { buildRecordAad, buildRecordEnvelope, encrypt, openEnvelopeJson, type EnclaveKey } from '../../capsule/index.js'\nimport { NoydbError } from '../../kernel/errors.js'\n\n// Naming helpers, declaration types, and LinkIntegrityError live in\n// `names.ts` (the always-loadable slice — #553); re-exported here so\n// existing import paths and the root barrel are unchanged.\nexport {\n LINK_COLLECTION_PREFIX,\n linkCollectionName,\n isLinkCollectionName,\n linkRowKey,\n LinkIntegrityError,\n} from './names.js'\nexport type { LinkOnDelete, LinkSpec, LinkRow, LinkSetHandle } from './names.js'\nimport { linkCollectionName, linkRowKey } from './names.js'\nimport type { LinkSpec, LinkRow, LinkSetHandle } from './names.js'\n\n/** Stored form (also the row key derivation source). */\ninterface LinkEntry {\n a: string\n b: string\n meta?: Record<string, unknown>\n}\n\n/**\n * @internal — the concrete handle. The Vault owns construction (one per\n * link name) and the cascade hooks; consumers use the {@link LinkSetHandle}\n * surface via `vault.links(name)`.\n */\nexport class LinkSet implements LinkSetHandle {\n private readonly collName: string\n private dekPromise: Promise<EnclaveKey> | null = null\n\n constructor(\n private readonly adapter: NoydbStore,\n private readonly vault: string,\n private readonly name: string,\n private readonly spec: LinkSpec,\n private readonly encrypted: boolean,\n private readonly getDEK: (collectionName: string) => Promise<EnclaveKey>,\n private readonly actor: string,\n private readonly emitter: NoydbEventEmitter,\n /** Vault-provided existence check for endpoint validation on connect(). */\n private readonly endpointExists: (collection: string, id: string) => Promise<boolean>,\n ) {\n this.collName = linkCollectionName(name)\n }\n\n private dek(): Promise<EnclaveKey> {\n if (!this.dekPromise) this.dekPromise = this.getDEK(this.collName)\n return this.dekPromise\n }\n\n private async encryptEntry(entry: LinkEntry, version: number, key: string): Promise<EncryptedEnvelope> {\n const json = JSON.stringify(entry)\n const identity = { collection: this.collName, id: key, by: this.actor, version }\n if (!this.encrypted) {\n return buildRecordEnvelope(identity, { iv: '', data: json})\n }\n const { iv, data } = await encrypt(json, await this.dek(), buildRecordAad(identity))\n return buildRecordEnvelope(identity, { iv, data})\n }\n\n private async decryptEntry(key: string, env: EncryptedEnvelope): Promise<LinkEntry> {\n const json = this.encrypted ? await openEnvelopeJson({ collection: this.collName, id: key }, env, await this.dek()) : (env._data ?? '')\n return JSON.parse(json) as LinkEntry\n }\n\n async connect(aId: string, bId: string, meta?: Record<string, unknown>): Promise<void> {\n if (!(await this.endpointExists(this.spec.a, aId))) {\n throw new LinkEndpointError(this.name, this.spec.a, aId)\n }\n if (!(await this.endpointExists(this.spec.b, bId))) {\n throw new LinkEndpointError(this.name, this.spec.b, bId)\n }\n const key = linkRowKey(aId, bId)\n const entry: LinkEntry = meta !== undefined ? { a: aId, b: bId, meta } : { a: aId, b: bId }\n const existing = await this.adapter.get(this.vault, this.collName, key)\n const env = await this.encryptEntry(entry, (existing?._v ?? 0) + 1, key)\n await this.adapter.put(this.vault, this.collName, key, env, existing?._v)\n this.emitter.emit('change', { vault: this.vault, collection: this.collName, id: key, action: 'put' })\n }\n\n async disconnect(aId: string, bId: string): Promise<void> {\n const key = linkRowKey(aId, bId)\n const existing = await this.adapter.get(this.vault, this.collName, key)\n if (!existing) return\n await this.adapter.delete(this.vault, this.collName, key)\n this.emitter.emit('change', { vault: this.vault, collection: this.collName, id: key, action: 'delete' })\n }\n\n async has(aId: string, bId: string): Promise<boolean> {\n return (await this.adapter.get(this.vault, this.collName, linkRowKey(aId, bId))) !== null\n }\n\n async of(id: string): Promise<LinkRow[]> {\n const rows = await this.list()\n return rows.filter((r) => r.a === id || r.b === id)\n }\n\n async list(): Promise<LinkRow[]> {\n const keys = await this.adapter.list(this.vault, this.collName)\n const out: LinkRow[] = []\n for (const key of keys) {\n const env = await this.adapter.get(this.vault, this.collName, key)\n if (!env) continue\n const e = await this.decryptEntry(key, env)\n out.push(e.meta !== undefined ? { a: e.a, b: e.b, meta: e.meta } : { a: e.a, b: e.b })\n }\n return out\n }\n\n // ── Vault-internal cascade helpers ──────────────────────────────────\n\n /** @internal — rows where the deleted endpoint id matches the relevant slot. */\n async _rowsTouchingEndpoint(collection: string, id: string): Promise<LinkRow[]> {\n const rows = await this.list()\n return rows.filter(\n (r) => (this.spec.a === collection && r.a === id) || (this.spec.b === collection && r.b === id),\n )\n }\n\n /** @internal — the storage collection name (for tx pre-image capture). */\n get _collectionName(): string {\n return this.collName\n }\n}\n\n/** Thrown by `connect()` when an endpoint record does not exist. */\nexport class LinkEndpointError extends NoydbError {\n readonly link: string\n readonly endpoint: string\n readonly missingId: string\n constructor(link: string, endpoint: string, missingId: string) {\n super(\n 'LINK_ENDPOINT',\n `link(\"${link}\").connect: endpoint \"${endpoint}\" has no record \"${missingId}\".`,\n )\n this.name = 'LinkEndpointError'\n this.link = link\n this.endpoint = endpoint\n this.missingId = missingId\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SatelliteConfigError
3
- } from "./chunk-244VXYPG.js";
3
+ } from "./chunk-37EPTBBR.js";
4
4
 
5
5
  // src/with-shape/satellites/validate.ts
6
6
  function validateSatelliteDeclaration(input) {
@@ -47,4 +47,4 @@ export {
47
47
  validateSatelliteDeclaration,
48
48
  hashFields
49
49
  };
50
- //# sourceMappingURL=chunk-6K7DPM7D.js.map
50
+ //# sourceMappingURL=chunk-XQRFSKYM.js.map
@@ -5,37 +5,36 @@ import {
5
5
  readDirectoryConfig,
6
6
  readUserVisibility,
7
7
  saveUserEnvelope
8
- } from "./chunk-7VCXZDA4.js";
8
+ } from "./chunk-LIOVX5TJ.js";
9
9
  import {
10
10
  parseDekKey
11
- } from "./chunk-JCN44NCY.js";
12
- import {
13
- envelopeOpensUnderAny,
14
- hasSealedBody,
15
- rekeyBlobSet,
16
- rekeyEnvelopeIfNeeded
17
- } from "./chunk-KJFW62P4.js";
18
- import {
19
- buildRecordEnvelope
20
- } from "./chunk-SEE332EV.js";
11
+ } from "./chunk-FLPCETP6.js";
21
12
  import {
22
13
  base64ToBuffer,
23
14
  bufferToBase64,
15
+ buildRecordEnvelope,
16
+ checkCanary,
24
17
  decrypt,
18
+ decryptBytes,
25
19
  deriveEchoKey,
26
20
  deriveKey,
27
21
  deriveSecretKey,
28
22
  encodeEchoParts,
29
23
  encrypt,
24
+ encryptBytes,
25
+ envelopeOpensUnderAny,
30
26
  generateDEK,
31
- generateIV,
32
27
  generateSalt,
28
+ hasSealedBody,
29
+ mintCanary,
30
+ rekeyBlobSet,
31
+ rekeyEnvelopeIfNeeded,
33
32
  unwrapKey,
34
33
  wrapKey
35
- } from "./chunk-UYB234ZR.js";
34
+ } from "./chunk-EZGV6F47.js";
36
35
  import {
37
36
  NOYDB_KEYRING_VERSION
38
- } from "./chunk-ODVPTDPK.js";
37
+ } from "./chunk-SV2PM2OY.js";
39
38
  import {
40
39
  BLOB_ADDRESS_KEY_ID,
41
40
  DirectoryDisabledError,
@@ -53,7 +52,7 @@ import {
53
52
  USER_ENVELOPE_COLLECTION,
54
53
  ValidationError,
55
54
  WrongPromptError
56
- } from "./chunk-244VXYPG.js";
55
+ } from "./chunk-37EPTBBR.js";
57
56
 
58
57
  // src/with-party/team/roster-epoch.ts
59
58
  function assertRosterEpochCurrent(found, expected, userId) {
@@ -297,19 +296,14 @@ function assertStrongEchoSecret(parts, opts) {
297
296
  // src/with-party/team/echo-secret.ts
298
297
  var ECHO_KDF_ITERATIONS = 6e5;
299
298
  var VERIFIER_PLAINTEXT = new Uint8Array(32).fill(90);
300
- var subtle = globalThis.crypto.subtle;
301
- async function getVerifierKey() {
302
- return subtle.importKey("raw", VERIFIER_PLAINTEXT, { name: "AES-GCM" }, true, ["encrypt"]);
303
- }
304
299
  async function mintVerifier(part, salt) {
305
300
  const kek = await deriveSecretKey(part, salt, { iterations: ECHO_KDF_ITERATIONS, keyUsage: "aes-kw" });
306
- return wrapKey(await getVerifierKey(), kek);
301
+ return mintCanary(kek, VERIFIER_PLAINTEXT);
307
302
  }
308
303
  async function checkVerifier(verifier, part, saltB64) {
309
304
  try {
310
305
  const kek = await deriveSecretKey(part, base64ToBuffer(saltB64), { iterations: ECHO_KDF_ITERATIONS, keyUsage: "aes-kw" });
311
- await subtle.unwrapKey("raw", base64ToBuffer(verifier), kek, "AES-KW", { name: "AES-GCM" }, false, ["encrypt"]);
312
- return true;
306
+ return await checkCanary(verifier, kek);
313
307
  } catch {
314
308
  return false;
315
309
  }
@@ -321,12 +315,9 @@ async function buildEchoBlock(parts, reveal, maskHint) {
321
315
  let revealField;
322
316
  if (reveal.kind === "portable") {
323
317
  const blobSalt = generateSalt();
324
- const iv = generateIV();
325
318
  const gcmKey = await deriveSecretKey(parts.prompt, blobSalt, { iterations: ECHO_KDF_ITERATIONS, keyUsage: "aes-gcm" });
326
- const ct = new Uint8Array(
327
- await subtle.encrypt({ name: "AES-GCM", iv }, gcmKey, new TextEncoder().encode(parts.echo))
328
- );
329
- revealField = { kind: "portable", blob: bufferToBase64(ct), iv: bufferToBase64(iv), salt: bufferToBase64(blobSalt) };
319
+ const { iv, data } = await encryptBytes(new TextEncoder().encode(parts.echo), gcmKey);
320
+ revealField = { kind: "portable", blob: data, iv, salt: bufferToBase64(blobSalt) };
330
321
  } else if (reveal.kind === "sealed") {
331
322
  const sealed = await reveal.deviceSeal.seal(new TextEncoder().encode(parts.echo));
332
323
  revealField = { kind: "sealed", blob: bufferToBase64(sealed), provider_hint: reveal.deviceSeal.id };
@@ -353,11 +344,7 @@ async function resolveEchoReveal(block, prompt, deviceSeal) {
353
344
  iterations: ECHO_KDF_ITERATIONS,
354
345
  keyUsage: "aes-gcm"
355
346
  });
356
- const plain = await subtle.decrypt(
357
- { name: "AES-GCM", iv: base64ToBuffer(block.reveal.iv) },
358
- gcmKey,
359
- base64ToBuffer(block.reveal.blob)
360
- );
347
+ const plain = await decryptBytes(block.reveal.iv, block.reveal.blob, gcmKey);
361
348
  return new TextDecoder().decode(plain);
362
349
  } catch {
363
350
  throw new WrongPromptError();
@@ -408,23 +395,8 @@ function canUpdateRole(callerRole, targetRole) {
408
395
  return false;
409
396
  }
410
397
  var CANARY_PLAINTEXT_BYTES = new Uint8Array(32);
411
- var canaryKeyPromise = null;
412
- function getCanaryKey() {
413
- if (canaryKeyPromise === null) {
414
- canaryKeyPromise = globalThis.crypto.subtle.importKey(
415
- "raw",
416
- CANARY_PLAINTEXT_BYTES,
417
- { name: "AES-GCM", length: 256 },
418
- true,
419
- // extractable so AES-KW can wrap it
420
- ["encrypt", "decrypt"]
421
- );
422
- }
423
- return canaryKeyPromise;
424
- }
425
398
  async function mintKeyringCanary(kek) {
426
- const canaryKey = await getCanaryKey();
427
- return wrapKey(canaryKey, kek);
399
+ return mintCanary(kek, CANARY_PLAINTEXT_BYTES);
428
400
  }
429
401
  async function verifyKeyringCanary(wrappedCanary, kek) {
430
402
  try {
@@ -457,7 +429,7 @@ async function deriveKekForKeyring(file, secret, salt) {
457
429
  return deriveKey(secret, salt);
458
430
  }
459
431
  function parseKeyringEnvelope(envelope) {
460
- return JSON.parse(envelope._data);
432
+ return JSON.parse(envelope._data ?? "");
461
433
  }
462
434
  async function readKeyringFile(store, vault, userId) {
463
435
  const envelope = await store.get(vault, "_keyring", userId);
@@ -1473,4 +1445,4 @@ export {
1473
1445
  hasImportCapability,
1474
1446
  evaluateImportCapability
1475
1447
  };
1476
- //# sourceMappingURL=chunk-M7NI3IWE.js.map
1448
+ //# sourceMappingURL=chunk-XSPFDUDG.js.map