@noy-db/hub 0.8.0-pre.0 → 0.9.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (706) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +8 -8
  3. package/codemods/0.4.0-pre.json +1 -1
  4. package/codemods/0.8.0-pre.json +1 -1
  5. package/dist/api-MSJXVYQG.js +18 -0
  6. package/dist/as/index.js +34 -25
  7. package/dist/as/index.js.map +1 -1
  8. package/dist/at/index.js +9 -10
  9. package/dist/attestation/index.js +39 -28
  10. package/dist/attestation/index.js.map +1 -1
  11. package/dist/{backup-7TFRVFLH.js → backup-7RKNZMQO.js} +15 -16
  12. package/dist/backup-7RKNZMQO.js.map +1 -0
  13. package/dist/blobs/index.js +13 -14
  14. package/dist/blobs/index.js.map +1 -1
  15. package/dist/broker/index.js +20 -13
  16. package/dist/broker/index.js.map +1 -1
  17. package/dist/by/index.js +2 -2
  18. package/dist/capsule/contract.d.ts +159 -0
  19. package/dist/capsule/contract.js +95 -0
  20. package/dist/capsule/enclave-aes/bound.d.ts +52 -0
  21. package/dist/{kernel/enclave → capsule/enclave-aes}/broker/proof.d.ts +12 -3
  22. package/dist/capsule/enclave-aes/capabilities.d.ts +2 -0
  23. package/dist/capsule/enclave-aes/classify/compare.d.ts +20 -0
  24. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/find.d.ts +1 -1
  25. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/reveal.d.ts +1 -1
  26. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/verify.d.ts +2 -2
  27. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/write.d.ts +1 -1
  28. package/dist/{kernel/enclave → capsule/enclave-aes}/crypto.d.ts +33 -129
  29. package/dist/capsule/enclave-aes/index.d.ts +113 -0
  30. package/dist/capsule/enclave-aes/index.js +361 -0
  31. package/dist/capsule/enclave-aes/index.js.map +1 -0
  32. package/dist/capsule/enclave-aes/primitives.d.ts +29 -0
  33. package/dist/capsule/index.d.ts +27 -0
  34. package/dist/capsule/plumbing/ct-equal.d.ts +11 -0
  35. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/deterministic.d.ts +8 -21
  36. package/dist/capsule/plumbing/digest.d.ts +122 -0
  37. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/envelope-body.d.ts +44 -48
  38. package/dist/capsule/plumbing/index.d.ts +100 -0
  39. package/dist/capsule/plumbing/lifecycle.d.ts +50 -0
  40. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/record-codec.d.ts +62 -20
  41. package/dist/{kernel/enclave → capsule/plumbing}/record-envelope.d.ts +1 -1
  42. package/dist/capsule/plumbing/rekey-blob.d.ts +33 -0
  43. package/dist/capsule/plumbing/rekey.d.ts +44 -0
  44. package/dist/capsule/plumbing/sealed-slot.d.ts +22 -0
  45. package/dist/capsule/plumbing/sealed-slots.d.ts +37 -0
  46. package/dist/capsule/plumbing/sealing.d.ts +29 -0
  47. package/dist/capsule/plumbing/sign.d.ts +9 -0
  48. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/tombstone.d.ts +2 -2
  49. package/dist/capsule-57BDK7LG.js +209 -0
  50. package/dist/cargo/index.js +36 -36
  51. package/dist/{chunk-BUDAGWL5.js → chunk-226V6XXJ.js} +41 -6
  52. package/dist/chunk-226V6XXJ.js.map +1 -0
  53. package/dist/{chunk-QWGU66MP.js → chunk-2A56DSRI.js} +5 -7
  54. package/dist/chunk-2A56DSRI.js.map +1 -0
  55. package/dist/chunk-2DX4JCRI.js +1 -0
  56. package/dist/{chunk-ODVPTDPK.js → chunk-2ELOCEXE.js} +1 -1
  57. package/dist/chunk-2ELOCEXE.js.map +1 -0
  58. package/dist/{chunk-CKNGS5MH.js → chunk-2WJKJZSO.js} +3 -5
  59. package/dist/chunk-2WJKJZSO.js.map +1 -0
  60. package/dist/{chunk-ZSX4S5XB.js → chunk-2ZFD2UJC.js} +9 -9
  61. package/dist/{chunk-YSK2CU2Q.js → chunk-3V6LZHUO.js} +3 -3
  62. package/dist/chunk-3V6LZHUO.js.map +1 -0
  63. package/dist/{chunk-HT2AHGQI.js → chunk-3Y5JLSXS.js} +2 -2
  64. package/dist/{chunk-M7NI3IWE.js → chunk-45VHWE4C.js} +67 -60
  65. package/dist/chunk-45VHWE4C.js.map +1 -0
  66. package/dist/{chunk-LGNDFRYC.js → chunk-4CFHL5RY.js} +1 -1
  67. package/dist/chunk-4CFHL5RY.js.map +1 -0
  68. package/dist/{enclave-27SMF6GF.js → chunk-4J3MNVVX.js} +83 -56
  69. package/dist/chunk-4J3MNVVX.js.map +1 -0
  70. package/dist/{chunk-SI3V7D4G.js → chunk-4UIP3QNW.js} +2 -2
  71. package/dist/{chunk-WNJ4AVGE.js → chunk-55P6OBB7.js} +2 -2
  72. package/dist/{chunk-6LNL42F5.js → chunk-5HNW4ZTA.js} +12 -15
  73. package/dist/chunk-5HNW4ZTA.js.map +1 -0
  74. package/dist/{chunk-SYVZW77S.js → chunk-5LMCVBMY.js} +6 -8
  75. package/dist/chunk-5LMCVBMY.js.map +1 -0
  76. package/dist/{chunk-MKXXMNJH.js → chunk-5UUJE4FP.js} +3 -3
  77. package/dist/chunk-5XONHAFD.js +12 -0
  78. package/dist/chunk-5XONHAFD.js.map +1 -0
  79. package/dist/{chunk-X4773RBR.js → chunk-6QLCS7ET.js} +2 -2
  80. package/dist/chunk-6QLCS7ET.js.map +1 -0
  81. package/dist/{chunk-FW22ZCXT.js → chunk-6WS35E6X.js} +2 -2
  82. package/dist/{chunk-FCKWQE2H.js → chunk-6Y5E4SJU.js} +7 -8
  83. package/dist/chunk-6Y5E4SJU.js.map +1 -0
  84. package/dist/{chunk-4AF3TUMP.js → chunk-72XBVN4J.js} +8 -2
  85. package/dist/{chunk-4AF3TUMP.js.map → chunk-72XBVN4J.js.map} +1 -1
  86. package/dist/{chunk-QBBUZ3I3.js → chunk-7BMOS4JQ.js} +11 -11
  87. package/dist/chunk-7BMOS4JQ.js.map +1 -0
  88. package/dist/{chunk-5FDLC6G7.js → chunk-7CT6SX4M.js} +3 -3
  89. package/dist/chunk-7CT6SX4M.js.map +1 -0
  90. package/dist/{chunk-LQ4ZHOKP.js → chunk-ABU6A4Z5.js} +36 -27
  91. package/dist/chunk-ABU6A4Z5.js.map +1 -0
  92. package/dist/{chunk-WX5ADGTZ.js → chunk-AJFPAHMW.js} +3 -3
  93. package/dist/chunk-AJFPAHMW.js.map +1 -0
  94. package/dist/{chunk-ZE4CHIED.js → chunk-APFFJN5T.js} +39 -37
  95. package/dist/chunk-APFFJN5T.js.map +1 -0
  96. package/dist/{chunk-6OWXYZK2.js → chunk-B65CC6Z6.js} +2 -2
  97. package/dist/{chunk-F2TDU7MQ.js → chunk-BJH77UY2.js} +2 -2
  98. package/dist/{chunk-L7DOS3TE.js → chunk-BMHQHH7Q.js} +3 -3
  99. package/dist/chunk-BMHQHH7Q.js.map +1 -0
  100. package/dist/{chunk-BJ2IXQ5Q.js → chunk-BNFNQNEP.js} +2 -2
  101. package/dist/chunk-BU2ABHFW.js +74 -0
  102. package/dist/chunk-BU2ABHFW.js.map +1 -0
  103. package/dist/{chunk-HLEK5VAN.js → chunk-BUE7R3B5.js} +2 -2
  104. package/dist/{chunk-ISZQJBML.js → chunk-BVF3SXD7.js} +6 -6
  105. package/dist/chunk-BVF3SXD7.js.map +1 -0
  106. package/dist/{chunk-M522UYZ2.js → chunk-BYHCKX73.js} +6 -10
  107. package/dist/chunk-BYHCKX73.js.map +1 -0
  108. package/dist/{chunk-EOVMT6TB.js → chunk-C53ER7AG.js} +2 -2
  109. package/dist/{chunk-K6W7BVZ6.js → chunk-CAD7N6HQ.js} +3 -3
  110. package/dist/chunk-CB4467G3.js +565 -0
  111. package/dist/chunk-CB4467G3.js.map +1 -0
  112. package/dist/{chunk-NWHBJQAL.js → chunk-D4V5JJAK.js} +4 -4
  113. package/dist/chunk-D4V5JJAK.js.map +1 -0
  114. package/dist/{chunk-QWFHIOD2.js → chunk-D5A3GCZ4.js} +11 -12
  115. package/dist/chunk-D5A3GCZ4.js.map +1 -0
  116. package/dist/{chunk-5O7D6ZTW.js → chunk-DHU2AQ6T.js} +2 -2
  117. package/dist/{chunk-DC5QVPV7.js → chunk-DL6BZKP3.js} +3 -3
  118. package/dist/chunk-DL6BZKP3.js.map +1 -0
  119. package/dist/{chunk-FQ54DWUQ.js → chunk-EA4VDI5D.js} +4 -4
  120. package/dist/chunk-EA4VDI5D.js.map +1 -0
  121. package/dist/{chunk-IT22PWKV.js → chunk-ECGXA4LT.js} +2 -2
  122. package/dist/{chunk-BP6DARYA.js → chunk-EMAWOGVH.js} +4 -4
  123. package/dist/chunk-EMAWOGVH.js.map +1 -0
  124. package/dist/{chunk-3F7GLNAE.js → chunk-EUD3HQM4.js} +7 -11
  125. package/dist/chunk-EUD3HQM4.js.map +1 -0
  126. package/dist/{chunk-T2LBQ5DV.js → chunk-FSQNGPHH.js} +335 -250
  127. package/dist/chunk-FSQNGPHH.js.map +1 -0
  128. package/dist/{chunk-7VCXZDA4.js → chunk-FZXRLNJO.js} +7 -11
  129. package/dist/chunk-FZXRLNJO.js.map +1 -0
  130. package/dist/{chunk-6LMQ5R26.js → chunk-G46JSSVX.js} +7 -7
  131. package/dist/{chunk-6LMQ5R26.js.map → chunk-G46JSSVX.js.map} +1 -1
  132. package/dist/{chunk-GF5BZ7D4.js → chunk-G5EDYVSE.js} +11 -13
  133. package/dist/chunk-G5EDYVSE.js.map +1 -0
  134. package/dist/chunk-G66YXUTY.js +60 -0
  135. package/dist/chunk-G66YXUTY.js.map +1 -0
  136. package/dist/{chunk-FFWI7UH7.js → chunk-GAGT3YZN.js} +1 -1
  137. package/dist/chunk-GAGT3YZN.js.map +1 -0
  138. package/dist/{chunk-4MXI3ODD.js → chunk-GCUMOKTN.js} +3 -3
  139. package/dist/{chunk-FH2L2E34.js → chunk-GETUTTZU.js} +4 -4
  140. package/dist/{chunk-FH2L2E34.js.map → chunk-GETUTTZU.js.map} +1 -1
  141. package/dist/chunk-GMKGGY7V.js +234 -0
  142. package/dist/chunk-GMKGGY7V.js.map +1 -0
  143. package/dist/{chunk-B6SJIBQK.js → chunk-GOJQ5DZA.js} +4 -4
  144. package/dist/{chunk-EIPVYUEP.js → chunk-GQ4MF3CM.js} +3 -3
  145. package/dist/chunk-GQ4MF3CM.js.map +1 -0
  146. package/dist/{chunk-PDCG6PR5.js → chunk-GRS2HIV6.js} +2 -2
  147. package/dist/chunk-GRS2HIV6.js.map +1 -0
  148. package/dist/{chunk-PEOZ34EM.js → chunk-GSAK56VF.js} +1 -1
  149. package/dist/chunk-GSAK56VF.js.map +1 -0
  150. package/dist/{chunk-3H5KLDZN.js → chunk-H3YM66EI.js} +4 -8
  151. package/dist/chunk-H3YM66EI.js.map +1 -0
  152. package/dist/{chunk-NRT6QZQB.js → chunk-IVMFIKNA.js} +6 -6
  153. package/dist/chunk-IVMFIKNA.js.map +1 -0
  154. package/dist/{chunk-PSIV7CJV.js → chunk-J4KAPKBJ.js} +2 -2
  155. package/dist/chunk-J4KAPKBJ.js.map +1 -0
  156. package/dist/{chunk-N5DNADLQ.js → chunk-JA25QG5M.js} +224 -34
  157. package/dist/chunk-JA25QG5M.js.map +1 -0
  158. package/dist/{chunk-T7IR4N2S.js → chunk-JBMMWYOH.js} +3 -1
  159. package/dist/{chunk-T7IR4N2S.js.map → chunk-JBMMWYOH.js.map} +1 -1
  160. package/dist/{chunk-YNIUIERI.js → chunk-JPSL5GIZ.js} +18 -59
  161. package/dist/chunk-JPSL5GIZ.js.map +1 -0
  162. package/dist/{chunk-TKUOEU2F.js → chunk-JSWKVZCR.js} +2 -2
  163. package/dist/chunk-JZ4EDEIP.js +22 -0
  164. package/dist/chunk-JZ4EDEIP.js.map +1 -0
  165. package/dist/{chunk-QOOUUR4A.js → chunk-K673OO6B.js} +10 -4
  166. package/dist/chunk-K673OO6B.js.map +1 -0
  167. package/dist/{chunk-NNARWO72.js → chunk-K7RW7FI4.js} +13 -12
  168. package/dist/chunk-K7RW7FI4.js.map +1 -0
  169. package/dist/{chunk-ZXABPVM6.js → chunk-L2F3CFCH.js} +9 -13
  170. package/dist/chunk-L2F3CFCH.js.map +1 -0
  171. package/dist/{chunk-X25NWUZB.js → chunk-L5A3KR7R.js} +3 -3
  172. package/dist/{chunk-T52GX4SA.js → chunk-LGGIFKJP.js} +4 -4
  173. package/dist/{chunk-ID6Z6E66.js → chunk-MQ7YWBQ5.js} +2 -2
  174. package/dist/{chunk-FXQBGFOX.js → chunk-MRJDLXI3.js} +14 -8
  175. package/dist/{chunk-FXQBGFOX.js.map → chunk-MRJDLXI3.js.map} +1 -1
  176. package/dist/chunk-NH2UOV3B.js +18 -0
  177. package/dist/chunk-NH2UOV3B.js.map +1 -0
  178. package/dist/{chunk-FN5EBVTQ.js → chunk-NIXRB5BP.js} +2 -2
  179. package/dist/{chunk-RHVMZSUU.js → chunk-NV4OESHC.js} +1 -1
  180. package/dist/chunk-NV4OESHC.js.map +1 -0
  181. package/dist/{chunk-HBRLNOHL.js → chunk-NYJBYVBG.js} +4 -4
  182. package/dist/{chunk-DF73H4UN.js → chunk-NZULV7LK.js} +5 -5
  183. package/dist/chunk-NZULV7LK.js.map +1 -0
  184. package/dist/{chunk-HK67PX4J.js → chunk-OFD2HD6F.js} +1 -1
  185. package/dist/chunk-OFD2HD6F.js.map +1 -0
  186. package/dist/{chunk-WY2XXTET.js → chunk-OJU67C23.js} +4 -4
  187. package/dist/{chunk-WY2XXTET.js.map → chunk-OJU67C23.js.map} +1 -1
  188. package/dist/{chunk-ZUVBC32X.js → chunk-OK57EATL.js} +2 -2
  189. package/dist/{chunk-KJFW62P4.js → chunk-OM3SEDI4.js} +652 -662
  190. package/dist/chunk-OM3SEDI4.js.map +1 -0
  191. package/dist/{chunk-TOPYQ6GA.js → chunk-PCYPXLEX.js} +6 -6
  192. package/dist/{chunk-GYGLAVCX.js → chunk-PFCTSXHU.js} +6 -2
  193. package/dist/chunk-PFCTSXHU.js.map +1 -0
  194. package/dist/{chunk-VNNRUGMM.js → chunk-PGC35KXE.js} +2 -2
  195. package/dist/{chunk-4I522ERM.js → chunk-PT2YCQVK.js} +4 -4
  196. package/dist/{chunk-Z5HTYMMQ.js → chunk-PTJY5R3O.js} +2 -2
  197. package/dist/chunk-PUBI455O.js +157 -0
  198. package/dist/chunk-PUBI455O.js.map +1 -0
  199. package/dist/{chunk-MTL7MXGR.js → chunk-QDHHHI5Q.js} +16 -17
  200. package/dist/chunk-QDHHHI5Q.js.map +1 -0
  201. package/dist/{chunk-F24ZZTHQ.js → chunk-QGFEBNHO.js} +4 -4
  202. package/dist/{chunk-A6IJU4TK.js → chunk-QGSNJD3B.js} +2 -2
  203. package/dist/chunk-QJNAHPVT.js +91 -0
  204. package/dist/chunk-QJNAHPVT.js.map +1 -0
  205. package/dist/{chunk-BK2HHQGN.js → chunk-QSHATAXJ.js} +2 -2
  206. package/dist/{chunk-ANWPSTZU.js → chunk-QXJXG7DN.js} +57 -30
  207. package/dist/chunk-QXJXG7DN.js.map +1 -0
  208. package/dist/{chunk-HBPPGWUM.js → chunk-RDOEOPOI.js} +14 -9
  209. package/dist/chunk-RDOEOPOI.js.map +1 -0
  210. package/dist/{chunk-JCN44NCY.js → chunk-RIZTZJX6.js} +2 -2
  211. package/dist/{chunk-MVZ2TPVP.js → chunk-RNACRT5H.js} +2 -2
  212. package/dist/chunk-RWFQVNXR.js +24 -0
  213. package/dist/chunk-RWFQVNXR.js.map +1 -0
  214. package/dist/{chunk-TROR3ALR.js → chunk-SFCD6OU7.js} +2 -2
  215. package/dist/{chunk-S57HTZ6X.js → chunk-SNM7LSYG.js} +5 -8
  216. package/dist/chunk-SNM7LSYG.js.map +1 -0
  217. package/dist/chunk-SPPOUJLN.js +40 -0
  218. package/dist/chunk-SPPOUJLN.js.map +1 -0
  219. package/dist/{chunk-3ELRMVJK.js → chunk-SWXLZ4HX.js} +31 -35
  220. package/dist/chunk-SWXLZ4HX.js.map +1 -0
  221. package/dist/{chunk-6B3VNR4P.js → chunk-TFWFPZRU.js} +26 -35
  222. package/dist/chunk-TFWFPZRU.js.map +1 -0
  223. package/dist/{chunk-F7XGHL5O.js → chunk-TMIJAR2X.js} +3 -3
  224. package/dist/{chunk-F7XGHL5O.js.map → chunk-TMIJAR2X.js.map} +1 -1
  225. package/dist/{chunk-I3JDRCMW.js → chunk-TZYTMZZY.js} +10 -10
  226. package/dist/{chunk-AOWGXU6D.js → chunk-UAKY6E4N.js} +2 -2
  227. package/dist/chunk-UHFGK6YA.js +40 -0
  228. package/dist/chunk-UHFGK6YA.js.map +1 -0
  229. package/dist/{chunk-3JWHAR6E.js → chunk-UNSQZSZP.js} +2 -2
  230. package/dist/{chunk-FQ5VRHIB.js → chunk-UOZOWN73.js} +5 -9
  231. package/dist/chunk-UOZOWN73.js.map +1 -0
  232. package/dist/{chunk-GHLTDZRS.js → chunk-V55JASXO.js} +25 -4
  233. package/dist/chunk-V55JASXO.js.map +1 -0
  234. package/dist/{chunk-244VXYPG.js → chunk-VSUQI5Q6.js} +45 -4
  235. package/dist/chunk-VSUQI5Q6.js.map +1 -0
  236. package/dist/{chunk-6K7DPM7D.js → chunk-WFWFXDNS.js} +2 -2
  237. package/dist/{chunk-CF5UU4S7.js → chunk-WHOKGHJ3.js} +7 -7
  238. package/dist/{chunk-LGRLME4M.js → chunk-X2RP4TO5.js} +16 -16
  239. package/dist/chunk-X2RP4TO5.js.map +1 -0
  240. package/dist/{chunk-VNPEWFPD.js → chunk-XA34THLF.js} +8 -6
  241. package/dist/chunk-XA34THLF.js.map +1 -0
  242. package/dist/{chunk-HVSCEIYL.js → chunk-XITHENBI.js} +12 -14
  243. package/dist/chunk-XITHENBI.js.map +1 -0
  244. package/dist/{chunk-ENAG3IY7.js → chunk-XKBTJGFP.js} +26 -28
  245. package/dist/chunk-XKBTJGFP.js.map +1 -0
  246. package/dist/{chunk-32YV3TJT.js → chunk-XXNWU5QZ.js} +2 -2
  247. package/dist/{chunk-B57AFBY2.js → chunk-XXQ5XIYY.js} +2 -2
  248. package/dist/{chunk-ORDURZBU.js → chunk-YFFMPZPY.js} +2 -2
  249. package/dist/{chunk-MDWKM2XC.js → chunk-YKGJ44Z7.js} +3 -3
  250. package/dist/{chunk-IUWCVHQ7.js → chunk-YQFBH5ZJ.js} +4 -4
  251. package/dist/{chunk-IUWCVHQ7.js.map → chunk-YQFBH5ZJ.js.map} +1 -1
  252. package/dist/{chunk-UPD5NWI5.js → chunk-Z3ZXVB2Y.js} +29 -16
  253. package/dist/chunk-Z3ZXVB2Y.js.map +1 -0
  254. package/dist/chunk-ZBUAX4EF.js +27 -0
  255. package/dist/chunk-ZBUAX4EF.js.map +1 -0
  256. package/dist/{chunk-MLPHLJIQ.js → chunk-ZHQJAV6D.js} +2 -2
  257. package/dist/{chunk-UD5EVGZ7.js → chunk-ZMVSKYTJ.js} +56 -96
  258. package/dist/chunk-ZMVSKYTJ.js.map +1 -0
  259. package/dist/{chunk-7JE7BDYJ.js → chunk-ZVSKSUGI.js} +5 -5
  260. package/dist/{chunk-J6UOHJY5.js → chunk-ZWF3EWZA.js} +4 -8
  261. package/dist/chunk-ZWF3EWZA.js.map +1 -0
  262. package/dist/classified/index.js +5 -5
  263. package/dist/{classified-marker-W3USLL5M.js → classified-marker-KHDOYIJB.js} +3 -3
  264. package/dist/classified-marker-KHDOYIJB.js.map +1 -0
  265. package/dist/collection-facade-LZIF7GWD.js +41 -0
  266. package/dist/{computed-TPFBX7U4.js → computed-XMFTHAXV.js} +3 -3
  267. package/dist/consent/index.js +10 -11
  268. package/dist/consent/index.js.map +1 -1
  269. package/dist/cover/index.js +9 -10
  270. package/dist/coverage/index.js +1 -1
  271. package/dist/custody/index.js +13 -13
  272. package/dist/{dead-filter-U2UYKSZL.js → dead-filter-CXV5NS67.js} +11 -3
  273. package/dist/dead-filter-CXV5NS67.js.map +1 -0
  274. package/dist/debug/index.js +11 -2
  275. package/dist/debug/index.js.map +1 -1
  276. package/dist/{delegation-S3AEPW2T.js → delegation-ONLM4JFI.js} +52 -31
  277. package/dist/delegation-ONLM4JFI.js.map +1 -0
  278. package/dist/derivations/index.js +25 -25
  279. package/dist/derive-3YSIS36W.js +20 -0
  280. package/dist/directory/index.js +10 -11
  281. package/dist/{dispatch-RH4LHKBH.js → dispatch-2MQFMEGY.js} +22 -23
  282. package/dist/dispatch-2MQFMEGY.js.map +1 -0
  283. package/dist/{dispatch-3CQDJV5Q.js → dispatch-OFDA4WQV.js} +5 -5
  284. package/dist/executor-E6YDJ7YA.js +8 -0
  285. package/dist/executor-NJX3YQQ4.js +35 -0
  286. package/dist/executor-WWM3DD4P.js +8 -0
  287. package/dist/export-accessible-RLHU5G4W.js +22 -0
  288. package/dist/extract-partition-QII2EDCS.js +35 -0
  289. package/dist/{fanout-sidecar-OT6RYLOL.js → fanout-sidecar-BO67PCPO.js} +13 -15
  290. package/dist/fanout-sidecar-BO67PCPO.js.map +1 -0
  291. package/dist/find-6M7GYDUZ.js +10 -0
  292. package/dist/forget/index.js +26 -10
  293. package/dist/forget/index.js.map +1 -1
  294. package/dist/guards/index.js +4 -4
  295. package/dist/history/index.js +13 -14
  296. package/dist/history/index.js.map +1 -1
  297. package/dist/i18n/index.js +14 -15
  298. package/dist/i18n/index.js.map +1 -1
  299. package/dist/index.d.ts +15 -13
  300. package/dist/index.js +198 -187
  301. package/dist/index.js.map +1 -1
  302. package/dist/indexing/index.js +6 -6
  303. package/dist/introspection/index.js +16 -13
  304. package/dist/issue-LPEEEMPP.js +19 -0
  305. package/dist/kernel/attestation-types.d.ts +62 -0
  306. package/dist/kernel/cache/index.d.ts +1 -1
  307. package/dist/kernel/collection-config.d.ts +1 -48
  308. package/dist/kernel/collection.d.ts +2 -1
  309. package/dist/kernel/cutover-transform.d.ts +1 -1
  310. package/dist/kernel/errors.d.ts +63 -6
  311. package/dist/kernel/export-scope.d.ts +40 -0
  312. package/dist/kernel/merge-authority.d.ts +1 -1
  313. package/dist/kernel/noydb.d.ts +3 -1
  314. package/dist/kernel/paths.d.ts +29 -5
  315. package/dist/kernel/prepared-write.d.ts +1 -1
  316. package/dist/kernel/put-many-atomic.d.ts +1 -1
  317. package/dist/kernel/query/live/live.d.ts +2 -1
  318. package/dist/kernel/query/sort-key.d.ts +0 -2
  319. package/dist/kernel/secret-mode.d.ts +1 -1
  320. package/dist/kernel/tx-write-gate.d.ts +2 -4
  321. package/dist/kernel/types.d.ts +135 -11
  322. package/dist/kernel/validation.d.ts +0 -10
  323. package/dist/kernel/vault.d.ts +6 -2
  324. package/dist/kernel/via/reconcile.d.ts +1 -42
  325. package/dist/kernel/write-queue.d.ts +17 -0
  326. package/dist/{ledger-KZPCMVZY.js → ledger-MSJGWKGW.js} +11 -12
  327. package/dist/liberate-LEKDYKTC.js +25 -0
  328. package/dist/link-set-QXXSPPHL.js +29 -0
  329. package/dist/{managed-secret-S3MQ6SH6.js → managed-secret-VSK7ANQI.js} +10 -11
  330. package/dist/materialized-views/index.js +27 -28
  331. package/dist/{migrate-cek-V7GKJZ7O.js → migrate-cek-GEBFYBDC.js} +2 -2
  332. package/dist/money/index.js +5 -5
  333. package/dist/noydb-F2AUF6KL.js +98 -0
  334. package/dist/on/index.js +25 -0
  335. package/dist/overlay-views/index.js +4 -4
  336. package/dist/periods/index.js +12 -13
  337. package/dist/pod/index.js +76 -74
  338. package/dist/{pod-handle-WGDFB4C2.js → pod-handle-YE7CT66T.js} +10 -11
  339. package/dist/pod-handle-YE7CT66T.js.map +1 -0
  340. package/dist/policy/index.js +10 -11
  341. package/dist/port/as/active.d.ts +4 -0
  342. package/dist/port/as/index.d.ts +1 -0
  343. package/dist/port/by/index.d.ts +14 -5
  344. package/dist/port/on/index.d.ts +27 -1
  345. package/dist/port/to/index.d.ts +1 -1
  346. package/dist/port/with/blob-strategy.d.ts +1 -1
  347. package/dist/port/with/broker-strategy.d.ts +14 -0
  348. package/dist/port/with/classified-strategy.d.ts +1 -1
  349. package/dist/port/with/collection-options.d.ts +1 -1
  350. package/dist/port/with/collection-registries.d.ts +1 -1
  351. package/dist/port/with/coverage-strategy.d.ts +17 -1
  352. package/dist/port/with/formats-strategy.d.ts +34 -1
  353. package/dist/port/with/lookup-strategy.d.ts +87 -6
  354. package/dist/port/with/service-bus.d.ts +2 -1
  355. package/dist/portability/index.js +8 -8
  356. package/dist/{post-register-SLSXIPBV.js → post-register-QC5TEAW3.js} +6 -6
  357. package/dist/post-register-QC5TEAW3.js.map +1 -0
  358. package/dist/query/all/index.js +16 -16
  359. package/dist/query/index.js +8 -8
  360. package/dist/query/live/index.js +7 -7
  361. package/dist/query/reduce/index.js +10 -10
  362. package/dist/query/relate/index.js +10 -10
  363. package/dist/reduce/index.js +8 -8
  364. package/dist/register-SN72QALV.js +30 -0
  365. package/dist/registry-253FF7C6.js +35 -0
  366. package/dist/registry-3AZWQTDT.js +23 -0
  367. package/dist/registry-7UEBTHWG.js +8 -0
  368. package/dist/registry-IVJ67NFQ.js +8 -0
  369. package/dist/request-withdrawal-UKVJZKX2.js +28 -0
  370. package/dist/{reveal-4BCKPEC2.js → reveal-YQTRNQFH.js} +10 -9
  371. package/dist/reveal-YQTRNQFH.js.map +1 -0
  372. package/dist/revoke-G2KXJQA5.js +24 -0
  373. package/dist/satellites/index.js +1 -1
  374. package/dist/schema-update/index.js +2 -2
  375. package/dist/sealed-record/index.js +16 -18
  376. package/dist/sealed-record/index.js.map +1 -1
  377. package/dist/search/index.js +4 -4
  378. package/dist/{seed-4JS2RJ4A.js → seed-JMQTRKRE.js} +125 -34
  379. package/dist/seed-JMQTRKRE.js.map +1 -0
  380. package/dist/sequence/index.js +9 -10
  381. package/dist/session/index.js +9 -10
  382. package/dist/session/index.js.map +1 -1
  383. package/dist/shadow/index.js +2 -2
  384. package/dist/signer-IUIYR3CX.js +23 -0
  385. package/dist/snapshots/index.js +11 -11
  386. package/dist/stale-SKV33PBU.js +28 -0
  387. package/dist/storage-NPBYP2SM.js +21 -0
  388. package/dist/store/index.js +1 -1
  389. package/dist/{store-coordination-provider-2O2VUHKW.js → store-coordination-provider-Z6HKPOGM.js} +11 -12
  390. package/dist/store-coordination-provider-Z6HKPOGM.js.map +1 -0
  391. package/dist/sync/index.js +18 -13
  392. package/dist/sync/index.js.map +1 -1
  393. package/dist/team/index.js +18 -17
  394. package/dist/tiers/index.js +11 -12
  395. package/dist/to/index.js +2 -2
  396. package/dist/transactions/index.js +3 -3
  397. package/dist/util/index.js +1 -1
  398. package/dist/vault-head/index.js +11 -14
  399. package/dist/vault-head/index.js.map +1 -1
  400. package/dist/{verify-RVT67IMH.js → verify-KWQIWLGH.js} +14 -12
  401. package/dist/verify-KWQIWLGH.js.map +1 -0
  402. package/dist/via/classified/binding.d.ts +2 -2
  403. package/dist/via/classified/config-drift.d.ts +2 -2
  404. package/dist/via/classified/errors.d.ts +1 -1
  405. package/dist/via/classified/presets.d.ts +29 -0
  406. package/dist/via/classified/resolve.d.ts +7 -0
  407. package/dist/via/geo/binding.d.ts +0 -4
  408. package/dist/via/lookup/handle.d.ts +4 -4
  409. package/dist/via/lookup/index.d.ts +32 -0
  410. package/dist/via/money/binding.d.ts +0 -9
  411. package/dist/walk-F4OLXI5H.js +20 -0
  412. package/dist/with-audit/attestation/index.d.ts +2 -1
  413. package/dist/with-audit/attestation/issue.d.ts +2 -2
  414. package/dist/with-audit/attestation/revoke.d.ts +2 -2
  415. package/dist/with-audit/attestation/scheme.d.ts +7 -0
  416. package/dist/with-audit/attestation/signer.d.ts +1 -1
  417. package/dist/with-audit/attestation/strategy.d.ts +4 -3
  418. package/dist/with-audit/attestation/types.d.ts +10 -0
  419. package/dist/with-audit/attestation/vault-facade.d.ts +9 -2
  420. package/dist/with-audit/attestation/verifier.d.ts +6 -0
  421. package/dist/with-audit/consent/consent.d.ts +1 -1
  422. package/dist/with-audit/consent/strategy.d.ts +1 -1
  423. package/dist/with-audit/forget/completeness.d.ts +66 -0
  424. package/dist/with-audit/forget/index.d.ts +2 -0
  425. package/dist/with-audit/forget/strategy.d.ts +65 -0
  426. package/dist/with-audit/forget/subject-index.d.ts +1 -1
  427. package/dist/with-audit/periods/periods.d.ts +0 -9
  428. package/dist/with-audit/periods/reserved-collections.d.ts +7 -0
  429. package/dist/with-audit/periods/vault-facade.d.ts +1 -1
  430. package/dist/with-audit/portability/request-withdrawal.d.ts +0 -1
  431. package/dist/with-audit/portability/withdraw-accessible.d.ts +0 -1
  432. package/dist/with-audit/sealed-record/index.d.ts +2 -2
  433. package/dist/with-audit/sealed-record/strategy.d.ts +1 -1
  434. package/dist/with-audit/tiers/index.d.ts +3 -3
  435. package/dist/with-audit/tiers/strategy.d.ts +19 -0
  436. package/dist/with-cargo/adopt-partition.d.ts +1 -1
  437. package/dist/with-cargo/extract-partition.d.ts +1 -47
  438. package/dist/with-cargo/floor.d.ts +1 -1
  439. package/dist/with-commit/history/history.d.ts +2 -2
  440. package/dist/with-commit/history/index.d.ts +1 -1
  441. package/dist/with-commit/history/ledger/store.d.ts +1 -1
  442. package/dist/with-commit/history/strategy.d.ts +1 -1
  443. package/dist/with-commit/history/time-machine.d.ts +1 -1
  444. package/dist/with-commit/numbering/index.d.ts +1 -3
  445. package/dist/with-commit/sequence/index.d.ts +1 -1
  446. package/dist/with-commit/tx/transaction.d.ts +2 -24
  447. package/dist/with-commit/vault-head/head.d.ts +1 -1
  448. package/dist/with-commit/vault-head/index.d.ts +1 -1
  449. package/dist/with-commit/vault-head/strategy.d.ts +8 -1
  450. package/dist/with-commit/vault-head/verify.d.ts +1 -1
  451. package/dist/with-fork/snapshots/noydb-facade.d.ts +7 -0
  452. package/dist/with-formula/derivations/dispatch.d.ts +1 -1
  453. package/dist/with-formula/derivations/fanout-sidecar.d.ts +1 -1
  454. package/dist/with-formula/derivations/trigger-match.d.ts +8 -5
  455. package/dist/with-formula/materialized-views/emit-memo.d.ts +0 -2
  456. package/dist/with-lookup/embeddings/index.d.ts +1 -2
  457. package/dist/with-lookup/indexing/collection-facade.d.ts +2 -16
  458. package/dist/with-lookup/indexing/eager-indexes.d.ts +2 -1
  459. package/dist/with-lookup/search/collection-facade.d.ts +1 -13
  460. package/dist/with-party/broker/index.d.ts +4 -4
  461. package/dist/with-party/broker/seed.d.ts +25 -10
  462. package/dist/with-party/directory/user-envelope/storage.d.ts +1 -1
  463. package/dist/with-party/policy/noydb-facade.d.ts +1 -31
  464. package/dist/with-party/team/delegation.d.ts +59 -3
  465. package/dist/with-party/team/echo-secret.d.ts +2 -4
  466. package/dist/with-party/team/index.d.ts +1 -1
  467. package/dist/with-party/team/keyring.d.ts +2 -22
  468. package/dist/with-party/team/magic-link-grant.d.ts +12 -2
  469. package/dist/with-party/team/noydb-facade.d.ts +5 -0
  470. package/dist/with-party/team/recovery.d.ts +1 -1
  471. package/dist/with-party/team/reserved-secret-collections.d.ts +10 -8
  472. package/dist/with-party/team/roster-tag.d.ts +43 -2
  473. package/dist/with-party/team/rotate-recover.d.ts +1 -1
  474. package/dist/with-party/team/tiers.d.ts +36 -5
  475. package/dist/with-party/team/wrapped-deks.d.ts +1 -1
  476. package/dist/with-pod/index.d.ts +1 -1
  477. package/dist/with-shape/blobs/blob-compaction.d.ts +1 -1
  478. package/dist/with-shape/blobs/blob-intent.d.ts +1 -1
  479. package/dist/with-shape/blobs/blob-set.d.ts +25 -2
  480. package/dist/with-shape/blobs/legacy-sweep.d.ts +17 -1
  481. package/dist/with-shape/introspection/describe.d.ts +2 -3
  482. package/dist/with-shape/introspection/index.d.ts +1 -0
  483. package/dist/with-shape/introspection/types.d.ts +2 -1
  484. package/dist/with-shape/introspection/walk.d.ts +1 -1
  485. package/dist/with-shape/links/lazy-handle.d.ts +1 -1
  486. package/dist/with-shape/links/link-set.d.ts +1 -1
  487. package/dist/with-shape/manifest/derive.d.ts +1 -1
  488. package/dist/with-shape/manifest/storage.d.ts +1 -1
  489. package/dist/with-shape/persisted-schemas/register.d.ts +1 -1
  490. package/dist/with-shape/persisted-schemas/storage.d.ts +1 -1
  491. package/dist/with-shape/satellites/dead-filter.d.ts +1 -1
  492. package/dist/with-shape/satellites/declare.d.ts +1 -1
  493. package/dist/with-shape/satellites/marker.d.ts +1 -1
  494. package/dist/with-shape/satellites/post-register.d.ts +1 -1
  495. package/dist/with-shape/schema-update/fence.d.ts +7 -0
  496. package/dist/with-sync/engine.d.ts +30 -1
  497. package/dist/with-sync/presence.d.ts +5 -10
  498. package/dist/with-sync/reserved-mirror.d.ts +89 -0
  499. package/dist/with-sync/strategy.d.ts +7 -0
  500. package/dist/with-sync/tab-write-relay.d.ts +0 -10
  501. package/dist/withdraw-accessible-ZKPLZ66E.js +25 -0
  502. package/dist/withdraw-accessible-ZKPLZ66E.js.map +1 -0
  503. package/package.json +14 -5
  504. package/dist/api-GVYEKSJE.js +0 -19
  505. package/dist/backup-7TFRVFLH.js.map +0 -1
  506. package/dist/chunk-244VXYPG.js.map +0 -1
  507. package/dist/chunk-3ELRMVJK.js.map +0 -1
  508. package/dist/chunk-3F7GLNAE.js.map +0 -1
  509. package/dist/chunk-3H5KLDZN.js.map +0 -1
  510. package/dist/chunk-5FDLC6G7.js.map +0 -1
  511. package/dist/chunk-6B3VNR4P.js.map +0 -1
  512. package/dist/chunk-6LNL42F5.js.map +0 -1
  513. package/dist/chunk-6QSQQLSE.js +0 -95
  514. package/dist/chunk-6QSQQLSE.js.map +0 -1
  515. package/dist/chunk-7VCXZDA4.js.map +0 -1
  516. package/dist/chunk-ANWPSTZU.js.map +0 -1
  517. package/dist/chunk-BP6DARYA.js.map +0 -1
  518. package/dist/chunk-BUDAGWL5.js.map +0 -1
  519. package/dist/chunk-CKNGS5MH.js.map +0 -1
  520. package/dist/chunk-DC5QVPV7.js.map +0 -1
  521. package/dist/chunk-DE4DH2RZ.js +0 -29
  522. package/dist/chunk-DE4DH2RZ.js.map +0 -1
  523. package/dist/chunk-DF73H4UN.js.map +0 -1
  524. package/dist/chunk-EIPVYUEP.js.map +0 -1
  525. package/dist/chunk-ENAG3IY7.js.map +0 -1
  526. package/dist/chunk-FCKWQE2H.js.map +0 -1
  527. package/dist/chunk-FFWI7UH7.js.map +0 -1
  528. package/dist/chunk-FQ54DWUQ.js.map +0 -1
  529. package/dist/chunk-FQ5VRHIB.js.map +0 -1
  530. package/dist/chunk-GF5BZ7D4.js.map +0 -1
  531. package/dist/chunk-GHLTDZRS.js.map +0 -1
  532. package/dist/chunk-GYGLAVCX.js.map +0 -1
  533. package/dist/chunk-HBPPGWUM.js.map +0 -1
  534. package/dist/chunk-HK67PX4J.js.map +0 -1
  535. package/dist/chunk-HVSCEIYL.js.map +0 -1
  536. package/dist/chunk-IEX7NK2K.js +0 -125
  537. package/dist/chunk-IEX7NK2K.js.map +0 -1
  538. package/dist/chunk-ISZQJBML.js.map +0 -1
  539. package/dist/chunk-J6UOHJY5.js.map +0 -1
  540. package/dist/chunk-KJFW62P4.js.map +0 -1
  541. package/dist/chunk-L7DOS3TE.js.map +0 -1
  542. package/dist/chunk-LGNDFRYC.js.map +0 -1
  543. package/dist/chunk-LGRLME4M.js.map +0 -1
  544. package/dist/chunk-LP7BEXCT.js +0 -32
  545. package/dist/chunk-LP7BEXCT.js.map +0 -1
  546. package/dist/chunk-LQ4ZHOKP.js.map +0 -1
  547. package/dist/chunk-M522UYZ2.js.map +0 -1
  548. package/dist/chunk-M7NI3IWE.js.map +0 -1
  549. package/dist/chunk-MTL7MXGR.js.map +0 -1
  550. package/dist/chunk-N5DNADLQ.js.map +0 -1
  551. package/dist/chunk-NNARWO72.js.map +0 -1
  552. package/dist/chunk-NRT6QZQB.js.map +0 -1
  553. package/dist/chunk-NWHBJQAL.js.map +0 -1
  554. package/dist/chunk-ODVPTDPK.js.map +0 -1
  555. package/dist/chunk-PDCG6PR5.js.map +0 -1
  556. package/dist/chunk-PEOZ34EM.js.map +0 -1
  557. package/dist/chunk-PSIV7CJV.js.map +0 -1
  558. package/dist/chunk-QBBUZ3I3.js.map +0 -1
  559. package/dist/chunk-QOOUUR4A.js.map +0 -1
  560. package/dist/chunk-QWFHIOD2.js.map +0 -1
  561. package/dist/chunk-QWGU66MP.js.map +0 -1
  562. package/dist/chunk-RHVMZSUU.js.map +0 -1
  563. package/dist/chunk-S57HTZ6X.js.map +0 -1
  564. package/dist/chunk-SEE332EV.js +0 -66
  565. package/dist/chunk-SEE332EV.js.map +0 -1
  566. package/dist/chunk-SOIQ4MM4.js +0 -20
  567. package/dist/chunk-SOIQ4MM4.js.map +0 -1
  568. package/dist/chunk-SYVZW77S.js.map +0 -1
  569. package/dist/chunk-T2LBQ5DV.js.map +0 -1
  570. package/dist/chunk-UBSFPGVJ.js +0 -1
  571. package/dist/chunk-UD5EVGZ7.js.map +0 -1
  572. package/dist/chunk-UPD5NWI5.js.map +0 -1
  573. package/dist/chunk-UYB234ZR.js +0 -479
  574. package/dist/chunk-UYB234ZR.js.map +0 -1
  575. package/dist/chunk-VNPEWFPD.js.map +0 -1
  576. package/dist/chunk-WX5ADGTZ.js.map +0 -1
  577. package/dist/chunk-X4773RBR.js.map +0 -1
  578. package/dist/chunk-YNIUIERI.js.map +0 -1
  579. package/dist/chunk-YSK2CU2Q.js.map +0 -1
  580. package/dist/chunk-ZE4CHIED.js.map +0 -1
  581. package/dist/chunk-ZXABPVM6.js.map +0 -1
  582. package/dist/classified-marker-W3USLL5M.js.map +0 -1
  583. package/dist/collection-facade-QU6IJBN3.js +0 -48
  584. package/dist/dead-filter-U2UYKSZL.js.map +0 -1
  585. package/dist/delegation-S3AEPW2T.js.map +0 -1
  586. package/dist/derive-OX7F6TGG.js +0 -21
  587. package/dist/dispatch-RH4LHKBH.js.map +0 -1
  588. package/dist/executor-B6SRO2QS.js +0 -36
  589. package/dist/executor-BI5CHWRY.js +0 -8
  590. package/dist/executor-RBKM5W5K.js +0 -8
  591. package/dist/export-accessible-VMA3YM5V.js +0 -22
  592. package/dist/extract-partition-NF7PXGPP.js +0 -37
  593. package/dist/fanout-sidecar-OT6RYLOL.js.map +0 -1
  594. package/dist/find-LZMP26KF.js +0 -10
  595. package/dist/issue-IHARXWW5.js +0 -18
  596. package/dist/kernel/enclave/classify/compare.d.ts +0 -10
  597. package/dist/kernel/enclave/index.d.ts +0 -87
  598. package/dist/kernel/enclave/record-keys/lifecycle.d.ts +0 -124
  599. package/dist/kernel/enclave/record-keys/rekey-blob.d.ts +0 -88
  600. package/dist/kernel/enclave/record-keys/rekey.d.ts +0 -91
  601. package/dist/kernel/enclave/record-keys/sealed-slot.d.ts +0 -14
  602. package/dist/kernel/enclave/record-keys/sealed-slots.d.ts +0 -123
  603. package/dist/kernel/enclave/record-keys/sealing.d.ts +0 -70
  604. package/dist/liberate-GRLCZMW4.js +0 -24
  605. package/dist/link-set-JCMJ7ULP.js +0 -30
  606. package/dist/noydb-SWQJ3MZS.js +0 -97
  607. package/dist/pod-handle-WGDFB4C2.js.map +0 -1
  608. package/dist/post-register-SLSXIPBV.js.map +0 -1
  609. package/dist/register-F3OYE7MJ.js +0 -31
  610. package/dist/registry-CILQFXFK.js +0 -8
  611. package/dist/registry-XNNYQVKV.js +0 -8
  612. package/dist/registry-YCDZV3VW.js +0 -35
  613. package/dist/registry-ZGYS33TE.js +0 -24
  614. package/dist/request-withdrawal-Z5NC6TXT.js +0 -30
  615. package/dist/reveal-4BCKPEC2.js.map +0 -1
  616. package/dist/revoke-ZL62WC2D.js +0 -23
  617. package/dist/seed-4JS2RJ4A.js.map +0 -1
  618. package/dist/signer-MBZZMXTC.js +0 -24
  619. package/dist/stale-TJ3DYZMQ.js +0 -29
  620. package/dist/storage-SVMKRGCW.js +0 -22
  621. package/dist/store-coordination-provider-2O2VUHKW.js.map +0 -1
  622. package/dist/verify-RVT67IMH.js.map +0 -1
  623. package/dist/walk-HJ42EISE.js +0 -21
  624. package/dist/withdraw-accessible-5NZUPAVA.js +0 -27
  625. /package/dist/{api-GVYEKSJE.js.map → api-MSJXVYQG.js.map} +0 -0
  626. /package/dist/{collection-facade-QU6IJBN3.js.map → capsule/contract.js.map} +0 -0
  627. /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/bidx.d.ts +0 -0
  628. /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/digest.d.ts +0 -0
  629. /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/vdig.d.ts +0 -0
  630. /package/dist/{kernel/enclave/classify → capsule/plumbing}/kofn.d.ts +0 -0
  631. /package/dist/{kernel/enclave/classify → capsule/plumbing}/normalize.d.ts +0 -0
  632. /package/dist/{kernel/enclave → capsule/plumbing}/record-aad.d.ts +0 -0
  633. /package/dist/{chunk-UBSFPGVJ.js.map → capsule-57BDK7LG.js.map} +0 -0
  634. /package/dist/{computed-TPFBX7U4.js.map → chunk-2DX4JCRI.js.map} +0 -0
  635. /package/dist/{chunk-ZSX4S5XB.js.map → chunk-2ZFD2UJC.js.map} +0 -0
  636. /package/dist/{chunk-HT2AHGQI.js.map → chunk-3Y5JLSXS.js.map} +0 -0
  637. /package/dist/{chunk-SI3V7D4G.js.map → chunk-4UIP3QNW.js.map} +0 -0
  638. /package/dist/{chunk-WNJ4AVGE.js.map → chunk-55P6OBB7.js.map} +0 -0
  639. /package/dist/{chunk-MKXXMNJH.js.map → chunk-5UUJE4FP.js.map} +0 -0
  640. /package/dist/{chunk-FW22ZCXT.js.map → chunk-6WS35E6X.js.map} +0 -0
  641. /package/dist/{chunk-6OWXYZK2.js.map → chunk-B65CC6Z6.js.map} +0 -0
  642. /package/dist/{chunk-F2TDU7MQ.js.map → chunk-BJH77UY2.js.map} +0 -0
  643. /package/dist/{chunk-BJ2IXQ5Q.js.map → chunk-BNFNQNEP.js.map} +0 -0
  644. /package/dist/{chunk-HLEK5VAN.js.map → chunk-BUE7R3B5.js.map} +0 -0
  645. /package/dist/{chunk-EOVMT6TB.js.map → chunk-C53ER7AG.js.map} +0 -0
  646. /package/dist/{chunk-K6W7BVZ6.js.map → chunk-CAD7N6HQ.js.map} +0 -0
  647. /package/dist/{chunk-5O7D6ZTW.js.map → chunk-DHU2AQ6T.js.map} +0 -0
  648. /package/dist/{chunk-IT22PWKV.js.map → chunk-ECGXA4LT.js.map} +0 -0
  649. /package/dist/{chunk-4MXI3ODD.js.map → chunk-GCUMOKTN.js.map} +0 -0
  650. /package/dist/{chunk-B6SJIBQK.js.map → chunk-GOJQ5DZA.js.map} +0 -0
  651. /package/dist/{chunk-TKUOEU2F.js.map → chunk-JSWKVZCR.js.map} +0 -0
  652. /package/dist/{chunk-X25NWUZB.js.map → chunk-L5A3KR7R.js.map} +0 -0
  653. /package/dist/{chunk-T52GX4SA.js.map → chunk-LGGIFKJP.js.map} +0 -0
  654. /package/dist/{chunk-ID6Z6E66.js.map → chunk-MQ7YWBQ5.js.map} +0 -0
  655. /package/dist/{chunk-FN5EBVTQ.js.map → chunk-NIXRB5BP.js.map} +0 -0
  656. /package/dist/{chunk-HBRLNOHL.js.map → chunk-NYJBYVBG.js.map} +0 -0
  657. /package/dist/{chunk-ZUVBC32X.js.map → chunk-OK57EATL.js.map} +0 -0
  658. /package/dist/{chunk-TOPYQ6GA.js.map → chunk-PCYPXLEX.js.map} +0 -0
  659. /package/dist/{chunk-VNNRUGMM.js.map → chunk-PGC35KXE.js.map} +0 -0
  660. /package/dist/{chunk-4I522ERM.js.map → chunk-PT2YCQVK.js.map} +0 -0
  661. /package/dist/{chunk-Z5HTYMMQ.js.map → chunk-PTJY5R3O.js.map} +0 -0
  662. /package/dist/{chunk-F24ZZTHQ.js.map → chunk-QGFEBNHO.js.map} +0 -0
  663. /package/dist/{chunk-A6IJU4TK.js.map → chunk-QGSNJD3B.js.map} +0 -0
  664. /package/dist/{chunk-BK2HHQGN.js.map → chunk-QSHATAXJ.js.map} +0 -0
  665. /package/dist/{chunk-JCN44NCY.js.map → chunk-RIZTZJX6.js.map} +0 -0
  666. /package/dist/{chunk-MVZ2TPVP.js.map → chunk-RNACRT5H.js.map} +0 -0
  667. /package/dist/{chunk-TROR3ALR.js.map → chunk-SFCD6OU7.js.map} +0 -0
  668. /package/dist/{chunk-I3JDRCMW.js.map → chunk-TZYTMZZY.js.map} +0 -0
  669. /package/dist/{chunk-AOWGXU6D.js.map → chunk-UAKY6E4N.js.map} +0 -0
  670. /package/dist/{chunk-3JWHAR6E.js.map → chunk-UNSQZSZP.js.map} +0 -0
  671. /package/dist/{chunk-6K7DPM7D.js.map → chunk-WFWFXDNS.js.map} +0 -0
  672. /package/dist/{chunk-CF5UU4S7.js.map → chunk-WHOKGHJ3.js.map} +0 -0
  673. /package/dist/{chunk-32YV3TJT.js.map → chunk-XXNWU5QZ.js.map} +0 -0
  674. /package/dist/{chunk-B57AFBY2.js.map → chunk-XXQ5XIYY.js.map} +0 -0
  675. /package/dist/{chunk-ORDURZBU.js.map → chunk-YFFMPZPY.js.map} +0 -0
  676. /package/dist/{chunk-MDWKM2XC.js.map → chunk-YKGJ44Z7.js.map} +0 -0
  677. /package/dist/{chunk-MLPHLJIQ.js.map → chunk-ZHQJAV6D.js.map} +0 -0
  678. /package/dist/{chunk-7JE7BDYJ.js.map → chunk-ZVSKSUGI.js.map} +0 -0
  679. /package/dist/{derive-OX7F6TGG.js.map → collection-facade-LZIF7GWD.js.map} +0 -0
  680. /package/dist/{enclave-27SMF6GF.js.map → computed-XMFTHAXV.js.map} +0 -0
  681. /package/dist/{executor-B6SRO2QS.js.map → derive-3YSIS36W.js.map} +0 -0
  682. /package/dist/{dispatch-3CQDJV5Q.js.map → dispatch-OFDA4WQV.js.map} +0 -0
  683. /package/dist/{executor-BI5CHWRY.js.map → executor-E6YDJ7YA.js.map} +0 -0
  684. /package/dist/{executor-RBKM5W5K.js.map → executor-NJX3YQQ4.js.map} +0 -0
  685. /package/dist/{export-accessible-VMA3YM5V.js.map → executor-WWM3DD4P.js.map} +0 -0
  686. /package/dist/{extract-partition-NF7PXGPP.js.map → export-accessible-RLHU5G4W.js.map} +0 -0
  687. /package/dist/{find-LZMP26KF.js.map → extract-partition-QII2EDCS.js.map} +0 -0
  688. /package/dist/{issue-IHARXWW5.js.map → find-6M7GYDUZ.js.map} +0 -0
  689. /package/dist/{ledger-KZPCMVZY.js.map → issue-LPEEEMPP.js.map} +0 -0
  690. /package/dist/{liberate-GRLCZMW4.js.map → ledger-MSJGWKGW.js.map} +0 -0
  691. /package/dist/{link-set-JCMJ7ULP.js.map → liberate-LEKDYKTC.js.map} +0 -0
  692. /package/dist/{managed-secret-S3MQ6SH6.js.map → link-set-QXXSPPHL.js.map} +0 -0
  693. /package/dist/{noydb-SWQJ3MZS.js.map → managed-secret-VSK7ANQI.js.map} +0 -0
  694. /package/dist/{migrate-cek-V7GKJZ7O.js.map → migrate-cek-GEBFYBDC.js.map} +0 -0
  695. /package/dist/{register-F3OYE7MJ.js.map → noydb-F2AUF6KL.js.map} +0 -0
  696. /package/dist/{registry-CILQFXFK.js.map → register-SN72QALV.js.map} +0 -0
  697. /package/dist/{registry-XNNYQVKV.js.map → registry-253FF7C6.js.map} +0 -0
  698. /package/dist/{registry-YCDZV3VW.js.map → registry-3AZWQTDT.js.map} +0 -0
  699. /package/dist/{registry-ZGYS33TE.js.map → registry-7UEBTHWG.js.map} +0 -0
  700. /package/dist/{request-withdrawal-Z5NC6TXT.js.map → registry-IVJ67NFQ.js.map} +0 -0
  701. /package/dist/{revoke-ZL62WC2D.js.map → request-withdrawal-UKVJZKX2.js.map} +0 -0
  702. /package/dist/{signer-MBZZMXTC.js.map → revoke-G2KXJQA5.js.map} +0 -0
  703. /package/dist/{stale-TJ3DYZMQ.js.map → signer-IUIYR3CX.js.map} +0 -0
  704. /package/dist/{storage-SVMKRGCW.js.map → stale-SKV33PBU.js.map} +0 -0
  705. /package/dist/{walk-HJ42EISE.js.map → storage-NPBYP2SM.js.map} +0 -0
  706. /package/dist/{withdraw-accessible-5NZUPAVA.js.map → walk-F4OLXI5H.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-cargo/walk-closure.ts","../src/with-cargo/extract-partition.ts"],"sourcesContent":["/**\n * Transitive-closure FK walker. Computes the set of\n * (collection, id) tuples reachable from seed predicates, so a\n * partition extraction ships a referentially-complete subset.\n *\n * Two-phase, plaintext, read-only (runs inside the unlocked vault\n * session — see foundation §13.4 / spec invariant 7):\n * 1. INBOUND expansion: from selected records, pull every record\n * that references them (children travel with parents), to a\n * fixed point.\n * 2. OUTBOUND completion: pull every VISIBLE parent the selected set\n * references, transitively, WITHOUT re-expanding inbound from those\n * parents (bounds the closure). A tier-elevated (or missing) parent is\n * excluded rather than admitted — see `danglingRefs` on the result.\n *\n * The FK graph is auto-derived from the vault's existing RefRegistry\n * (the `ref('target')` declarations on collections) — no hand-written\n * edge list.\n *\n * @module\n */\nimport type { Vault } from '../kernel/vault.js'\nimport { PartitionExtractionError } from '../kernel/errors.js'\n\n/** Seed predicate per collection. Records that return true become roots. */\nexport interface WalkClosureOptions {\n readonly seeds: Record<\n string,\n (record: Record<string, unknown>) => boolean | Promise<boolean>\n >\n /** Max fixed-point iterations before throwing. Default 16. */\n readonly maxDepth?: number\n}\n\n/**\n * #759: an outbound FK edge whose referenced parent was excluded from the\n * closure — either because it doesn't exist, or because it is tier-elevated\n * and therefore invisible (same \"elevated ≡ missing\" semantics as root\n * selection / inbound expansion). The child keeps its FK value; the parent\n * does not travel. Callers (extract-partition) surface this as a residue\n * notice rather than silently dropping it.\n */\nexport interface DanglingRefNotice {\n /** Collection of the child record that holds the dangling FK. */\n readonly collection: string\n /** Id of the child record. */\n readonly id: string\n /** FK field on the child that references the missing/elevated parent. */\n readonly field: string\n /** Target collection the FK points at. */\n readonly target: string\n /** Id of the missing/elevated parent. */\n readonly targetId: string\n /**\n * #772: distinguishes an intentional tier boundary (`'elevated'` — the\n * parent exists but is above the caller's readable tier) from a genuine\n * data-integrity gap (`'missing'` — no envelope for `targetId` at all).\n */\n readonly reason: 'missing' | 'elevated'\n}\n\nexport interface ClosureResult {\n /** collection → set of record ids that travel together. */\n readonly closure: Map<string, Set<string>>\n readonly graph: {\n /** Fixed-point iterations the walk needed to converge. */\n readonly depth: number\n /** True if an edge pointed back to an already-selected node. */\n readonly cyclesDetected: boolean\n }\n /** #759: outbound FK edges whose parent was excluded (missing or elevated). */\n readonly danglingRefs: ReadonlyArray<DanglingRefNotice>\n}\n\nexport async function walkClosure(\n vault: Vault,\n opts: WalkClosureOptions,\n): Promise<ClosureResult> {\n const closure = new Map<string, Set<string>>()\n\n // Records carry a string `id` by construction (Collection.put(id: string)).\n // A non-string id during the walk means a malformed record — fail loud\n // rather than silently dropping it from the closure (which would leave a\n // dangling FK or a missing child in the extracted bundle).\n const requireStringId = (collection: string, record: Record<string, unknown>): string => {\n const id = record['id']\n if (typeof id !== 'string') {\n throw new PartitionExtractionError(\n `walkClosure: record in collection \"${collection}\" has a non-string ` +\n `id (${typeof id}); cannot include it in the partition closure.`,\n )\n }\n return id\n }\n\n const add = (collection: string, id: string): boolean => {\n let set = closure.get(collection)\n if (!set) {\n set = new Set<string>()\n closure.set(collection, set)\n }\n if (set.has(id)) return false\n set.add(id)\n return true\n }\n\n // Phase 0: evaluate seed predicates.\n for (const [collectionName, predicate] of Object.entries(opts.seeds)) {\n const coll = vault.collection<Record<string, unknown>>(collectionName)\n const records = await coll.list()\n for (const record of records) {\n if (await predicate(record)) {\n add(collectionName, requireStringId(collectionName, record))\n }\n }\n }\n\n const { refRegistry, adapter, name: vaultName } = vault._introspectState()\n const maxDepth = opts.maxDepth ?? 16\n let cyclesDetected = false\n\n // `depth` counts PRODUCTIVE expansion generations (rounds that added at\n // least one new record), taken as the max over the two phases — i.e. the\n // FK hop-distance the closure needed, not the raw loop-iteration count.\n // The terminal draining pass that adds nothing does not count.\n let inboundDepth = 0\n let outboundDepth = 0\n\n // Phase 1 — INBOUND expansion. Worklist of newly-added (collection,id)\n // whose children we still need to pull.\n let frontier: Array<[string, string]> = []\n for (const [c, ids] of closure) for (const id of ids) frontier.push([c, id])\n\n while (frontier.length > 0) {\n const next: Array<[string, string]> = []\n for (const [collectionName, id] of frontier) {\n // Which collections reference THIS collection, and via which field?\n for (const inbound of refRegistry.getInbound(collectionName)) {\n const childColl = vault.collection<Record<string, unknown>>(inbound.collection)\n // TODO(perf): re-scans the full inbound collection on every frontier\n // element. O(frontier · inboundCollections · records) per depth. Fine\n // at consumer-firm scale (foundation §13.4); revisit with an index or\n // pagination if extraction over very large vaults gets slow.\n const childRecords = await childColl.list()\n for (const child of childRecords) {\n const fk = child[inbound.field]\n // Only scalar FK values can match an id; skip null/objects\n // (mirrors checkIntegrity's scalar guard, vault.ts).\n if (typeof fk !== 'string' && typeof fk !== 'number') continue\n if (String(fk) !== id) continue\n const childId = requireStringId(inbound.collection, child)\n if (add(inbound.collection, childId)) {\n next.push([inbound.collection, childId])\n } else {\n cyclesDetected = true\n }\n }\n }\n }\n if (next.length > 0 && ++inboundDepth > maxDepth) {\n throw new PartitionExtractionError(\n `walkClosure exceeded maxDepth=${maxDepth}; the FK graph may be ` +\n `unexpectedly deep or cyclic. Raise maxDepth or narrow the seeds.`,\n )\n }\n frontier = next\n }\n\n // Phase 2 — OUTBOUND completion. Pull referenced parents so no FK\n // dangles. Transitive over outbound edges only; parents are NOT\n // inbound-expanded (that would drag in unrelated siblings).\n let outboundFrontier: Array<[string, string]> = []\n for (const [c, ids] of closure) for (const id of ids) outboundFrontier.push([c, id])\n\n const danglingRefs: DanglingRefNotice[] = []\n\n while (outboundFrontier.length > 0) {\n const next: Array<[string, string]> = []\n for (const [collectionName, id] of outboundFrontier) {\n const outbound = refRegistry.getOutbound(collectionName)\n if (Object.keys(outbound).length === 0) continue\n const coll = vault.collection<Record<string, unknown>>(collectionName)\n const record = await coll.get(id)\n if (!record) continue\n for (const [field, descriptor] of Object.entries(outbound)) {\n const rawId = record[field]\n // Only scalar FK values reference a parent id; skip null/objects.\n if (typeof rawId !== 'string' && typeof rawId !== 'number') continue\n const parentId = String(rawId)\n // #759: verify the referenced parent is visible through the SAME\n // tier-gated primitive root selection / inbound expansion use\n // (`Collection.get()` → `#getRaw`, which returns null for both a\n // missing record and a tier-elevated one). An elevated parent must\n // be excluded exactly like an elevated root or inbound target —\n // \"elevated ≡ invisible\" — rather than admitted into the closure,\n // where `reKeyClosure`'s raw adapter read would later hit it and\n // fail loud (the #748 canary). Record the resulting dangling FK\n // instead of silently dropping it.\n const parentColl = vault.collection<Record<string, unknown>>(descriptor.target)\n const parentRecord = await parentColl.get(parentId)\n if (!parentRecord) {\n // #772: a raw (tier-unaware) read distinguishes the two cases the\n // tier-gated `Collection.get()` above collapses to the same null —\n // an envelope present with `_tier > 0` is an intentional tier\n // boundary; no envelope at all is a genuine data-integrity gap.\n const rawParentEnv = await adapter.get(vaultName, descriptor.target, parentId)\n const reason: 'missing' | 'elevated' =\n rawParentEnv && (rawParentEnv._tier ?? 0) > 0 ? 'elevated' : 'missing'\n danglingRefs.push({\n collection: collectionName, id, field, target: descriptor.target, targetId: parentId, reason,\n })\n continue\n }\n // Reaching an already-selected parent here is normal DAG\n // convergence (a child referencing its in-scope parent), not a\n // cycle — so do NOT flag cyclesDetected in the outbound phase.\n if (add(descriptor.target, parentId)) {\n next.push([descriptor.target, parentId])\n }\n }\n }\n if (next.length > 0 && ++outboundDepth > maxDepth) {\n throw new PartitionExtractionError(\n `walkClosure exceeded maxDepth=${maxDepth} during outbound completion.`,\n )\n }\n outboundFrontier = next\n }\n\n const depth = Math.max(inboundDepth, outboundDepth)\n\n return { closure, graph: { depth, cyclesDetected }, danglingRefs }\n}\n","/**\n * Partition extraction. Walks the FK closure, re-encrypts\n * the selected records under fresh per-collection DEKs, seals those DEKs\n * under a one-time transfer key, and serializes an unowned\n * `extracted-partition` bundle.\n *\n * @module\n */\nimport type { Vault } from '../kernel/vault.js'\nimport type { EncryptedEnvelope, BlobObject, SlotRecord, VersionRecord } from '../kernel/types.js'\nimport { NOYDB_BACKUP_VERSION } from '../kernel/types.js'\nimport { buildRecordAad, recordAadFor,\n decrypt,\n encrypt,\n openEnvelopeJson,\n writeEnvelopeBody,\n generateDEK,\n bufferToBase64,\n encryptBytesWithAAD,\n decryptBytesWithAAD,\n unwrapCek,\n wrapCek,\n type EnclaveKey,\n} from '../kernel/enclave/index.js'\nimport {\n BLOB_COLLECTION,\n BLOB_INDEX_COLLECTION,\n BLOB_CHUNKS_COLLECTION,\n BLOB_SLOTS_PREFIX,\n BLOB_VERSIONS_PREFIX,\n} from '../with-shape/blobs/blob-set.js'\nimport { BLOB_INTENT_COLLECTION } from '../with-shape/blobs/blob-intent.js'\nimport { PartitionExtractionError } from '../kernel/errors.js'\nimport { walkClosure, type WalkClosureOptions, type DanglingRefNotice } from './walk-closure.js'\nimport { generateULID } from '../with-pod/ulid.js'\nimport { SCHEMAS_COLLECTION } from '../with-shape/persisted-schemas/storage.js'\nimport { LEDGER_COLLECTION } from '../with-commit/history/ledger/constants.js'\nimport { canonicalJson, hashEntry } from '../with-commit/history/ledger/entry.js'\nimport type { LedgerEntry } from '../with-commit/history/ledger/entry.js'\nimport { envelopePayloadHash } from '../with-commit/history/ledger/hash.js'\nimport { buildRecordEnvelope } from '../kernel/enclave/index.js'\nimport {\n assemblePodContainer,\n buildExtractedPartitionWrapper,\n type TransferSealPayload,\n} from '../with-pod/pod.js'\n\n/** Re-keyed collections snapshot + the fresh DEKs used. */\nexport interface ReKeyResult {\n readonly collections: Record<string, Record<string, EncryptedEnvelope>>\n readonly deks: Map<string, EnclaveKey>\n}\n\n/**\n * Re-encrypt every record in `closure` under a fresh per-collection DEK.\n * Reads raw source envelopes, decrypts under the source DEK, re-encrypts\n * under the new DEK. Plaintext-pipeline: requires an unlocked vault.\n */\nexport async function reKeyClosure(\n vault: Vault,\n closure: Map<string, Set<string>>,\n fieldProjection?: Record<string, readonly string[]>,\n): Promise<ReKeyResult> {\n const { name: vaultName, adapter, getDEK } = vault._introspectState()\n const collections: Record<string, Record<string, EncryptedEnvelope>> = {}\n const deks = new Map<string, EnclaveKey>()\n\n for (const [collectionName, ids] of closure) {\n const srcDek = await getDEK(collectionName)\n const destDek = await generateDEK()\n deks.set(collectionName, destDek)\n const out: Record<string, EncryptedEnvelope> = {}\n // FR-7 structural field projection: when this collection has a projection,\n // narrow the plaintext body to `id` (always) + the listed fields BEFORE\n // re-encryption, so excluded fields never travel in the bundle. Applied\n // identically in both re-key branches; only the body changes — the `_cek`\n // re-wrap order is untouched. Absent/empty projection → byte-identical to\n // the un-projected path (`proj` is undefined and `project` is a no-op).\n const projList = fieldProjection?.[collectionName]\n const proj = projList ? new Set(projList) : undefined\n const project = (plaintext: string): string => {\n if (!proj) return plaintext\n const rec = JSON.parse(plaintext) as Record<string, unknown>\n const kept: Record<string, unknown> = {}\n if ('id' in rec) kept['id'] = rec['id'] // id ALWAYS preserved\n for (const f of proj) if (f in rec) kept[f] = rec[f]\n return JSON.stringify(kept)\n }\n\n for (const id of ids) {\n const env = await adapter.get(vaultName, collectionName, id)\n if (!env) continue\n // #748 defense-in-depth canary: `closure` is built exclusively through\n // tier-gated `Collection.list()`/`get()` (collection.ts `ensureHydrated`\n // / `#getRaw` both skip `_tier > 0` envelopes), so an elevated record\n // can never reach this raw `adapter.get` fetch — this should be\n // unreachable by construction. Not a user-facing guard; a cheap tripwire\n // in case that invariant ever regresses.\n if ((env._tier ?? 0) > 0) {\n throw new PartitionExtractionError(\n `#748: reKeyClosure fetched an elevated envelope (collection=\"${collectionName}\" id=\"${id}\" `\n + `_tier=${env._tier}) — this is unreachable by construction (walkClosure only ever adds ids `\n + `reachable through tier-gated Collection reads). Refusing to carry it into a partition.`,\n )\n }\n if (env._cek !== undefined) {\n // Per-record CEK: a naive `{ ...env }` spread would carry a\n // SOURCE-DEK-wrapped CEK into a bundle re-keyed under a different\n // destination DEK — silently undecryptable for the recipient.\n // Re-wrap: unwrap the CEK under the source DEK, re-encrypt the body\n // under the SAME CEK (decision 2 — CEK reused on re-key, preserving\n // the history-chain identity), then wrap the CEK under the fresh\n // destination DEK. The recipient gains access transitively once they\n // re-wrap the collection DEK under their KEK on adopt.\n // Identity is UNCHANGED by extraction — collection/id/_tier/_by all\n // travel with the record, and `vault` is deliberately unbound (the ADR's\n // constraint (c) exists precisely so adoption can re-home a partition).\n // So the same AAD opens the source and seals the destination (#1041).\n const aad = recordAadFor({ collection: collectionName, id }, env)\n const cek = await unwrapCek(env._cek, srcDek)\n const plaintext = await decrypt(env._iv, env._data, cek, aad)\n const { iv, data } = await encrypt(project(plaintext), cek, aad)\n const wrapped = await wrapCek(cek, destDek)\n out[id] = { ...env, _iv: iv, _data: data, _cek: wrapped }\n continue\n }\n const plaintext = await openEnvelopeJson({ collection: collectionName, id }, env, srcDek)\n const { iv, data } = await encrypt(project(plaintext), destDek, recordAadFor({ collection: collectionName, id }, env))\n out[id] = { ...env, _iv: iv, _data: data }\n }\n collections[collectionName] = out\n }\n\n return { collections, deks }\n}\n\n/**\n * Re-key the persisted JSON Schemas (`_schemas/<collection>`) for the\n * closure collections under the destination DEKs. Returns a\n * `{ collection: envelope }` map for the carried collections that actually\n * have a schema; collections without one are omitted.\n */\nexport async function reKeySchemas(\n vault: Vault,\n closure: Map<string, Set<string>>,\n destDeks: Map<string, EnclaveKey>,\n fieldProjection?: Record<string, readonly string[]>,\n): Promise<Record<string, EncryptedEnvelope>> {\n const { name: vaultName, adapter, getDEK } = vault._introspectState()\n const out: Record<string, EncryptedEnvelope> = {}\n\n for (const collectionName of closure.keys()) {\n // FR-7: skip a projected collection's schema — the narrowed shape no\n // longer matches the stored JSON Schema, so carrying it would assert a\n // contract the projected records violate (missing required fields).\n if (fieldProjection?.[collectionName]) continue\n const env = await adapter.get(vaultName, SCHEMAS_COLLECTION, collectionName)\n if (!env) continue // collection has no persisted schema — skip\n const destDek = destDeks.get(collectionName)\n if (!destDek) continue\n const srcDek = await getDEK(collectionName)\n const plaintext = await openEnvelopeJson({ collection: SCHEMAS_COLLECTION, id: collectionName }, env, srcDek)\n const { iv, data } = await encrypt(plaintext, destDek, recordAadFor({ collection: SCHEMAS_COLLECTION, id: collectionName }, env))\n out[collectionName] = { ...env, _iv: iv, _data: data }\n }\n return out\n}\n\nconst paddedIndex = (n: number): string => String(n).padStart(10, '0')\n\nexport interface ReKeyLedgerResult {\n /** { paddedIndex: re-encrypted entry envelope } for backup._internal._ledger. */\n readonly entries: Record<string, EncryptedEnvelope>\n /** Recomputed ledgerHead for the carried chain (index -1 when empty). */\n readonly head: { hash: string; index: number; ts: string }\n}\n\n/**\n * Build the carried `_ledger` chain for an extracted partition.\n * Filters source entries to the closure, RE-CHAINS them (fresh index + prevHash),\n * and re-encrypts under `ledgerDek`. The `payloadHash` is recomputed against the\n * re-keyed envelope ONLY for the latest `put` per (collection,id) — the entry\n * `verifyBackupIntegrity` cross-checks; earlier puts + deletes keep their source\n * `payloadHash` verbatim (recomputing an intermediate put would assert a false\n * hash for an older version). Amendments + out-of-closure entries are dropped;\n * `_ledger_deltas`/`_history` are deferred to slice 2.\n */\nexport async function reKeyLedger(\n vault: Vault,\n closure: Map<string, Set<string>>,\n reKeyedCollections: Record<string, Record<string, EncryptedEnvelope>>,\n ledgerDek: EnclaveKey,\n): Promise<ReKeyLedgerResult> {\n const { name: vaultName, adapter, getDEK } = vault._introspectState()\n const srcLedgerDek = await getDEK(LEDGER_COLLECTION)\n\n // 1. Load + decrypt source entries in index order.\n const ids = (await adapter.list(vaultName, LEDGER_COLLECTION)).sort()\n const srcEntries: LedgerEntry[] = []\n for (const id of ids) {\n const env = await adapter.get(vaultName, LEDGER_COLLECTION, id)\n if (!env) continue\n srcEntries.push(JSON.parse(await openEnvelopeJson({ collection: LEDGER_COLLECTION, id }, env, srcLedgerDek)) as LedgerEntry)\n }\n\n // 2. Keep closure put/delete entries (drop amendments + out-of-closure).\n const kept = srcEntries.filter(\n (e) => (e.op === 'put' || e.op === 'delete') && (closure.get(e.collection)?.has(e.id) ?? false),\n )\n\n // 3a. Reverse pass: index of the LATEST put per (collection,id).\n const latestPutIndex = new Map<string, number>()\n for (let i = kept.length - 1; i >= 0; i--) {\n const e = kept[i]!\n if (e.op !== 'put') continue\n const key = `${e.collection}/${e.id}`\n if (!latestPutIndex.has(key)) latestPutIndex.set(key, i)\n }\n\n // 3b. Forward re-chain + re-encrypt.\n const entries: Record<string, EncryptedEnvelope> = {}\n let prevHash = ''\n let last: LedgerEntry | undefined\n for (let i = 0; i < kept.length; i++) {\n const src = kept[i]!\n const key = `${src.collection}/${src.id}`\n const isLatestPut = src.op === 'put' && latestPutIndex.get(key) === i\n const reKeyedEnv = reKeyedCollections[src.collection]?.[src.id]\n const payloadHash = isLatestPut && reKeyedEnv\n ? await envelopePayloadHash(reKeyedEnv)\n : src.payloadHash\n const entry: LedgerEntry = {\n index: i,\n prevHash,\n op: src.op,\n collection: src.collection,\n id: src.id,\n version: src.version,\n ts: src.ts,\n actor: src.actor,\n payloadHash,\n ...(src.reason !== undefined ? { reason: src.reason } : {}),\n }\n const ledgerIdentity = { collection: LEDGER_COLLECTION, id: paddedIndex(i), version: i + 1 }\n const { iv, data } = await encrypt(canonicalJson(entry), ledgerDek, buildRecordAad(ledgerIdentity))\n entries[paddedIndex(i)] = buildRecordEnvelope(\n ledgerIdentity,\n { ts: entry.ts, iv, data},\n )\n prevHash = await hashEntry(entry)\n last = entry\n }\n\n return {\n entries,\n head: last ? { hash: prevHash, index: last.index, ts: last.ts } : { hash: '', index: -1, ts: '' },\n }\n}\n\n/** Build the AAD binding for chunk integrity: \"{eTag}:{chunkIndex}:{chunkCount}\".\n * Mirrors `chunkAAD` in blob-set.ts — the eTag is preserved verbatim across\n * the transfer (see `reKeyBlobs`), so the same AAD that bound a chunk in the\n * source keeps binding it in the bundle. */\nfunction chunkAAD(eTag: string, chunkIndex: number, chunkCount: number): Uint8Array {\n return new TextEncoder().encode(`${eTag}:${chunkIndex}:${chunkCount}`)\n}\n\n/** Carried blob internals + the fresh transfer `_blob` DEK (present only when\n * the closure references at least one chunk-based blob). */\nexport interface ReKeyBlobsResult {\n /** `_blob_slots_<C>` / `_blob_versions_<C>` / `_blob_index` / `_blob_chunks`\n * envelopes for the bundle's `_internal` map. */\n readonly internal: Record<string, Record<string, EncryptedEnvelope>>\n /** Fresh transfer `_blob` DEK — seal it so owner-creation wraps it under the\n * recipient KEK. Undefined when no blob travels (source keyring untouched). */\n readonly blobDek?: EnclaveKey\n}\n\n/**\n * Carry the FK-closed slice's blobs — HARDENED key handling (no master-key leak).\n *\n * The source vault's shared `_blob` DEK decrypts (or unwraps the content CEK of)\n * EVERY blob in the source. Sealing it into the transfer would hand the recipient\n * a key to blobs far outside their slice. Instead we mint a **fresh transfer\n * `_blob` DEK** and arrange every carried blob into per-blob-CEK mode under it:\n *\n * - **erasable blob** (`_cek` present): unwrap the per-blob content CEK under the\n * SOURCE `_blob` DEK, re-wrap it under the FRESH transfer DEK. Chunks travel\n * **verbatim** (still ciphertext under that same content CEK — passthrough).\n * - **legacy blob** (no `_cek`, chunks under the shared `_blob` DEK): promote it\n * IN-BUNDLE — mint a fresh content CEK, decrypt each chunk under the source\n * `_blob` DEK and re-encrypt under the content CEK (same AAD, so the eTag-bound\n * integrity holds), then wrap the content CEK under the transfer DEK. The\n * SOURCE is never mutated (non-destructive), and the bundle never holds\n * plaintext blob bytes (zero-knowledge preserved).\n *\n * **eTag identity is preserved** (not re-HMAC'd). eTags are HMAC-keyed off the\n * `_blob` DEK, but they are stored as OPAQUE keys (`_blob_index/<eTag>`,\n * `_blob_chunks/<eTag>_<i>`) and never recomputed on read — only on a `put()`\n * for dedup. Keeping them verbatim keeps every slot/version/index/chunk key and\n * the chunk AAD coherent with zero chunk-key churn. The only consequence: a\n * future `put()` of identical bytes in the adopted vault computes a different\n * eTag (HMAC under the fresh DEK) and will NOT dedup against the carried blob —\n * an accepted, documented trade for hardened key isolation.\n *\n * Slots/versions are re-keyed under their parent collection's destination DEK\n * (honoring `fieldProjection` — projected-out blob fields' slots never travel);\n * `BlobObject.refCount` is recomputed from carried references only.\n *\n * `external` slots reference an unencrypted shared-bucket object that is not in\n * the bundle — their slot metadata travels (so the catalog entry survives) but\n * the bytes do not (a documented v1 limitation; their eTag is `''`).\n */\nexport async function reKeyBlobs(\n vault: Vault,\n closure: Map<string, Set<string>>,\n destDeks: Map<string, EnclaveKey>,\n fieldProjection?: Record<string, readonly string[]>,\n): Promise<ReKeyBlobsResult> {\n const { name: vaultName, adapter, getDEK } = vault._introspectState()\n const internal: Record<string, Record<string, EncryptedEnvelope>> = {}\n\n // travel set: eTag → number of carried references (slots + versions) within\n // the closure. Recomputed refCount, NOT the source's (which may count\n // out-of-slice referrers, leaving the adopted blob un-GC-able).\n const carriedRefs = new Map<string, number>()\n const addRef = (eTag: string): void => {\n if (!eTag) return // external slot — no chunk-based blob to carry\n carriedRefs.set(eTag, (carriedRefs.get(eTag) ?? 0) + 1)\n }\n\n const place = (collection: string, id: string, env: EncryptedEnvelope): void => {\n let bucket = internal[collection]\n if (!bucket) { bucket = {}; internal[collection] = bucket }\n bucket[id] = env\n }\n\n // ── Slots + versions (parent-collection-DEK keyed) ─────────────────\n for (const [collectionName, ids] of closure) {\n const destDek = destDeks.get(collectionName)\n if (!destDek) continue\n const srcDek = await getDEK(collectionName)\n const projList = fieldProjection?.[collectionName]\n const proj = projList ? new Set(projList) : undefined\n\n // Slots: one envelope per record, a `{ slotName: SlotRecord }` map.\n const slotsCollection = `${BLOB_SLOTS_PREFIX}${collectionName}`\n for (const id of ids) {\n // #767: carry any in-flight `_blob_intent` marker for this record,\n // re-keyed under the SAME destination DEK as its owning collection\n // (mirrors `dumpVault`'s backup allowlist, which carries `_blob_intent`\n // for the identical crash-recovery reason — a partition extracted\n // mid-shred/mid-rehome must carry the marker so resume-on-touch heals\n // it post-restore). Checked independently of the slot-map fetch below:\n // a marker can be present even when the slot map has already been\n // touched or removed by the in-flight operation.\n const intentId = `${collectionName}::${id}`\n const intentEnv = await adapter.get(vaultName, BLOB_INTENT_COLLECTION, intentId)\n if (intentEnv) {\n const intentPlain = await openEnvelopeJson({ collection: BLOB_INTENT_COLLECTION, id: intentId }, intentEnv, srcDek)\n const intentBody = await writeEnvelopeBody({ collection: BLOB_INTENT_COLLECTION, id: intentId, version: intentEnv._v }, intentPlain, destDek)\n place(BLOB_INTENT_COLLECTION, intentId, { ...intentEnv, ...intentBody })\n }\n\n const env = await adapter.get(vaultName, slotsCollection, id)\n if (!env) continue\n // #748 defense-in-depth canary — see the matching one in `reKeyClosure`:\n // `ids` comes from the same tier-gated `closure`, so an elevated\n // record's slot map should never be reachable here. Unreachable by\n // construction; a cheap tripwire, not a user-facing guard.\n if ((env._tier ?? 0) > 0) {\n throw new PartitionExtractionError(\n `#748: reKeyBlobs fetched an elevated slot-map envelope (collection=\"${slotsCollection}\" `\n + `id=\"${id}\" _tier=${env._tier}) — this is unreachable by construction. Refusing to carry it into a partition.`,\n )\n }\n const slots = JSON.parse(await openEnvelopeJson({ collection: slotsCollection, id }, env, srcDek)) as Record<string, SlotRecord>\n // FR-7: drop projected-out blob fields' slots (slot names are blob field\n // names) — their eTags then never enter the travel set.\n const kept: Record<string, SlotRecord> = {}\n for (const [slotName, slot] of Object.entries(slots)) {\n if (proj && !proj.has(slotName)) continue\n // #769: strip the internal `pendingRelease` resume breadcrumb before\n // it travels. It is a SOURCE-VAULT-LOCAL rehome marker (see\n // `SlotRecord.pendingRelease`'s doc comment) pointing at an old eTag\n // awaiting release — meaningless, and potentially misleading, once\n // detached from the source vault (the eTag it names may be absent\n // from the destination). Unlike `_blob_intent` above (carried, so\n // resume-on-touch heals it post-restore), this breadcrumb must NOT\n // travel into a cross-vault partition.\n const { pendingRelease, ...strippedSlot } = slot\n void pendingRelease\n kept[slotName] = strippedSlot\n addRef(slot.eTag)\n }\n if (Object.keys(kept).length === 0) continue\n const { iv, data } = await encrypt(JSON.stringify(kept), destDek, recordAadFor({ collection: slotsCollection, id }, env))\n place(slotsCollection, id, { ...env, _iv: iv, _data: data })\n }\n\n // Versions: key = `${recordId}::${slotName}::${label}`; each an independent\n // refCount hold on its eTag.\n const versionsCollection = `${BLOB_VERSIONS_PREFIX}${collectionName}`\n const versionKeys = await adapter.list(vaultName, versionsCollection)\n for (const key of versionKeys) {\n const [recordId, slotName] = key.split('::')\n if (recordId === undefined || !ids.has(recordId)) continue\n if (proj && slotName !== undefined && !proj.has(slotName)) continue\n const env = await adapter.get(vaultName, versionsCollection, key)\n if (!env) continue\n // #748 defense-in-depth canary — see `reKeyClosure`'s. Unreachable by\n // construction; a cheap tripwire, not a user-facing guard.\n if ((env._tier ?? 0) > 0) {\n throw new PartitionExtractionError(\n `#748: reKeyBlobs fetched an elevated version-record envelope (collection=\"${versionsCollection}\" `\n + `key=\"${key}\" _tier=${env._tier}) — this is unreachable by construction. Refusing to carry it into a partition.`,\n )\n }\n const record = JSON.parse(await openEnvelopeJson({ collection: versionsCollection, id: key }, env, srcDek)) as VersionRecord\n addRef(record.eTag)\n const { iv, data } = await encrypt(JSON.stringify(record), destDek, recordAadFor({ collection: versionsCollection, id: key }, env))\n place(versionsCollection, key, { ...env, _iv: iv, _data: data })\n }\n }\n\n // No chunk-based blob in the closure → carry nothing, mint nothing. Guarded\n // so `getDEK('_blob')` does not auto-mint + persist a phantom DEK on the\n // source keyring (mirrors the carryLedger non-destructive guard).\n if (carriedRefs.size === 0) return { internal }\n\n // ── Index + chunks (re-keyed under a FRESH transfer `_blob` DEK) ────\n const srcBlobDek = await getDEK(BLOB_COLLECTION)\n const transferBlobDek = await generateDEK()\n\n for (const [eTag, refCount] of carriedRefs) {\n const idxEnv = await adapter.get(vaultName, BLOB_INDEX_COLLECTION, eTag)\n if (!idxEnv) continue // dangling slot reference — nothing to carry\n const blob = JSON.parse(await openEnvelopeJson({ collection: BLOB_INDEX_COLLECTION, id: eTag }, idxEnv, srcBlobDek)) as BlobObject\n\n // Resolve the per-blob content CEK; passthrough vs. in-bundle promotion.\n let contentCek: EnclaveKey\n let chunksPassthrough: boolean\n if (blob._cek !== undefined) {\n contentCek = await unwrapCek(blob._cek, srcBlobDek)\n chunksPassthrough = true\n } else {\n // Legacy: promote to per-blob-CEK for the bundle (source untouched).\n contentCek = await generateDEK()\n chunksPassthrough = false\n }\n\n // Chunks: verbatim for an already-erasable blob; decrypt-then-re-encrypt\n // under the fresh content CEK for a legacy blob (same eTag-bound AAD).\n for (let i = 0; i < blob.chunkCount; i++) {\n const chunkId = `${eTag}_${i}`\n const chunkEnv = await adapter.get(vaultName, BLOB_CHUNKS_COLLECTION, chunkId)\n if (!chunkEnv) {\n throw new PartitionExtractionError(\n `reKeyBlobs: blob chunk ${i}/${blob.chunkCount} missing for eTag \"${eTag}\"; `\n + `cannot carry an incomplete blob into the partition.`,\n )\n }\n if (chunksPassthrough) {\n place(BLOB_CHUNKS_COLLECTION, chunkId, chunkEnv)\n } else {\n const aad = chunkAAD(eTag, i, blob.chunkCount)\n const plain = await decryptBytesWithAAD(chunkEnv._iv, chunkEnv._data, srcBlobDek, aad)\n const { iv, data } = await encryptBytesWithAAD(plain, contentCek, aad)\n place(BLOB_CHUNKS_COLLECTION, chunkId, { ...chunkEnv, _iv: iv, _data: data })\n }\n }\n\n // Index: per-blob-CEK mode under the transfer DEK, refCount corrected.\n // Drop any transient `_cekPending` (never set on a settled blob — possible\n // only if the source is mid-migration; we set a fresh settled `_cek`).\n const { _cekPending, ...rest } = blob\n void _cekPending\n const carried: BlobObject = { ...rest, refCount, _cek: await wrapCek(contentCek, transferBlobDek) }\n const { iv, data } = await encrypt(JSON.stringify(carried), transferBlobDek, recordAadFor({ collection: BLOB_INDEX_COLLECTION, id: eTag }, idxEnv))\n place(BLOB_INDEX_COLLECTION, eTag, { ...idxEnv, _iv: iv, _data: data })\n }\n\n return { internal, blobDek: transferBlobDek }\n}\n\n/** A minted transfer key (raw 32 bytes) + the seal carrying the DEK set. */\nexport interface SealResult {\n readonly seal: TransferSealPayload\n readonly transferKey: Uint8Array\n}\n\n/**\n * Mint a random 32-byte transfer key, export each DEK to raw bytes, and\n * AES-256-GCM-seal the `{ collection: base64(rawDEK) }` map under the\n * transfer key. The transfer key is returned to the caller out-of-band;\n * only the sealed bytes travel in the bundle. Layout: iv(12) ‖ ct ‖ tag.\n */\nexport async function sealDeks(deks: Map<string, EnclaveKey>): Promise<SealResult> {\n const dekMap: Record<string, string> = {}\n for (const [collection, dek] of deks) {\n const raw = await crypto.subtle.exportKey('raw', dek)\n dekMap[collection] = bufferToBase64(raw)\n }\n\n const transferKey = crypto.getRandomValues(new Uint8Array(32))\n const key = await crypto.subtle.importKey('raw', transferKey, 'AES-GCM', false, ['encrypt'])\n const iv = crypto.getRandomValues(new Uint8Array(12))\n const plaintext = new TextEncoder().encode(JSON.stringify(dekMap))\n const ct = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, plaintext)\n\n const combined = new Uint8Array(iv.byteLength + ct.byteLength)\n combined.set(iv, 0)\n combined.set(new Uint8Array(ct), iv.byteLength)\n\n const sealId = bufferToBase64(crypto.getRandomValues(new Uint8Array(12)))\n return {\n seal: { v: 1, alg: 'aes-256-gcm-pre-shared', sealId, payload: bufferToBase64(combined) },\n transferKey,\n }\n}\n\nexport interface ExtractPartitionResult {\n readonly bundleBytes: Uint8Array\n /** Raw 32-byte transfer key — deliver out-of-band; required to adopt. */\n readonly transferKey: Uint8Array\n readonly sealId: string\n /**\n * #759: outbound FK edges whose referenced parent was excluded from the\n * partition (missing, or tier-elevated and therefore invisible). The\n * child record still carries its FK value; the parent did not travel —\n * callers should surface this to the operator rather than assume\n * referential completeness.\n */\n readonly danglingRefs: ReadonlyArray<DanglingRefNotice>\n}\n\n/**\n * Extract a re-keyed, transfer-sealed partition. Owner-only\n * (invariant 5): producing a standalone re-keyed vault is an\n * ownership operation. Non-destructive on the source.\n */\nexport type ExtractPartitionOptions = WalkClosureOptions & {\n readonly compression?: 'auto' | 'brotli' | 'gzip' | 'none'\n readonly carrySchemas?: boolean\n readonly carryLedger?: boolean\n /**\n * FR-7 structural field projection: per-collection allow-list of fields\n * to keep. Non-listed fields are dropped from each record BEFORE\n * re-encryption (so they never travel in the bundle); `id` is always\n * preserved. A projected collection's persisted schema is NOT carried.\n * Absent/empty → un-projected behavior (byte-identical to today).\n */\n readonly fieldProjection?: Record<string, readonly string[]>\n}\n\n/**\n * Public extract-partition entry — gated behind `cargoStrategy: withCargo()`\n * (S4). Routes through the source vault's cargo strategy so an un-opted-in\n * caller hits `NO_CARGO`'s throw; `withCargo()` dynamically imports and runs\n * {@link extractPartitionCore} (the crypto engine, kept out of the floor).\n */\nexport async function extractPartition(\n vault: Vault,\n opts: ExtractPartitionOptions,\n): Promise<ExtractPartitionResult> {\n return vault.cargoStrategy.extractPartition(vault, opts)\n}\n\n/**\n * The extract-partition crypto engine — reached only when `withCargo()` is\n * opted in (via the lazy `with-cargo/active.ts` chunk). Body unchanged from the\n * hardened implementation; only the public gate wraps it.\n */\nexport async function extractPartitionCore(\n vault: Vault,\n opts: ExtractPartitionOptions,\n): Promise<ExtractPartitionResult> {\n // FR-6: extract-and-sever is the inalienability-floor half — owner-only. A\n // custodian operates fully but must NEVER produce a standalone re-keyed\n // partition (that would let it sever a copy out from under the sealed owner).\n // Explicit assertion so the security boundary is auditable at this site.\n if (vault.role === 'custodian') {\n throw new PartitionExtractionError(\n 'extractPartition is owner-only; a custodian cannot extract-and-sever '\n + '(FR-6: producing a re-keyed standalone partition is an ownership operation; use the Deed owner).',\n )\n }\n if (vault.role !== 'owner') {\n throw new PartitionExtractionError(\n `extractPartition requires the 'owner' role on the source vault; caller is '${vault.role}'. `\n + `Producing a re-keyed standalone partition is an ownership operation.`,\n )\n }\n\n // Persisted-schema writes (collection({ persistJsonSchema: true })) are fire-\n // and-forget queued onto vault._pendingSchemaWrites — a caller that does\n // `collection() → put() → extractPartition({ carrySchemas: true })` in quick\n // succession can hit a window where _schemas/<col> is not yet on disk and\n // reKeySchemas silently drops the row. Drain BEFORE reKeySchemas reads.\n if (opts.carrySchemas) await vault._drainPendingSchemaWrites()\n\n const { closure, danglingRefs } = await walkClosure(vault, opts)\n const { collections, deks } = await reKeyClosure(vault, closure, opts.fieldProjection)\n\n // carryLedger: mint a fresh _ledger DEK, build the carried chain, and\n // SEAL the ledger DEK alongside the data DEKs so owner-creation wraps it into the\n // recipient keyring (lets them decrypt + verify the chain). Must run BEFORE\n // sealDeks.\n let ledgerHead: { hash: string; index: number; ts: string } | undefined\n let ledgerEntries: Record<string, EncryptedEnvelope> | undefined\n if (opts.carryLedger && vault._getLedgerOrNull() !== null) {\n // Skip when the source vault has no history strategy: reKeyLedger's first\n // `getDEK(LEDGER_COLLECTION)` would auto-mint and persist a phantom\n // _ledger DEK on the source keyring (contradicting \"non-destructive on\n // the source\"), and there's nothing to carry anyway. Mirrors the same\n // null-guard the source audit-append uses below.\n const ledgerDek = await generateDEK()\n const built = await reKeyLedger(vault, closure, collections, ledgerDek)\n if (built.head.index >= 0) {\n ledgerEntries = built.entries\n ledgerHead = built.head\n deks.set(LEDGER_COLLECTION, ledgerDek)\n }\n }\n\n // Carry the slice's blobs — re-keyed under a FRESH transfer `_blob` DEK\n // (HARDENED: never carries the source's shared blob DEK). Runs BEFORE\n // sealDeks so the fresh DEK is sealed alongside the data DEKs; non-destructive\n // on the source (mints + reads `_blob` only when the closure has blobs).\n const blobs = await reKeyBlobs(vault, closure, deks, opts.fieldProjection)\n if (blobs.blobDek) deks.set(BLOB_COLLECTION, blobs.blobDek)\n\n // Build _internal (schemas + ledger + blobs). reKeySchemas reads data-\n // collection DEKs only, so it is unaffected by the _ledger DEK added above.\n const internalSchemas = opts.carrySchemas ? await reKeySchemas(vault, closure, deks, opts.fieldProjection) : {}\n const internal: Record<string, Record<string, EncryptedEnvelope>> = {}\n if (Object.keys(internalSchemas).length > 0) internal[SCHEMAS_COLLECTION] = internalSchemas\n if (ledgerEntries) internal[LEDGER_COLLECTION] = ledgerEntries\n for (const [collection, records] of Object.entries(blobs.internal)) internal[collection] = records\n const hasInternal = Object.keys(internal).length > 0\n\n const { seal, transferKey } = await sealDeks(deks)\n\n // Source-side audit: record that a partition\n // was handed over. Non-destructive — an audit append, no record touched.\n // No-op when the source vault has no history strategy. append() fills\n // index/prevHash/ts and (since actor is '') the ledger's configured actor.\n await vault._getLedgerOrNull()?.append({\n op: 'lifecycle',\n collection: '',\n id: '',\n version: 0,\n actor: '',\n payloadHash: '',\n reason: `partition-handed-over:${seal.sealId}`,\n })\n\n // Build the dump JSON: unowned (empty keyrings), empty ledger (default),\n // re-keyed collections only.\n const { name: vaultName } = vault._introspectState()\n const backup = {\n _noydb_backup: NOYDB_BACKUP_VERSION,\n _compartment: vaultName,\n _exported_at: new Date().toISOString(),\n _exported_by: '', // unowned — no source user travels\n keyrings: {},\n collections,\n ...(hasInternal ? { _internal: internal } : {}),\n ...(ledgerHead ? { ledgerHead: { hash: ledgerHead.hash, index: ledgerHead.index, ts: ledgerHead.ts } } : {}),\n }\n const bodyJsonStr = JSON.stringify(buildExtractedPartitionWrapper(JSON.stringify(backup), seal))\n\n // An extracted partition is a NEW vault, not a re-export of the source —\n // mint a fresh handle rather than reusing the source's stable ULID\n // (which would collide if a recipient imports both source + partition).\n const handle = generateULID()\n const bundleBytes = await assemblePodContainer({\n handle,\n bodyJsonStr,\n compression: opts.compression,\n headerExtras: {\n bundleKind: 'extracted-partition',\n transferSeal: { v: seal.v, alg: seal.alg, sealId: seal.sealId }, // indicator only\n },\n })\n\n return { bundleBytes, transferKey, sealId: seal.sealId, danglingRefs }\n}\n"],"mappings":"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","names":["plaintext","iv","data"]}
@@ -1 +0,0 @@
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 '../../kernel/enclave/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 +0,0 @@
1
- {"version":3,"sources":["../src/with-shape/persisted-schemas/storage.ts"],"sourcesContent":["/**\n * Read / write the per-collection persisted-schema envelope. Mirrors the\n * standard noy-db record envelope shape and is **AES-GCM encrypted with\n * the collection's DEK** — the schema body (field names, enum values,\n * constraints) is sensitive metadata, so it gets the same encryption\n * envelope as the records it describes.\n *\n * Storage layout:\n *\n * <vault>/_schemas/<collection> → EncryptedEnvelope\n *\n * The DEK passed to {@link savePersistedSchema} / {@link loadPersistedSchema}\n * is the same key the collection uses for its records.\n *\n * @module\n */\n\nimport { buildRecordAad, buildRecordEnvelope, encrypt, openEnvelopeJson, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { PersistedSchemaEnvelope } from './types.js'\n\n/** Reserved collection name where persisted schemas live. */\nexport const SCHEMAS_COLLECTION = '_schemas' as const\n\n/**\n * A decrypted persisted-schema read paired with the wrapping envelope's\n * `_v`. The version is the optimistic-concurrency token: pass it back to\n * {@link savePersistedSchema} as `expectedVersion` to make the write a CAS\n * that rejects a stale put (see #583 — the `_schemas` record is shared by\n * the JSON-Schema writer and the classified-marker writer).\n */\nexport interface PersistedSchemaEntry {\n /** Wrapping envelope `_v` (0 when no record exists yet). */\n readonly version: number\n /** Decrypted payload, or `undefined` when absent/corrupt/wrong-DEK. */\n readonly payload: PersistedSchemaEnvelope | undefined\n}\n\n/**\n * Read the persisted-schema envelope for one collection together with its\n * wrapping `_v`. The version reflects the raw stored envelope even when the\n * payload cannot be decrypted/parsed (so a CAS still guards a corrupt record\n * rather than silently clobbering it).\n */\nexport async function loadPersistedSchemaEntry(\n store: NoydbStore,\n vault: string,\n collection: string,\n dek: EnclaveKey,\n): Promise<PersistedSchemaEntry> {\n const envelope = await store.get(vault, SCHEMAS_COLLECTION, collection)\n if (!envelope) return { version: 0, payload: undefined }\n const version = envelope._v\n try {\n const plaintext = await openEnvelopeJson({ collection: SCHEMAS_COLLECTION, id: collection }, envelope, dek)\n const parsed = JSON.parse(plaintext) as PersistedSchemaEnvelope\n if (parsed._noydb_schema !== 1) return { version, payload: undefined }\n return { version, payload: parsed }\n } catch {\n return { version, payload: undefined }\n }\n}\n\n/**\n * Read and decrypt the persisted-schema envelope for one collection.\n * Returns `undefined` when no envelope has been written or when decryption\n * fails (e.g. wrong DEK passed). Tolerates corrupted records — JSON parse\n * failures surface as `undefined`, mirroring `_meta/handle`'s contract.\n */\nexport async function loadPersistedSchema(\n store: NoydbStore,\n vault: string,\n collection: string,\n dek: EnclaveKey,\n): Promise<PersistedSchemaEnvelope | undefined> {\n return (await loadPersistedSchemaEntry(store, vault, collection, dek)).payload\n}\n\n/**\n * Encrypt and persist a schema envelope for one collection. Always\n * overwrites any prior write (callers gate on hash equality before calling\n * to avoid no-op writes).\n *\n * Pass `expectedVersion` — the `version` from {@link loadPersistedSchemaEntry}\n * captured at load time — to make the write an optimistic CAS: on a store with\n * `casAtomic`, `store.put` throws {@link ConflictError} when the record was\n * bumped concurrently, so a lost update on the shared `_schemas` record is\n * rejected instead of silently clobbering the other writer's field (#583).\n * When omitted, keeps the legacy unconditional last-writer-wins behaviour.\n */\nexport async function savePersistedSchema(\n store: NoydbStore,\n vault: string,\n collection: string,\n dek: EnclaveKey,\n payload: PersistedSchemaEnvelope,\n expectedVersion?: number,\n): Promise<void> {\n const json = JSON.stringify(payload)\n const baseVersion = expectedVersion ?? (await store.get(vault, SCHEMAS_COLLECTION, collection))?._v ?? 0\n const identity = { collection: SCHEMAS_COLLECTION, id: collection, version: baseVersion + 1 }\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n const env = buildRecordEnvelope(\n identity,\n { iv, data },\n )\n await store.put(vault, SCHEMAS_COLLECTION, collection, env, expectedVersion)\n}\n"],"mappings":";;;;;;;;;;;;AAsBO,IAAM,qBAAqB;AAsBlC,eAAsB,yBACpB,OACA,OACA,YACA,KAC+B;AAC/B,QAAM,WAAW,MAAM,MAAM,IAAI,OAAO,oBAAoB,UAAU;AACtE,MAAI,CAAC,SAAU,QAAO,EAAE,SAAS,GAAG,SAAS,OAAU;AACvD,QAAM,UAAU,SAAS;AACzB,MAAI;AACF,UAAM,YAAY,MAAM,iBAAiB,EAAE,YAAY,oBAAoB,IAAI,WAAW,GAAG,UAAU,GAAG;AAC1G,UAAM,SAAS,KAAK,MAAM,SAAS;AACnC,QAAI,OAAO,kBAAkB,EAAG,QAAO,EAAE,SAAS,SAAS,OAAU;AACrE,WAAO,EAAE,SAAS,SAAS,OAAO;AAAA,EACpC,QAAQ;AACN,WAAO,EAAE,SAAS,SAAS,OAAU;AAAA,EACvC;AACF;AAQA,eAAsB,oBACpB,OACA,OACA,YACA,KAC8C;AAC9C,UAAQ,MAAM,yBAAyB,OAAO,OAAO,YAAY,GAAG,GAAG;AACzE;AAcA,eAAsB,oBACpB,OACA,OACA,YACA,KACA,SACA,iBACe;AACf,QAAM,OAAO,KAAK,UAAU,OAAO;AACnC,QAAM,cAAc,oBAAoB,MAAM,MAAM,IAAI,OAAO,oBAAoB,UAAU,IAAI,MAAM;AACvG,QAAM,WAAW,EAAE,YAAY,oBAAoB,IAAI,YAAY,SAAS,cAAc,EAAE;AAC5F,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,MAAM,KAAK,eAAe,QAAQ,CAAC;AACtE,QAAM,MAAM;AAAA,IACV;AAAA,IACA,EAAE,IAAI,KAAK;AAAA,EACb;AACA,QAAM,MAAM,IAAI,OAAO,oBAAoB,YAAY,KAAK,eAAe;AAC7E;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-commit/tx/atomic-eligibility.ts","../src/kernel/best-effort-revert.ts","../src/kernel/tx-write-gate.ts","../src/with-commit/tx/transaction.ts"],"sourcesContent":["/**\n * #893/#906-prep — the atomic-commit eligibility gate.\n *\n * `runTransaction` (Task 5) consults `canCommitAtomically` right after the\n * body returns, to decide whether the whole staged batch can be prepared and\n * submitted as ONE `store.tx(ops)` call instead of the per-op abortable\n * path (Phase 1 pre-flight + Phase 2 Collection-layer execute + Phase 3\n * best-effort revert — see `transaction.ts`'s module doc).\n *\n * Pure decision logic: no side effects, no store reads. Every input is\n * already sitting on `db` / `ctx`.\n *\n * ALL of the following must hold. (Commit-time changeset invariants are NOT a\n * condition: that phase lives after Phase 2 in `runTransaction` and runs\n * unchanged on either path — a failing invariant reverts the atomic batch\n * through the same `ctx._executed` plan.)\n *\n * 1. The store declares `capabilities.txAtomic === true` AND implements\n * `tx()`. Both — mirrors the pairing rule `bestEffortRevert` already\n * enforces (`kernel/best-effort-revert.ts:78`): an undeclared `tx()` is\n * never used, even if present.\n * 2. Not an amendment transaction (`ctx._amendment === false`) — amendments\n * need the guard-registry change-set + invariant machinery the atomic\n * path skips entirely.\n * 3. No `(vault, collection, id)` is touched twice in the batch. The\n * abortable path's Phase 1 snapshots each key once and Phase 2 replays\n * every op against that same snapshot in order; a single `store.tx()`\n * write set can't express \"this key changes twice in sequence.\"\n * 4. Every touched collection reports `_txAtomicSafe(op.type)` — see that\n * method's doc comment on `Collection` for what it excludes and why.\n * #931: of the wrapper-level write signals, only a live `onBeforeWrite`\n * hook still excludes — it may REFUSE a write, which only\n * `Collection.put()`/`.delete()` honors. After-hooks and the\n * `afterPut`/`afterDelete` observe bus are refusal-free observers, so\n * the atomic path fires them itself per leg post-finalize\n * (`Collection._fireAtomicAfterWrite`) instead of forfeiting the batch.\n * Delete gates on the enforcer's `_deleteCascadesPossible(name)` (#922):\n * `Vault.enforceRefsOnDelete` cascades from THREE sources (lookup-ref\n * edges, classic inbound refs, managed-link `onDelete`), and the Vault\n * predicate unions exactly those three — a `getInbound`-only check would\n * be narrower and wrongly admit unsafe atomic deletes, because\n * `_prepareDelete` runs the cascades DURING prepare (see\n * `with-shape/links/vault-facade.ts` `enforceRefsOnDelete`).\n *\n * @internal\n */\n\nimport type { Noydb } from '../../kernel/noydb.js'\nimport type { TxContext } from './transaction.js'\nimport { keyOf } from './transaction.js'\n\nexport function canCommitAtomically(db: Noydb, ctx: TxContext): boolean {\n const store = db._store\n if (store.capabilities?.txAtomic !== true || typeof store.tx !== 'function') return false\n if (ctx._amendment) return false\n\n const seen = new Set<string>()\n for (const op of ctx._ops) {\n const key = keyOf(op)\n if (seen.has(key)) return false\n seen.add(key)\n }\n\n for (const op of ctx._ops) {\n const coll = db.vault(op.vaultName).collection(op.collectionName)\n if (!coll._txAtomicSafe(op.type)) return false\n }\n\n return true\n}\n","/**\n * Best-effort reverse-revert — the shape shared by two independent rollback\n * paths that used to duplicate it: satellite fan-out\n * (`with-shape/satellites/fanout.ts`'s `revertAndCompensate`) and\n * multi-record transactions (`with-commit/tx/transaction.ts`'s\n * `revertExecuted`). Both revert a list of already-executed writes on\n * failure by walking them in REVERSE order and, per leg, restoring the\n * captured prior envelope straight through the raw adapter — `put(prior)`\n * when one existed, `delete` when the leg was a fresh insert — bypassing\n * the Collection layer so the revert itself doesn't re-fire\n * encryption/ledger/change-event machinery. Each leg's raw-adapter revert\n * is best-effort: a throw is swallowed so a revert-path failure never masks\n * the original error that triggered the rollback, and the loop moves on to\n * the next leg.\n *\n * An optional per-leg `compensate` callback runs immediately after a\n * successful raw-adapter revert (still inside the same try, so a throwing\n * callback is swallowed the same way as a throwing raw revert). What the\n * callback does is entirely caller-defined — satellites' fan-out fires\n * `_compensateRevertedWrite` only for legs whose write actually landed\n * (#596); with-commit's transaction executor invalidates the Collection\n * cache. Neither concern belongs here: this helper only knows the generic\n * reverse/best-effort/raw-revert shape.\n *\n * Lives in `kernel/` — not a `with-*` service — so both consumers (one a\n * gated service, `with-commit`; one an always-on schema feature,\n * `with-shape/satellites`) can import it without creating a\n * `with-* → with-*` edge, which would force either family to always bundle\n * the other and defeat the opt-in tree-shaking both are built around.\n *\n * @internal — implementation-sharing only, not part of the public surface.\n */\nimport type { EncryptedEnvelope, StoreCapabilities, TxOp } from './types.js'\n\n/** One already-executed write a revert pass needs to unwind. */\nexport interface BestEffortRevertLeg {\n readonly vaultName: string\n readonly collectionName: string\n readonly id: string\n /** Envelope captured before the write, or `null` if the id didn't exist yet. */\n readonly prior: EncryptedEnvelope | null\n}\n\n/** The subset of `NoydbStore` a raw-adapter revert needs. */\nexport interface BestEffortRevertAdapter {\n put(vaultName: string, collectionName: string, id: string, envelope: EncryptedEnvelope): Promise<void>\n delete(vaultName: string, collectionName: string, id: string): Promise<void>\n /**\n * Optional storage-layer transaction (#886). When the store declares\n * `txAtomic`, the whole revert is submitted as ONE operation instead of a\n * per-leg loop, so a crash mid-revert can no longer leave the vault\n * half-unwound.\n */\n tx?(ops: readonly TxOp[]): Promise<void>\n readonly capabilities?: StoreCapabilities\n}\n\n/**\n * Revert `executed` in reverse order via the raw adapter, best-effort.\n * `compensate`, when supplied, runs once per leg immediately after that\n * leg's own raw revert succeeds.\n */\nexport async function bestEffortRevert<T extends BestEffortRevertLeg>(\n executed: readonly T[],\n adapter: BestEffortRevertAdapter,\n compensate?: (leg: T) => Promise<void> | void,\n): Promise<void> {\n const legs = [...executed].reverse()\n\n // #886 — atomic revert when the store can do it. The legs already carry RAW\n // prior envelopes (captured pre-write), which is exactly the shape `tx()`\n // wants, so this needs none of the Collection-layer machinery that makes\n // delegating the FORWARD write path a much larger job.\n //\n // Failure stays best-effort: if the store rejects the batch we fall through\n // to the per-leg loop rather than surfacing a revert-path error, because a\n // revert failure must never mask the original error that triggered it.\n if (adapter.capabilities?.txAtomic === true && typeof adapter.tx === 'function') {\n const ops: TxOp[] = legs.map(leg =>\n leg.prior !== null\n ? { type: 'put', vault: leg.vaultName, collection: leg.collectionName, id: leg.id, envelope: leg.prior }\n : { type: 'delete', vault: leg.vaultName, collection: leg.collectionName, id: leg.id },\n )\n try {\n await adapter.tx(ops)\n if (compensate) for (const leg of legs) { try { await compensate(leg) } catch { /* best-effort */ } }\n return\n } catch {\n // fall through to the per-leg path below\n }\n }\n\n for (const leg of legs) {\n try {\n if (leg.prior !== null) {\n await adapter.put(leg.vaultName, leg.collectionName, leg.id, leg.prior)\n } else {\n await adapter.delete(leg.vaultName, leg.collectionName, leg.id)\n }\n if (compensate) await compensate(leg)\n } catch {\n // best-effort — a revert-path failure must not mask the original\n // error that triggered the rollback (matches both callers' prior\n // independent implementations).\n }\n }\n}\n","/**\n * #1420 — the in-flight-write gate.\n *\n * ## The defect this closes\n *\n * `runTransaction`'s commit opens with a **pre-flight**: it re-reads every\n * touched envelope and enforces the caller's `expectedVersion`. That check is\n * only as honest as the read, and the read could not see a plain\n * `Collection.put()` that had *started* but not yet reached the store. Such a\n * put is invisible to the pre-flight, so:\n *\n * - it lands after the pre-flight and the transaction overwrites it from a\n * stale snapshot (both writers report success, one write vanishes); or\n * - it lands after the transaction's own write and clobbers it from a stale\n * base (two writes, one version bump).\n *\n * Neither raised an error on either side, and the loss was **store-capability\n * dependent** — `to-memory` declares `txAtomic` and takes the atomic commit\n * path, `to-browser-idb` does not and takes the per-op replay — so an app\n * tested on a memory store could ship lossy on IndexedDB.\n *\n * ## The remedy: make the pre-flight's read honest\n *\n * Every logical `Collection.put()` / `.delete()` registers itself here for the\n * span of its read-modify-write, keyed by `(vault, collection, id)`. A commit\n * **drains** the writes already in flight on the keys it is about to touch,\n * BEFORE its pre-flight reads. The in-flight case then behaves exactly like\n * the fully-awaited one the pre-flight already handled correctly: the read\n * sees the new version and a mismatched `expectedVersion` refuses with\n * `ConflictError`. No silent loss, and no new refusal for writes that were\n * never in conflict.\n *\n * ## Why this is deadlock-free\n *\n * The wait is strictly one-directional and one-deep. A registered write never\n * waits on anything from this module — only a commit does, and only on writes\n * registered *before* it started draining ({@link drainKeyedWrites} takes a\n * snapshot and does not loop to a fixed point). So there is no cycle to close,\n * and the drain terminates as soon as the pre-existing writes settle,\n * whichever way they settle. A commit's own Phase-2 writes register *after*\n * the drain has returned, so a commit never waits on itself.\n *\n * ## Scope, deliberately\n *\n * Keyed on the STORE object, not the `Noydb` instance — two hub instances over\n * one store are the same race, and this keeps the always-on kernel files\n * (`noydb.ts`) untouched. Same-process only: it is a JS-object registry, and\n * makes no claim about a second tab or a second process. That is the same\n * boundary the reported defect has.\n *\n * ⛔ This does NOT serialize transaction against transaction. Two concurrent\n * commits touching one key can still interleave — a distinct case from the\n * reported one, and one whose fix (a commit barrier) has to solve reentrancy\n * for the commit's own replay writes first. Do not extend this module into\n * that without measuring the reentrancy path.\n *\n * @internal\n */\n\n/** store → key → the settle-promises of the writes currently in flight on it. */\nconst inFlight = new WeakMap<object, Map<string, Set<Promise<void>>>>()\n\n/** The shared `(vault, collection, id)` key shape. @internal */\nexport function writeGateKey(vault: string, collection: string, id: string): string {\n return `${vault}\\x00${collection}\\x00${id}`\n}\n\n/**\n * Run one logical write registered against `(vault, collection, id)` so a\n * concurrent commit can drain it. Returns / throws exactly what `fn` does —\n * registration is invisible to the caller.\n *\n * @internal\n */\nexport async function trackKeyedWrite<R>(\n store: object,\n vault: string,\n collection: string,\n id: string,\n fn: () => Promise<R>,\n): Promise<R> {\n let byKey = inFlight.get(store)\n if (byKey === undefined) {\n byKey = new Map()\n inFlight.set(store, byKey)\n }\n const key = writeGateKey(vault, collection, id)\n let waiters = byKey.get(key)\n if (waiters === undefined) {\n waiters = new Set()\n byKey.set(key, waiters)\n }\n const running = fn()\n // A drainer must never inherit this write's rejection: it is waiting for the\n // write to STOP being in flight, not for it to succeed. The original promise\n // is what the caller gets, unswallowed.\n const settled = running.then(() => undefined, () => undefined)\n waiters.add(settled)\n try {\n return await running\n } finally {\n waiters.delete(settled)\n if (waiters.size === 0) byKey.delete(key)\n }\n}\n\n/**\n * Wait for every write already in flight on `keys` to settle. Snapshot-based\n * and single-pass — see the deadlock note in the module doc. A no-op (and no\n * microtask) when nothing is in flight, which is the overwhelmingly common\n * case for a commit.\n *\n * @internal\n */\nexport async function drainKeyedWrites(store: object, keys: Iterable<string>): Promise<void> {\n const byKey = inFlight.get(store)\n if (byKey === undefined || byKey.size === 0) return\n let waits: Promise<void>[] | undefined\n for (const key of keys) {\n const waiters = byKey.get(key)\n if (waiters === undefined) continue\n for (const w of waiters) (waits ??= []).push(w)\n }\n if (waits !== undefined) await Promise.all(waits)\n}\n","/**\n * Multi-record atomic transactions.\n *\n * Lets an application stage writes across two or more collections (or\n * vaults) and commit them all-or-nothing.\n *\n * ```ts\n * await db.transaction(async (tx) => {\n * const inv = tx.vault('acme').collection<Invoice>('invoices')\n * const pay = tx.vault('acme').collection<Payment>('payments')\n * await inv.put(invoiceId, { ...invoice, status: 'paid' })\n * await pay.put(paymentId, { invoiceId, amount, paidAt })\n * })\n * // If the body throws before returning: nothing persisted.\n * // If the body returns: all puts committed; any CAS mismatch rolls\n * // the batch back and surfaces as ConflictError.\n * ```\n *\n * ## Atomicity semantics\n *\n * Ops are buffered during the body. On body-return the hub:\n *\n * 0. **Drain (#1420)** — waits out any plain `Collection.put()` /\n * `.delete()` already in flight on a key this commit touches, so\n * the pre-flight below reads a version that includes it. Without\n * this the pre-flight cannot see a write that has STARTED but not\n * yet reached the store, and one of the two writes vanishes with\n * both reporting success.\n * 1. **Pre-flight** — re-reads every touched envelope and enforces\n * any caller-supplied `expectedVersion`. A mismatch throws\n * `ConflictError` with *no* writes performed.\n * 2. **Execute** — re-validates each op's `expectedVersion` against\n * the CURRENT stored version (#1420 — the pre-flight's verdict is\n * otherwise unchecked all the way to the write, and this path\n * carries no store-level CAS the way the atomic one does), then\n * calls `Collection.put()` / `.delete()` for each staged op in\n * declaration order. History snapshots, ledger appends, and change\n * events fire as normal per op.\n * 3. **Unwind on failure** — if step 2 throws mid-batch, each\n * already-committed op is reverted via the raw store (restoring\n * the captured prior envelope, or deleting if none existed). The\n * ledger is NOT rewritten — audit history preserves the partial\n * commit and the revert.\n *\n * **Atomic delegation (#906).** Steps 1–3 above describe the OCC\n * fallback. When the store declares `StoreCapabilities.txAtomic` AND\n * implements `tx()`, AND the staged batch is statically safe\n * (`canCommitAtomically` — see `atomic-eligibility.ts`), the commit\n * instead: prepares every op through `Collection._preparePut` /\n * `_prepareDelete` (encrypt, resolve the prior version, mint the #589\n * marker — no observable side effect), submits the whole write set as\n * ONE `store.tx(ops)` call with a per-leg `expectedVersion`, then\n * finalizes each op in staged order. The visible difference: history\n * snapshots, ledger entries and change events all fire AFTER the bytes\n * are durable, instead of interleaving per op.\n *\n * **Crash window.** On the OCC fallback, steps 2–3 are not a\n * storage-layer transaction — if the process dies between two executed\n * ops, the on-disk state is partial. The atomic path above closes that\n * window for the write set itself (`to-memory` and the SQL stores in\n * `noy-db-to` implement `tx()`); a crash between the batch landing and\n * the finalize loop finishing still leaves per-op side effects (history,\n * ledger, cache) incomplete. Stores that cannot commit a batch\n * atomically (file, S3) omit `tx()` and keep the fallback.\n *\n * ## Not covered\n *\n * - Cross-sync-peer atomicity. Transactions commit against the\n * primary store only; the sync engine pushes on its normal\n * schedule. For cross-peer two-phase commit use `SyncTransaction`\n * via `db.transaction(vaultName)`.\n * - Read-your-writes within the body. `tx.collection().get(id)`\n * returns the most-recently-staged value for that id when one\n * exists; if no staged op has touched the id, it reads the current\n * committed state. Version numbers returned by `get` reflect the\n * pre-transaction state (staged puts have no version yet).\n *\n * @module\n */\n\nimport type { Noydb } from '../../kernel/noydb.js'\nimport type { Vault } from '../../kernel/vault.js'\nimport type { Collection } from '../../kernel/collection.js'\nimport type { EncryptedEnvelope, TxOp } from '../../kernel/types.js'\nimport type { PreparedPut, PreparedDelete } from '../../kernel/prepared-write.js'\nimport { canCommitAtomically } from './atomic-eligibility.js'\nimport {\n AmendmentForbiddenError,\n ConflictError,\n InvariantError,\n ValidationError,\n} from '../../kernel/errors.js'\nimport { generateULID } from '../../with-pod/ulid.js'\nimport type { GuardExecutor as GuardExecutorModule } from '../../with-audit/guards/executor.js'\nimport type { LedgerEntry } from '../history/ledger/entry.js'\nimport type { TransactionInvariant } from './invariants.js'\nimport type { GuardChange, GuardContext, ReadOnlyVaultFacade } from '../../with-audit/guards/types.js'\nimport { bestEffortRevert } from '../../kernel/best-effort-revert.js'\nimport { drainKeyedWrites } from '../../kernel/tx-write-gate.js'\n\n/** One op buffered inside a running `TxContext`. @internal */\nexport interface StagedOp {\n type: 'put' | 'delete'\n vaultName: string\n collectionName: string\n id: string\n record?: unknown\n expectedVersion?: number\n /**\n * Optional human-readable tag forwarded to the resulting ledger\n * entry's `reason` field. Set by callers via\n * `tx.vault(v).collection(c).put(id, record, { reason })`.\n */\n reason?: string\n}\n\n/**\n * One executed op (main staged op or recursive side-effect like a\n * derivation output) paired with the envelope captured before the write.\n * `revertExecuted` walks this array in reverse on rollback.\n * @internal\n */\nexport interface ExecutedOp {\n op: StagedOp\n priorEnvelope: EncryptedEnvelope | null\n}\n\n/**\n * Options accepted by `db.transaction({ amendment, reason }, fn)`.\n * Only the amendment variant uses these — a plain `db.transaction(fn)`\n * never sees this shape.\n */\nexport interface AmendmentTxOptions {\n /** Opt into amendment mode. Required to be `true`. */\n readonly amendment: true\n /** Human-readable rationale recorded in the ledger entry. Required. */\n readonly reason: string\n}\n\n/**\n * Transaction handle passed to the user's body. Use\n * `tx.vault(name).collection<T>(name)` to get a per-collection\n * facade; its `put`/`delete`/`get` calls stage ops against the tx.\n */\nexport class TxContext {\n /** Stable id for this transaction; shared by all writes it performs. */\n readonly txId: string = generateULID()\n /** @internal */\n readonly _ops: StagedOp[] = []\n /**\n * @internal — write log built up in Phase 2. Each entry records the\n * envelope captured BEFORE the write so a mid-batch failure can\n * restore prior state via `revertExecuted`. Side-effect writes (e.g.\n * recursive derivation outputs fired inside `Collection.put`) are\n * appended here in execution order so they roll back alongside the\n * main staged ops.\n */\n readonly _executed: ExecutedOp[] = []\n /** @internal */\n readonly _db: Noydb\n /**\n * @internal — true when this TxContext was opened in amendment\n * mode. Toggles the lazy-`beginAmendment` + role-check path on first\n * `tx.vault(name)` and unlocks the post-Phase-2 invariant + audit run.\n */\n readonly _amendment: boolean\n /** @internal — vaults that have already had `beginAmendment` called. */\n readonly _amendmentVaults = new Map<string, Vault>()\n\n /** @internal */\n constructor(db: Noydb, amendment = false) {\n this._db = db\n this._amendment = amendment\n }\n\n /** Scope subsequent `collection()` calls to the named vault. */\n vault(name: string): TxVault {\n const v = this._db.vault(name)\n if (this._amendment && !this._amendmentVaults.has(name)) {\n // Role check is per-vault. The task spec (\"only admin or owner\n // can open an amendment\") is implemented lazy-on-first-touch\n // because the role lives on the vault's keyring, and `tx.vault()`\n // is the first place we know which vault we're addressing. The\n // observable effect is identical to an eager check in the single-\n // vault case the tests exercise; multi-vault amendments check\n // each touched vault as they first appear.\n const role = v.role\n // FR-6: custodian is admin-rank for operational mutations, and an\n // amendment is an operational (data-correcting) act — not an ownership\n // meta-capability — so custodian is allowed alongside owner/admin.\n if (role !== 'admin' && role !== 'owner' && role !== 'custodian') {\n throw new AmendmentForbiddenError(v.userId, role)\n }\n // Amendments require an initialised guard registry — they\n // produce a structured invariant + change-set audit. A vault\n // opened without `guardStrategies` (or via the sync fallback\n // path) has a null registry and cannot run an amendment.\n const reg = v._getGuardRegistry()\n if (reg === null) {\n throw new ValidationError(\n `Vault \"${name}\": amendment mode requires at least one ` +\n `guardStrategy registered via createNoydb({ guardStrategies }). ` +\n `Open the vault with guardStrategies before calling ` +\n `db.transaction({ amendment: true }).`,\n )\n }\n reg.beginAmendment()\n this._amendmentVaults.set(name, v)\n }\n return new TxVault(this, v)\n }\n}\n\n/** Per-vault facade inside a running transaction. */\nexport class TxVault {\n /** @internal */\n readonly _ctx: TxContext\n /** @internal */\n readonly _vault: Vault\n\n /** @internal */\n constructor(ctx: TxContext, vault: Vault) {\n this._ctx = ctx\n this._vault = vault\n }\n\n /** Scope subsequent op calls to the named collection. */\n collection<T>(name: string): TxCollection<T> {\n const c = this._vault.collection<T>(name)\n return new TxCollection<T>(this._ctx, this._vault, c, name)\n }\n}\n\n/** Per-collection facade inside a running transaction. */\nexport class TxCollection<T> {\n /** @internal */\n readonly _ctx: TxContext\n /** @internal */\n readonly _vault: Vault\n /** @internal */\n readonly _coll: Collection<T>\n /** @internal */\n readonly _name: string\n\n /** @internal */\n constructor(ctx: TxContext, vault: Vault, coll: Collection<T>, name: string) {\n this._ctx = ctx\n this._vault = vault\n this._coll = coll\n this._name = name\n }\n\n /**\n * Read the current committed value, or the most-recently-staged\n * value from the same transaction if one exists.\n */\n async get(id: string): Promise<T | null> {\n for (let i = this._ctx._ops.length - 1; i >= 0; i--) {\n const op = this._ctx._ops[i]!\n if (\n op.vaultName === this._vault.name &&\n op.collectionName === this._name &&\n op.id === id\n ) {\n if (op.type === 'delete') return null\n return op.record as T\n }\n }\n return this._coll.get(id)\n }\n\n /**\n * Stage a put. Does not write until the transaction body returns.\n * Supply `{ expectedVersion }` to enforce optimistic concurrency\n * during the commit pre-flight.\n */\n put(id: string, record: T, options?: { expectedVersion?: number; reason?: string }): void {\n const op: StagedOp = {\n type: 'put',\n vaultName: this._vault.name,\n collectionName: this._name,\n id,\n record,\n }\n if (options?.expectedVersion !== undefined) op.expectedVersion = options.expectedVersion\n if (options?.reason !== undefined) op.reason = options.reason\n this._ctx._ops.push(op)\n }\n\n /**\n * Stage a delete. Does not write until the transaction body returns.\n * Supply `{ expectedVersion }` to enforce optimistic concurrency\n * during the commit pre-flight.\n */\n delete(id: string, options?: { expectedVersion?: number }): void {\n const op: StagedOp = {\n type: 'delete',\n vaultName: this._vault.name,\n collectionName: this._name,\n id,\n }\n if (options?.expectedVersion !== undefined) op.expectedVersion = options.expectedVersion\n this._ctx._ops.push(op)\n }\n}\n\n/**\n * Commit plan: pre-flight check + execution + revert plan.\n *\n * @internal — driven by `withTransactions()` (via `tx/active.ts`) for\n * user-facing `db.transaction(...)` calls and by the `amendment` path\n * in `noydb.ts`. `Collection.putManyAtomic` runs its own Phase 2 loop\n * but shares the `_activeTxContext` mechanism (and the `revertExecuted`\n * helper) so nested side-effect derivation writes get registered for\n * revert alongside the bulk-put source ops.\n */\nexport async function runTransaction<T>(\n db: Noydb,\n fn: (tx: TxContext) => Promise<T> | T,\n options?: AmendmentTxOptions,\n txInvariants?: ReadonlyArray<TransactionInvariant>,\n): Promise<T> {\n // ─── Amendment-mode pre-flight ───────────────────────────────\n // `reason` is the only thing we can validate before the body runs;\n // the per-vault role check happens lazily on first `tx.vault(name)`\n // because we don't know which vaults the body will touch ahead of\n // time. Throwing here keeps the failure mode close to the call site\n // so the developer doesn't have to walk an async stack to find the\n // missing-reason mistake.\n if (options?.amendment) {\n if (typeof options.reason !== 'string' || options.reason.trim().length === 0) {\n throw new ValidationError(\n 'db.transaction({ amendment: true }) requires a non-empty `reason` string.',\n )\n }\n }\n\n const ctx = new TxContext(db, options?.amendment === true)\n const bodyResult = await fn(ctx)\n\n if (ctx._ops.length === 0) {\n // Body produced no ops. If amendment mode was active we still\n // need to close any opened windows so a subsequent (unrelated)\n // write doesn't surprise-collect into a stale change-set. Each\n // `beginAmendment` is matched by exactly one `consumeChanges`.\n if (ctx._amendment) {\n for (const v of ctx._amendmentVaults.values()) {\n // Registry is guaranteed non-null here — `tx.vault(name)`\n // threw above if it was null before adding to\n // `_amendmentVaults`.\n const reg = v._getGuardRegistry()\n if (reg !== null) {\n reg.consumeChanges()\n reg.consumeMeta()\n }\n }\n }\n return bodyResult\n }\n\n // Phase 1 — pre-flight: snapshot every touched envelope and enforce\n // any caller-supplied expectedVersion. Same (vault, coll, id) touched\n // more than once in one tx snapshots only the *initial* committed\n // state; the in-order replay in Phase 2 takes care of successor ops.\n const priorEnvelopes = new Map<string, EncryptedEnvelope | null>()\n const store = db._store\n\n // #1420 — before the first pre-flight read, wait out any plain\n // `Collection.put()`/`.delete()` already in flight on a key this commit is\n // about to touch. Without it the pre-flight's re-read cannot see a write\n // that has started but not yet reached the store, so the check passes\n // against a stale snapshot and one of the two writes vanishes with both\n // reporting success. Draining makes the in-flight case behave exactly like\n // the fully-awaited one, which the pre-flight below already handled\n // correctly. Deadlock-free by construction — see `kernel/tx-write-gate.ts`.\n await drainKeyedWrites(store, ctx._ops.map(keyOf))\n\n // Commit-time changeset invariants need PLAINTEXT prior records\n // for `before`, but `priorEnvelopes` holds ENCRYPTED envelopes. So for\n // ops in a watched scope we additionally decrypt the prior record here,\n // in Phase 1, BEFORE Phase 2 overwrites it. Snapshots only the initial\n // committed state per (vault, coll, id), matching the envelope snapshot.\n const invariants = txInvariants ?? []\n const watchedScopes = new Set(invariants.map(i => i.scope))\n const plainBefore = new Map<string, unknown>()\n\n for (const op of ctx._ops) {\n const key = keyOf(op)\n if (!priorEnvelopes.has(key)) {\n const env = await store.get(op.vaultName, op.collectionName, op.id)\n priorEnvelopes.set(key, env)\n }\n if (watchedScopes.has(op.collectionName) && !plainBefore.has(key)) {\n const prior = await db\n .vault(op.vaultName)\n .collection(op.collectionName)\n .get(op.id)\n plainBefore.set(key, prior ?? null)\n }\n if (op.expectedVersion !== undefined) {\n const env = priorEnvelopes.get(key) ?? null\n const actual = env?._v ?? 0\n if (actual !== op.expectedVersion) {\n throw new ConflictError(\n actual,\n `Transaction pre-flight: ${op.vaultName}/${op.collectionName}/${op.id} ` +\n `expected v${op.expectedVersion}, found v${actual}`,\n )\n }\n }\n }\n\n // Phase 2 — execute via the Collection layer so history snapshots,\n // ledger entries, and change events fire normally. We capture each\n // successful op so a mid-batch throw can revert in Phase 3.\n //\n // `_activeTxContext` is published on the Noydb instance for the\n // duration of Phase 2 so recursive writes triggered inside\n // `Collection.put` (today: eager derivation outputs) can register\n // their own envelopes onto `ctx._executed` and roll back alongside\n // the main staged ops. The `finally` clears it before the\n // amendment commit phase runs.\n db._setActiveTxContext(ctx)\n try {\n if (canCommitAtomically(db, ctx)) {\n // #906 — the whole batch goes to the store as ONE `tx()` write set.\n // Tracked here because the atomic path bypasses `Collection.put()`,\n // which is where an ordinary write enters the queue: without this,\n // `hub.writeQueue.pending` would read false for the entire commit.\n // One tracked unit, not one per op — the batch IS one logical write.\n await db._writeQueueTracker.track(() => commitAtomicBatch(db, ctx, priorEnvelopes))\n } else {\n const writtenKeys = new Set<string>()\n try {\n for (const op of ctx._ops) {\n const coll = db.vault(op.vaultName).collection(op.collectionName)\n const key = keyOf(op)\n const prior = priorEnvelopes.get(key) ?? null\n // #1420 — the per-op path replays through `Collection.put()`, which\n // carries no CAS: whatever the pre-flight decided stands unchecked\n // until the write lands. The atomic path re-validates by handing\n // every leg an `expectedVersion` to `store.tx()`; this is that check\n // for the path that has no store-level batch. Once THIS transaction\n // has written the key its version has legitimately moved, so a\n // second op on the same key is exempt — matching Phase 1, which\n // snapshots (and checks) only the first occurrence per key.\n if (op.expectedVersion !== undefined && !writtenKeys.has(key)) {\n const current = await store.get(op.vaultName, op.collectionName, op.id)\n const actual = current?._v ?? 0\n if (actual !== op.expectedVersion) {\n throw new ConflictError(\n actual,\n `Transaction execute: ${op.vaultName}/${op.collectionName}/${op.id} ` +\n `expected v${op.expectedVersion}, found v${actual}`,\n )\n }\n }\n // Record the revert plan BEFORE the call so a mid-`coll.put` throw\n // (e.g. strict-mode derivation failure firing after `store.put`\n // has already committed the envelope) still has its source write\n // reverted. `revertExecuted` is best-effort: putting prior back is\n // idempotent when the failing op never actually wrote, and\n // `_invalidateCacheEntry` is a no-op when the collection isn't\n // hydrated.\n ctx._executed.push({ op, priorEnvelope: prior })\n if (op.type === 'put') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await coll.put(op.id, op.record as any, op.reason !== undefined ? { reason: op.reason } : undefined)\n } else {\n await coll.delete(op.id)\n }\n writtenKeys.add(key)\n }\n } catch (err) {\n // Phase 3 — best-effort revert. See helper docstring.\n await revertExecuted(ctx._executed, store, db)\n // Drain amendment windows so the next transaction starts clean.\n if (ctx._amendment) {\n for (const v of ctx._amendmentVaults.values()) {\n const reg = v._getGuardRegistry()\n if (reg !== null) {\n reg.consumeChanges()\n reg.consumeMeta()\n }\n }\n }\n throw err\n }\n }\n } finally {\n db._clearActiveTxContext(ctx)\n }\n\n // ─── Amendment commit phase (only if amendment === true) ────\n // Body succeeded — now run each touched vault's invariants over the\n // collected change-set, then append a structured ledger entry. If\n // any invariant throws, treat it exactly like a mid-Phase-2 failure:\n // revert every executed op and re-throw the InvariantError.\n if (ctx._amendment) {\n // Lazy-load GuardExecutor at the dispatch site — keeps the floor\n // bundle free of the guards service when amendments aren't used.\n // Mirrors the deferred-load pattern from elsewhere in this module.\n const { GuardExecutor } = (await import('../../with-audit/guards/executor.js')) as {\n GuardExecutor: typeof GuardExecutorModule\n }\n try {\n for (const [vaultName, v] of ctx._amendmentVaults) {\n const registry = v._getGuardRegistry()\n // Registry is guaranteed non-null at this point — the\n // `tx.vault(name)` path that populates `_amendmentVaults`\n // throws if the registry is null. The defensive check here\n // is for TypeScript's narrowing.\n if (registry === null) continue\n const changesByCollection = registry.consumeChanges()\n const meta = registry.consumeMeta()\n if (changesByCollection.size === 0) continue\n\n const readOnlyVault = v._getReadOnlyFacade()\n if (readOnlyVault === null) continue\n\n // Build the invariant ctx once per vault — it's the same shape\n // every guard sees on the normal `check` path, just with a\n // synthetic `existing: null` (invariants get the full change\n // set in their first parameter; `existing` is a per-record\n // concept that doesn't apply here).\n const invariantsPassed: string[] = []\n for (const [collection, changes] of changesByCollection) {\n const guards = registry.guardsFor(collection).filter(g => g.amendment !== undefined)\n for (const guard of guards) {\n await GuardExecutor.runInvariant(guard, changes, {\n existing: null,\n vault: readOnlyVault,\n userId: v.userId,\n role: v.role,\n })\n }\n if (guards.length > 0) invariantsPassed.push(collection)\n }\n\n // Append the audit ledger entry. Silent no-op when the\n // history strategy isn't configured — the records still\n // committed, only the multi-record summary is unavailable.\n const ledger = v._getLedgerOrNull()\n if (ledger) {\n const role = v.role as 'admin' | 'owner'\n const amendment: NonNullable<LedgerEntry['amendment']> = {\n reason: options!.reason,\n role,\n changes: meta,\n invariantsPassed,\n }\n await ledger.append({\n op: 'amendment',\n collection: '',\n id: '',\n version: 0,\n actor: v.userId,\n // No payload to hash — the per-record entries already\n // captured `payloadHash` at their own append time. We use\n // a sha256 of the canonical reason string so the field is\n // populated with something deterministic and non-empty.\n payloadHash: '',\n amendment,\n })\n }\n void vaultName\n }\n } catch (err) {\n await revertExecuted(ctx._executed, store, db)\n throw err instanceof InvariantError ? err : new InvariantError(\n err instanceof Error ? err.message : `invariant violated: ${String(err)}`,\n )\n }\n }\n\n // ─── Commit-time changeset invariant phase ───────────\n // Runs for BOTH ordinary and amendment transactions (placed after the\n // amendment phase so an amendment commit is still subject to these\n // set-level constraints). Assemble the changeset from the executed\n // staged ops, deduped to the LAST write per (vault, coll, id) while\n // preserving write order, then group `GuardChange` by collection\n // (scope) and run each matching invariant. A throw mirrors the\n // amendment-phase failure mode exactly: revert every executed op and\n // re-throw as `InvariantError`.\n if (invariants.length > 0) {\n // Dedup ctx._ops to the last write per key, preserving first-seen\n // (write) order so the changeset is stable and order-meaningful.\n const lastOp = new Map<string, StagedOp>()\n const order: string[] = []\n for (const op of ctx._ops) {\n const key = keyOf(op)\n if (!lastOp.has(key)) order.push(key)\n lastOp.set(key, op)\n }\n\n // Group {before, after} pairs by collection name (the invariant\n // scope). `before` is the plaintext prior captured in Phase 1 (null\n // for inserts / unwatched — only watched scopes were captured, and\n // every grouped key belongs to a watched scope). `after` is the\n // written record, null for a delete.\n const changesByScope = new Map<string, GuardChange<unknown>[]>()\n // Parallel map: scope → the vault name of its (last-seen) op, used to\n // build the per-invariant read-only ctx (facade + userId + role).\n const scopeVault = new Map<string, string>()\n for (const key of order) {\n const op = lastOp.get(key)!\n if (!watchedScopes.has(op.collectionName)) continue\n const before = plainBefore.get(key) ?? null\n const after = op.type === 'delete' ? null : (op.record ?? null)\n const change = { before, after } as GuardChange<unknown>\n const arr = changesByScope.get(op.collectionName)\n if (arr) arr.push(change)\n else changesByScope.set(op.collectionName, [change])\n\n // Stash the vault name alongside so we can build a per-vault ctx.\n // (All ops in a scope group could span vaults; we resolve the\n // vault per change below via a parallel map keyed the same way.)\n scopeVault.set(op.collectionName, op.vaultName)\n }\n\n try {\n for (const inv of invariants) {\n const changes = changesByScope.get(inv.scope)\n if (changes === undefined || changes.length === 0) continue\n const vaultName = scopeVault.get(inv.scope)!\n const v = db.vault(vaultName)\n // Prefer the real read-only facade so the invariant can read\n // sibling collections; fall back to a minimal read-only stub.\n const facade: ReadOnlyVaultFacade =\n v._getReadOnlyFacade() ?? {\n collection<R = unknown>(name: string) {\n const c = v.collection<R>(name)\n return {\n get: (id: string) => c.get(id),\n list: () => c.list(),\n query: () => c.query(),\n }\n },\n }\n const ctxForInv: GuardContext<unknown> = {\n existing: null,\n vault: facade,\n userId: v.userId,\n role: v.role,\n }\n await inv.check(changes, ctxForInv)\n }\n } catch (err) {\n await revertExecuted(ctx._executed, store, db)\n throw err instanceof InvariantError ? err : new InvariantError(\n err instanceof Error ? err.message : `invariant violated: ${String(err)}`,\n )\n }\n }\n\n return bodyResult\n}\n\n/**\n * One prepared leg of an atomic batch: the `store.tx()` op to submit plus the\n * carrier its finalize half needs.\n * @internal\n */\ninterface AtomicLeg {\n readonly op: StagedOp\n readonly coll: Collection<unknown>\n readonly txOp: TxOp\n readonly prepared: PreparedPut<unknown> | PreparedDelete<unknown>\n}\n\n/**\n * #906 — the atomic commit path: prepare every staged op, submit the whole\n * write set as ONE `store.tx()` call, then finalize each op in staged order.\n *\n * Only ever reached through `canCommitAtomically` (`atomic-eligibility.ts`),\n * whose exclusions are what make this safe — no CRDT, no refs, no\n * derivation/MV source, no unique constraints, no amendment, no id touched\n * twice. Do not call it without that blessing.\n *\n * @internal\n */\nasync function commitAtomicBatch(\n db: Noydb,\n ctx: TxContext,\n priorEnvelopes: ReadonlyMap<string, EncryptedEnvelope | null>,\n): Promise<void> {\n const store = db._store\n const legs: AtomicLeg[] = []\n\n // ─── Prepare ───────────────────────────────────────────────────\n // Encrypt, resolve the prior version, mint the #589 marker — with zero\n // observable side effects on the collections the gate admits. A throw here\n // therefore needs no unwind: nothing has been written yet, and the caller\n // sees the refusal exactly as it would from `Collection.put()`. The revert\n // plan is recorded all the same, because a FINALIZE failure below does need\n // it (same record-before-the-call convention as the OCC loop).\n for (const op of ctx._ops) {\n const coll = db.vault(op.vaultName).collection(op.collectionName)\n // The two refusals `Collection.put()` / `.delete()` assert before anything\n // else — schema-update gate + schema fence. They live in those wrappers,\n // not in the prepare halves this path calls, so assert them here, per op.\n await coll._assertWriteGates()\n const prior = priorEnvelopes.get(keyOf(op)) ?? null\n // Every leg carries CAS against the Phase-1 snapshot, so a writer landing\n // between the body returning and the batch reaching the store loses.\n const base = { vault: op.vaultName, collection: op.collectionName, id: op.id, expectedVersion: prior?._v ?? 0 }\n ctx._executed.push({ op, priorEnvelope: prior })\n if (op.type === 'put') {\n const prepared = await coll._preparePut(op.id, op.record, op.reason !== undefined ? { reason: op.reason } : undefined)\n legs.push({ op, coll, prepared, txOp: { type: 'put', ...base, envelope: prepared.envelope } })\n } else {\n const prepared = await coll._prepareDelete(op.id, false)\n if (prepared === null) {\n // Nothing to delete (no live record / already a marker / shredded) —\n // the case `_doDelete` answers with `false`. Drop the op and its\n // revert entry rather than sending a leg for it.\n ctx._executed.pop()\n continue\n }\n legs.push({\n op, coll, prepared,\n // Under sync the delete is a marker PUT at `live._v + 1` (#589);\n // without sync it is a physical removal.\n txOp: prepared.marker ? { type: 'put', ...base, envelope: prepared.marker } : { type: 'delete', ...base },\n })\n }\n }\n\n // Every op turned out to be a no-op delete — same as the OCC loop, which\n // would have written nothing either. Don't hand the store an empty batch.\n if (legs.length === 0) return\n\n // ─── The batch ─────────────────────────────────────────────────\n // Deliberately NOT inside a revert-guarded try: a rejection means the store\n // applied nothing (all-or-nothing is the capability we gated on), so there\n // is nothing to unwind and a revert pass would write over records this\n // transaction never touched. No ledger entry, history snapshot or change\n // event has fired either — every one of those lives in finalize below. The\n // error (a `ConflictError` when a leg lost its CAS) surfaces unwrapped.\n await store.tx!(legs.map(l => l.txOp))\n\n // ─── Finalize ──────────────────────────────────────────────────\n // History snapshot, ledger entry, cache/index update, change event — per op,\n // in staged order, all AFTER the bytes are durable (where the OCC loop\n // interleaves them per op). #931: then the after-write observers (user\n // `onAfterWrite` hooks + the `afterPut`/`afterDelete` observe bus), which\n // no longer gate eligibility — they cannot refuse a completed write.\n try {\n for (const leg of legs) {\n if (leg.op.type === 'put') await leg.coll._finalizePut(leg.prepared as PreparedPut<unknown>)\n else await leg.coll._finalizeDelete(leg.prepared as PreparedDelete<unknown>)\n await leg.coll._fireAtomicAfterWrite(leg.op.type, leg.prepared)\n }\n } catch (err) {\n // The bytes ARE durable now, so this is today's best-effort unwind over\n // the plan recorded above. No amendment window to drain — the gate\n // excludes amendment transactions.\n await revertExecuted(ctx._executed, store, db)\n throw err\n }\n}\n\n/**\n * Phase 3 helper — restore captured prior envelopes via the raw store\n * to avoid re-firing Collection-level side effects (we don't want a\n * cascade of change events undoing themselves). The ledger is left\n * as-is: each committed op appended an entry; the revert is\n * deliberately NOT recorded as a compensating entry because the\n * caller-facing contract is \"atomic or not at all,\" not \"every write\n * visible in the audit trail.\" Auditors who need the intermediate\n * state can still reconstruct it by walking the ledger through the\n * failed-tx timestamp.\n *\n * Delegates the reverse/best-effort/raw-revert shape to the shared\n * `bestEffortRevert` helper (`kernel/best-effort-revert.ts`); the cache\n * invalidation below rides along as that helper's per-leg `compensate`\n * callback, gated on `db` exactly as before.\n *\n * @internal — shared between `runTransaction` and\n * `Collection.putManyAtomic`. Both register source ops + nested\n * derivation side-effect ops onto `_executed`; this helper unwinds the\n * combined list in reverse on rollback.\n */\nexport async function revertExecuted(\n executed: ReadonlyArray<ExecutedOp>,\n store: Noydb['_store'],\n db?: Noydb,\n): Promise<void> {\n const legs = executed.map(({ op, priorEnvelope }) => ({\n vaultName: op.vaultName,\n collectionName: op.collectionName,\n id: op.id,\n prior: priorEnvelope,\n }))\n await bestEffortRevert(\n legs,\n store,\n db\n ? async (leg) => {\n // Sync the Collection-layer cache with what we just wrote at\n // the raw store. Without this, eager-mode `get` would still\n // return the rolled-back record from its in-memory map. The\n // Collection's `_invalidateCacheEntry` is a no-op when the\n // collection hasn't yet been hydrated.\n const coll = db.vault(leg.vaultName).collection(leg.collectionName)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await (coll as any)._invalidateCacheEntry(leg.id)\n }\n : undefined,\n )\n}\n\n/** @internal — shared (vault, collection, id) key shape; also used by `atomic-eligibility.ts`'s duplicate-key scan. */\nexport function keyOf(op: StagedOp): string {\n return `${op.vaultName}\\x00${op.collectionName}\\x00${op.id}`\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-cargo/active.ts","../src/with-cargo/describe-extraction.ts","../src/with-cargo/adopt-partition.ts","../src/with-cargo/decrypt-partition.ts"],"sourcesContent":["/**\n * Enable the cargo (partition extraction) capability.\n * Pass to `createNoydb({ cargoStrategy: withCargo() })` to make the source-side\n * `extractPartition(vault, ...)` free function live. The extraction crypto\n * engine is dynamically imported here, so it stays out of the floor bundle\n * until opted in. The recipient-side `adoptPartition` /\n * `decryptExtractedPartition` free functions — and `diffVault` (shared\n * import/merge infra) — are ungated and unaffected.\n */\nimport type { CargoStrategy } from './strategy.js'\n\nexport function withCargo(): CargoStrategy {\n return {\n async extractPartition(vault, opts) {\n const { extractPartitionCore } = await import('./extract-partition.js')\n return extractPartitionCore(vault, opts)\n },\n }\n}\n","/**\n * Partition-extraction dry-run. Read-only preview of what an\n * `extractPartition` would move: record counts, byte totals, and the\n * timestamp span per collection — computed from raw encrypted\n * envelopes WITHOUT decrypting them. Writes nothing, mutates nothing.\n *\n * @module\n */\nimport type { Vault } from '../kernel/vault.js'\nimport { walkClosure, type WalkClosureOptions, type DanglingRefNotice } from './walk-closure.js'\n\nexport interface ExtractionPreview {\n readonly totalRecords: number\n /** Sum of serialized encrypted-envelope sizes (bytes). */\n readonly totalBytes: number\n readonly byCollection: ReadonlyArray<{\n readonly name: string\n readonly recordCount: number\n readonly bytes: number\n /** Earliest envelope `_ts` in this collection (lexicographic). */\n readonly oldestTs?: string\n readonly newestTs?: string\n }>\n readonly graph: { readonly depth: number; readonly cyclesDetected: boolean }\n /** Records the walk reached but whose envelope couldn't be read. */\n readonly inaccessible: ReadonlyArray<{ readonly collection: string; readonly id: string }>\n /**\n * #772: outbound FK edges whose referenced parent was excluded from the\n * closure (missing, or tier-elevated and therefore invisible) — mirrors\n * `ExtractPartitionResult.danglingRefs` (#759) so a preview/extract pair\n * gives the SAME signal. A parent excluded this way never appears in\n * `byCollection` at all; this is the only preview-time indicator.\n */\n readonly danglingRefs: ReadonlyArray<DanglingRefNotice>\n}\n\nexport async function describeExtraction(\n vault: Vault,\n opts: WalkClosureOptions,\n): Promise<ExtractionPreview> {\n const { closure, graph, danglingRefs } = await walkClosure(vault, opts)\n\n const { name: vaultName, adapter } = vault._introspectState()\n const encoder = new TextEncoder()\n\n const byCollection: Array<{\n name: string; recordCount: number; bytes: number; oldestTs?: string; newestTs?: string\n }> = []\n const inaccessible: Array<{ collection: string; id: string }> = []\n let totalBytes = 0\n let totalRecords = 0\n\n for (const [collectionName, ids] of closure) {\n let bytes = 0\n let oldestTs: string | undefined\n let newestTs: string | undefined\n let recordCount = 0\n\n for (const id of ids) {\n const env = await adapter.get(vaultName, collectionName, id)\n if (!env) {\n // Walk reached it (via decrypted list) but the raw store read\n // returned nothing — surface rather than miscount.\n inaccessible.push({ collection: collectionName, id })\n continue\n }\n recordCount++\n bytes += encoder.encode(JSON.stringify(env)).length\n const ts = env._ts\n if (oldestTs === undefined || ts < oldestTs) oldestTs = ts\n if (newestTs === undefined || ts > newestTs) newestTs = ts\n }\n\n byCollection.push({\n name: collectionName,\n recordCount,\n bytes,\n // Spread conditionally — exactOptionalPropertyTypes forbids an\n // explicit `undefined` on an optional property.\n ...(oldestTs !== undefined ? { oldestTs } : {}),\n ...(newestTs !== undefined ? { newestTs } : {}),\n })\n totalBytes += bytes\n totalRecords += recordCount\n }\n\n byCollection.sort((a, b) => a.name.localeCompare(b.name))\n\n return Object.freeze({\n totalRecords,\n totalBytes,\n byCollection,\n graph,\n inaccessible,\n danglingRefs,\n })\n}\n","/**\n * Partition adoption. Recipient side: verify an extracted bundle,\n * validate the transfer key, import the re-keyed collections into a\n * destination store, and record an `_meta/adoption` marker. The bundle\n * stays UNOWNED after adoption — `createOwnerOnAdoptedPartition`\n * mints the owner; the transfer seal is then destroyed.\n *\n * @module\n */\nimport { base64ToBuffer, wrapKey, type EnclaveKey } from '../kernel/enclave/index.js'\nimport { TransferSealError, AdoptionStateError, ValidationError } from '../kernel/errors.js'\nimport type { NoydbStore, VaultSnapshot, KeyringFile } from '../kernel/types.js'\nimport { createOwnerKeyring, requireRosterKey } from '../with-party/team/keyring.js'\nimport { mintRosterTag } from '../with-party/team/roster-tag.js' // #1115\nimport { nextRosterEpoch } from '../with-party/team/roster-epoch.js' // #1097\nimport { resolveManagedSecret } from '../with-party/team/managed-secret.js'\nimport type { NoydbSealer } from '../with-party/team/managed-secret.js'\nimport type { NoydbShamir } from '../with-party/team/noydb-shamir.js'\nimport type { RecoveryEnrollmentInput } from '../with-party/team/rotate-recover.js'\nimport { LedgerStore } from '../with-commit/history/ledger/store.js'\nimport { LEDGER_COLLECTION } from '../with-commit/history/ledger/constants.js'\nimport type { TransferSealPayload } from '../with-pod/pod.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/pod.js'\nimport { buildRecordEnvelope } from '../kernel/enclave/index.js'\n\n/**\n * Reverse of `sealDeks`. Imports the transfer key, decrypts the\n * sealed `{ collection: base64(rawDEK) }` map (layout iv(12)‖ct‖tag), and\n * re-imports each DEK as an AES-GCM key. Throws `TransferSealError` on a\n * wrong key (AES-GCM auth-tag failure) or malformed payload.\n */\nexport async function unsealDeks(\n seal: TransferSealPayload,\n transferKey: Uint8Array,\n): Promise<Map<string, EnclaveKey>> {\n if (transferKey.byteLength !== 32) {\n throw new TransferSealError(\n `transfer key must be 32 bytes, got ${transferKey.byteLength}.`,\n )\n }\n const key = await crypto.subtle.importKey('raw', transferKey as BufferSource, 'AES-GCM', false, ['decrypt'])\n const raw = base64ToBuffer(seal.payload)\n let plaintext: ArrayBuffer\n try {\n plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: raw.slice(0, 12) as BufferSource },\n key,\n raw.slice(12) as BufferSource,\n )\n } catch {\n throw new TransferSealError(\n 'transfer seal could not be opened — wrong transfer key (AES-GCM authentication failed).',\n )\n }\n let dekMap: Record<string, string>\n try {\n dekMap = JSON.parse(new TextDecoder().decode(plaintext)) as Record<string, string>\n } catch {\n throw new TransferSealError('transfer seal payload is not valid JSON after decryption.')\n }\n const deks = new Map<string, EnclaveKey>()\n for (const [collection, b64] of Object.entries(dekMap)) {\n // Extractable: the recipient must be able to re-wrap these under their\n // own KEK (AES-KW) at owner-creation. Matches generateDEK.\n const dek = await crypto.subtle.importKey('raw', base64ToBuffer(b64) as BufferSource, 'AES-GCM', true, ['encrypt', 'decrypt'])\n deks.set(collection, dek)\n }\n return deks\n}\n\nexport interface AdoptPartitionOptions {\n readonly transferKey: Uint8Array\n readonly destinationStore: NoydbStore\n readonly vaultName: string\n}\n\nexport interface AdoptPartitionResult {\n readonly vaultName: string\n readonly needsOwner: true\n readonly sealId: string\n}\n\nexport async function adoptPartition(\n bundleBytes: Uint8Array,\n opts: AdoptPartitionOptions,\n): Promise<AdoptPartitionResult> {\n const { transferKey, destinationStore, vaultName } = opts\n\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new ValidationError(\n 'adoptPartition requires an extracted-partition bundle with a transfer seal. '\n + 'For ordinary backups use readPod + vault.load.',\n )\n }\n\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n\n // Validate the transfer key by unsealing in memory; throws\n // TransferSealError on mismatch. DEKs are discarded here — they stay\n // sealed at rest (in _meta/adoption) until owner-creation wraps them under the\n // recipient's KEK.\n await unsealDeks(seal, transferKey)\n\n // Single-occupancy per vaultName: an `_meta/adoption` marker already present\n // means this slot holds a partition (adopted-and-unowned, or already owned).\n // saveAll below would overwrite its data and replace the marker, stranding the\n // prior adoption's transfer seal. Refuse regardless of sealId — re-adopting the\n // SAME bundle is a redundant call, and adopting a DIFFERENT bundle here would\n // clobber the existing partition. Either way, pick a fresh vaultName.\n const existing = await destinationStore.get(vaultName, '_meta', 'adoption')\n if (existing) {\n const prior = JSON.parse(existing._data) as { sealId?: string }\n if (prior.sealId === seal.sealId) {\n throw new AdoptionStateError(\n `partition (sealId ${seal.sealId}) is already adopted into vault \"${vaultName}\".`,\n )\n }\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds an adopted partition (sealId ${prior.sealId}); `\n + `adopting a different partition (sealId ${seal.sealId}) here would overwrite it. `\n + `Adopt into a fresh vaultName instead.`,\n )\n }\n\n // The marker-only check above misses a worse case: a vaultName already in use\n // by an ORDINARY vault (createNoydb + openVault) carries no `_meta/adoption`,\n // yet `saveAll` below is destructive on SQL adapters (`DELETE FROM ... WHERE\n // vault = ?` followed by upsert) and would wipe the legitimate keyring +\n // data. Refuse adoption into ANY occupied slot — a fresh vaultName is the\n // documented precondition.\n const existingKeyring = await destinationStore.list(vaultName, '_keyring')\n if (existingKeyring.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds a keyring (an unrelated owner exists at this slot); `\n + `adoptPartition requires a fresh vaultName to avoid destructive saveAll on SQL adapters.`,\n )\n }\n\n const backup = JSON.parse(dump) as { collections: VaultSnapshot; _internal?: VaultSnapshot }\n await destinationStore.saveAll(vaultName, backup.collections)\n\n // Import carried internal collections (e.g. _schemas from carrySchemas).\n // saveAll only writes data collections; _internal is written per-record.\n if (backup._internal) {\n for (const [collection, records] of Object.entries(backup._internal)) {\n for (const [id, envelope] of Object.entries(records)) {\n await destinationStore.put(vaultName, collection, id, envelope)\n }\n }\n }\n\n const adoptedAt = new Date().toISOString()\n const adoption = { sealId: seal.sealId, adoptedAt, needsOwner: true as const, transferSeal: seal }\n await destinationStore.put(vaultName, '_meta', 'adoption',\n buildRecordEnvelope({ collection: '_meta', id: 'adoption', version: 1 },\n { ts: adoptedAt, iv: '', data: JSON.stringify(adoption) }))\n\n return { vaultName, needsOwner: true, sealId: seal.sealId }\n}\n\nexport interface CreateOwnerResult {\n readonly vaultName: string\n readonly userId: string\n}\n\n/** Standard-mode owner: recipient supplies the secret. */\nexport interface CreateOwnerStandardOptions {\n readonly userId: string\n readonly secret: string\n readonly transferKey: Uint8Array\n}\n\n/**\n * Managed-mode owner: the secret is minted + sealed under\n * a `NoydbSealer` (e.g. an `at-*` OS keychain) so the partition\n * auto-unlocks on the recipient's device. Managed mode mandates a strong\n * (Shamir) recovery profile at creation, which needs the\n * `shamirRecovery` provider injected.\n */\nexport interface CreateOwnerManagedOptions {\n readonly userId: string\n readonly secretMode: 'managed'\n readonly sealingKey: NoydbSealer\n readonly recovery: ReadonlyArray<RecoveryEnrollmentInput>\n readonly shamirRecovery: NoydbShamir\n readonly transferKey: Uint8Array\n}\n\nexport type CreateOwnerOptions = CreateOwnerStandardOptions | CreateOwnerManagedOptions\n\nfunction isManaged(o: CreateOwnerOptions): o is CreateOwnerManagedOptions {\n return 'secretMode' in o && o.secretMode === 'managed'\n}\n\n/**\n * Mint the first owner keyring on an adopted-but-unowned partition,\n * then destroy the transfer seal.\n *\n * Standard mode: the recipient supplies a secret. Managed mode: the\n * secret is minted + sealed under a `NoydbSealer` and a strong\n * (Shamir) recovery profile is enrolled — orchestrated via the existing\n * `openVaultAndEnrollRecovery` ceremony.\n *\n * Either way, reuses `createOwnerKeyring` to derive the KEK + write the base\n * keyring, then wraps the partition's DEKs (recovered from the seal) under that\n * KEK and re-persists the merged keyring file.\n *\n * Idempotent under retry: the seal is destroyed LAST (Stage D), after the\n * keyring (Stage A), the ledger transition (Stage B), and — in managed mode —\n * strong-recovery enrollment (Stage C). A failure in the fallible enrollment\n * step leaves the seal intact, and re-running with the same `userId` +\n * `transferKey` resumes from the first incomplete stage. (Multi-profile recovery\n * arrays may re-enroll an already-enrolled profile on retry; managed mode's\n * mandated single Shamir profile does not.)\n */\nexport async function createOwnerOnAdoptedPartition(\n store: NoydbStore,\n vaultName: string,\n opts: CreateOwnerOptions,\n): Promise<CreateOwnerResult> {\n const { userId, transferKey } = opts\n\n // Managed mode requires a strong (Shamir) recovery profile, validated BEFORE\n // any disk write — same gate as createNoydb.\n if (isManaged(opts) && !opts.recovery.some((r) => r.profile === 'shamir')) {\n throw new AdoptionStateError(\n 'managed-mode adoption requires at least one strong (shamir) recovery profile in '\n + '`recovery` — paper alone is not strong when there is no user secret to fall back on.',\n )\n }\n\n // 1. Verify adopted-unowned state.\n const adoptionEnv = await store.get(vaultName, '_meta', 'adoption')\n if (!adoptionEnv) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" is not an adopted partition (no _meta/adoption). `\n + `createOwnerOnAdoptedPartition only applies to vaults created via adoptPartition.`,\n )\n }\n const adoption = JSON.parse(adoptionEnv._data) as {\n sealId: string; adoptedAt: string; needsOwner?: boolean\n consumedAt?: string; transferSeal?: TransferSealPayload\n }\n if (adoption.consumedAt !== undefined || adoption.transferSeal === undefined) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has an owner (transfer seal consumed at ${adoption.consumedAt}).`,\n )\n }\n\n // 2. Recover the partition DEKs from the seal (throws on wrong key) BEFORE\n // writing any keyring, so a bad transfer key leaves no trace. Always\n // validated, including when resuming a partial prior call.\n const partitionDeks = await unsealDeks(adoption.transferSeal, transferKey)\n\n // The ceremony below is split into stages so a failure in the fallible\n // managed-enrollment step (network/provider outage) leaves the call RETRYABLE\n // — the seal is destroyed only once everything durable is in place. Each stage\n // detects its own prior completion rather than relying on a single resume bit.\n\n // A keyring present for a DIFFERENT user (with the seal still unconsumed) is a\n // genuine second-owner attempt — refuse it. A same-user keyring is a resumed\n // partial call and is handled by the stage checks below.\n const existingKeyring = await store.get(vaultName, '_keyring', userId)\n const otherOwners = (await store.list(vaultName, '_keyring')).filter((u) => u !== userId)\n if (otherOwners.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has a keyring for a different owner; cannot create owner \"${userId}\".`,\n )\n }\n\n // Stage A — mint the owner keyring + merge the partition DEKs. Considered done\n // only when the keyring already holds every partition DEK. createOwnerKeyring\n // overwrites (fresh KEK + fresh _users DEK), so re-running is safe ONLY while\n // no recovery has been enrolled yet — guaranteed here because enrollment\n // (Stage C) runs strictly after Stage A completes.\n const partitionCollections = [...partitionDeks.keys()]\n const priorDeks = existingKeyring ? (JSON.parse(existingKeyring._data) as KeyringFile).deks : {}\n const ownerMinted = existingKeyring !== null && partitionCollections.every((c) => c in priorDeks)\n if (!ownerMinted) {\n // Resolve the owner secret. Managed mode mints a random secret, seals\n // it under the provider, and persists _meta/sealed-secret (so the\n // partition auto-unlocks on the recipient's device); standard mode uses the\n // caller's secret. Idempotent under retry — resolveManagedSecret's reopen\n // arm reuses an already-sealed secret.\n const secret = isManaged(opts)\n ? await resolveManagedSecret(store, vaultName, opts.sealingKey)\n : opts.secret\n\n // Mint the owner keyring (KEK + _users DEK + canary, written to disk).\n const unlocked = await createOwnerKeyring(store, vaultName, { userId: userId, secret: secret })\n\n // Merge the partition DEKs (wrapped under the new KEK) into the keyring.\n const env = await store.get(vaultName, '_keyring', userId)\n if (!env) throw new AdoptionStateError(`keyring write for \"${userId}\" did not persist`)\n const keyringFile = JSON.parse(env._data) as KeyringFile\n const kek = unlocked.kek\n if (!kek) throw new AdoptionStateError(`owner keyring for \"${userId}\" has no KEK to wrap partition DEKs under`)\n const mergedDeks: Record<string, string> = { ...keyringFile.deks }\n for (const [collection, dek] of partitionDeks) {\n mergedDeks[collection] = await wrapKey(dek, kek)\n }\n // #1115 — RESTAMP. The roster tag now covers the DEK key SET, and this merge\n // is precisely a change to that set: adoption joins the partition's\n // collections to a keyring `createOwnerKeyring` stamped moments earlier\n // without them. Writing the merged file under the old tag would leave every\n // adopted vault failing `roster-tag-mismatch` on its first open.\n //\n // The roster key is the one just minted for this NEW vault — unlike\n // `liberateVault`, which joins an EXISTING vault and must keep the\n // incumbent key so co-members can still verify each other.\n const merged: KeyringFile = { ...keyringFile, deks: mergedDeks }\n // #1097 — stamped BEFORE the tag is minted, so it lands inside the\n // authenticated canonical and a store can neither edit nor strip it.\n const withEpoch = { ...merged, roster_epoch: nextRosterEpoch(keyringFile.roster_epoch) }\n const rosterKey = requireRosterKey(unlocked, 'adoptPartition')\n const mergedFile: KeyringFile = { ...withEpoch, roster_tag: await mintRosterTag(withEpoch, rosterKey) }\n await store.put(vaultName, '_keyring', userId, { ...env, _data: JSON.stringify(mergedFile) })\n }\n\n // Stage B — record the ownership transition on the carried\n // audit chain (carryLedger sealed the _ledger DEK). No-op without that DEK.\n // Idempotent: appended only if the closing `transfer-seal-consumed` entry is\n // absent, so a retry does not duplicate the pair.\n const ledgerDek = partitionDeks.get(LEDGER_COLLECTION)\n if (ledgerDek) {\n const ledger = new LedgerStore({\n adapter: store,\n vault: vaultName,\n encrypted: true,\n getDEK: async () => ledgerDek,\n actor: userId,\n })\n const creationReason = `creation-of-new-owner:${userId}`\n const consumedReason = `transfer-seal-consumed:${adoption.sealId}`\n // Gate each append on its own presence — a crash or store error strictly\n // between the two adjacent puts would otherwise re-append the first one\n // on retry. The pair is the audit record, not a single transaction.\n const recordedReasons = new Set((await ledger.loadAllEntries()).map((e) => e.reason))\n if (!recordedReasons.has(creationReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: creationReason })\n }\n if (!recordedReasons.has(consumedReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: consumedReason })\n }\n }\n\n // Stage C — Managed mode: enroll the mandatory strong recovery\n // by orchestrating the existing public ceremony. The partition is\n // now a managed-mode vault on disk (sealed secret + keyring), so we\n // open it as a normal client and let openVaultAndEnrollRecovery do the\n // gate-bypass + enroll + re-assert. Dynamic import keeps the Noydb class\n // out of the @noy-db/hub/cargo static graph. Runs BEFORE seal destruction\n // so a failure here leaves the seal intact and the call retryable.\n if (isManaged(opts)) {\n const { createNoydb } = await import('../kernel/noydb.js')\n const db = await createNoydb({\n store,\n user: userId,\n secretMode: 'managed',\n sealingKey: opts.sealingKey,\n shamirRecovery: opts.shamirRecovery,\n })\n await db.team.openVaultAndEnrollRecovery(vaultName, { recovery: opts.recovery })\n }\n\n // Stage D — Destroy the transfer seal LAST — the commit point. Everything\n // above is either idempotent or resumable, so the seal is only consumed\n // once the owner keyring (and, in managed mode, strong recovery) is\n // durably in place. Retain sealId + consumedAt for audit.\n const consumed = { sealId: adoption.sealId, adoptedAt: adoption.adoptedAt, consumedAt: new Date().toISOString() }\n await store.put(vaultName, '_meta', 'adoption', { ...adoptionEnv, _data: JSON.stringify(consumed) })\n\n return { vaultName, userId }\n}\n","/**\n * Read-side counterpart to `extractPartition`: decrypt an\n * extracted-partition bundle's records to plaintext using its transfer\n * key, WITHOUT adopting it into a vault. Used by an outward\n * orchestration layer's reconcile/merge and field-authority flows to\n * compare incoming records against a receiver. The transfer key validates the bundle\n * (wrong key throws).\n * @module\n */\nimport type { EncryptedEnvelope } from '../kernel/types.js'\nimport { openEnvelopeJson } from '../kernel/enclave/index.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/pod.js'\nimport { unsealDeks } from './adopt-partition.js'\n\n/** One decrypted record from an extracted-partition compartment. */\nexport interface DecryptedRecord {\n readonly id: string\n readonly record: Record<string, unknown>\n /** Source envelope write timestamp (ISO) — for last-write-wins merges. */\n readonly ts: string\n /** Source envelope version. */\n readonly version: number\n /** Provenance source id (FR-5). Present only when the source collection had provenance:true and a source was supplied on put. */\n readonly source?: string\n /** ISO-8601 timestamp the provenance source was recorded (FR-5). */\n readonly sourceTs?: string\n}\n\n/**\n * Decrypt every record of an extracted-partition bundle to plaintext,\n * grouped by collection. Throws if the bundle isn't an\n * extracted-partition or the transfer key is wrong.\n */\nexport async function decryptExtractedPartition(\n bundleBytes: Uint8Array,\n transferKey: Uint8Array,\n): Promise<Record<string, DecryptedRecord[]>> {\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new Error('decryptExtractedPartition: bundle is not an extracted-partition.')\n }\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n const deks = await unsealDeks(seal, transferKey) // throws TransferSealError on wrong key\n const backup = JSON.parse(dump) as { collections: Record<string, Record<string, EncryptedEnvelope>> }\n const out: Record<string, DecryptedRecord[]> = {}\n for (const [collection, byId] of Object.entries(backup.collections)) {\n const dek = deks.get(collection)\n if (dek === undefined) continue // no DEK sealed for this collection — skip\n const recs: DecryptedRecord[] = []\n for (const [id, env] of Object.entries(byId)) {\n const plaintext = await openEnvelopeJson({ collection, id }, env, dek)\n const body = JSON.parse(plaintext) as Record<string, unknown>\n recs.push({\n id,\n record: { ...body, id },\n ts: env._ts,\n version: env._v,\n ...(env._source !== undefined ? { source: env._source } : {}),\n ...(env._sourceTs !== undefined ? { sourceTs: env._sourceTs } : {}),\n })\n }\n out[collection] = recs\n }\n return out\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWO,SAAS,YAA2B;AACzC,SAAO;AAAA,IACL,MAAM,iBAAiB,OAAO,MAAM;AAClC,YAAM,EAAE,qBAAqB,IAAI,MAAM,OAAO,iCAAwB;AACtE,aAAO,qBAAqB,OAAO,IAAI;AAAA,IACzC;AAAA,EACF;AACF;;;ACkBA,eAAsB,mBACpB,OACA,MAC4B;AAC5B,QAAM,EAAE,SAAS,OAAO,aAAa,IAAI,MAAM,YAAY,OAAO,IAAI;AAEtE,QAAM,EAAE,MAAM,WAAW,QAAQ,IAAI,MAAM,iBAAiB;AAC5D,QAAM,UAAU,IAAI,YAAY;AAEhC,QAAM,eAED,CAAC;AACN,QAAM,eAA0D,CAAC;AACjE,MAAI,aAAa;AACjB,MAAI,eAAe;AAEnB,aAAW,CAAC,gBAAgB,GAAG,KAAK,SAAS;AAC3C,QAAI,QAAQ;AACZ,QAAI;AACJ,QAAI;AACJ,QAAI,cAAc;AAElB,eAAW,MAAM,KAAK;AACpB,YAAM,MAAM,MAAM,QAAQ,IAAI,WAAW,gBAAgB,EAAE;AAC3D,UAAI,CAAC,KAAK;AAGR,qBAAa,KAAK,EAAE,YAAY,gBAAgB,GAAG,CAAC;AACpD;AAAA,MACF;AACA;AACA,eAAS,QAAQ,OAAO,KAAK,UAAU,GAAG,CAAC,EAAE;AAC7C,YAAM,KAAK,IAAI;AACf,UAAI,aAAa,UAAa,KAAK,SAAU,YAAW;AACxD,UAAI,aAAa,UAAa,KAAK,SAAU,YAAW;AAAA,IAC1D;AAEA,iBAAa,KAAK;AAAA,MAChB,MAAM;AAAA,MACN;AAAA,MACA;AAAA;AAAA;AAAA,MAGA,GAAI,aAAa,SAAY,EAAE,SAAS,IAAI,CAAC;AAAA,MAC7C,GAAI,aAAa,SAAY,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/C,CAAC;AACD,kBAAc;AACd,oBAAgB;AAAA,EAClB;AAEA,eAAa,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAExD,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;;;ACjEA,eAAsB,WACpB,MACA,aACkC;AAClC,MAAI,YAAY,eAAe,IAAI;AACjC,UAAM,IAAI;AAAA,MACR,sCAAsC,YAAY,UAAU;AAAA,IAC9D;AAAA,EACF;AACA,QAAM,MAAM,MAAM,OAAO,OAAO,UAAU,OAAO,aAA6B,WAAW,OAAO,CAAC,SAAS,CAAC;AAC3G,QAAM,MAAM,eAAe,KAAK,OAAO;AACvC,MAAI;AACJ,MAAI;AACF,gBAAY,MAAM,OAAO,OAAO;AAAA,MAC9B,EAAE,MAAM,WAAW,IAAI,IAAI,MAAM,GAAG,EAAE,EAAkB;AAAA,MACxD;AAAA,MACA,IAAI,MAAM,EAAE;AAAA,IACd;AAAA,EACF,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,SAAS,CAAC;AAAA,EACzD,QAAQ;AACN,UAAM,IAAI,kBAAkB,2DAA2D;AAAA,EACzF;AACA,QAAM,OAAO,oBAAI,IAAwB;AACzC,aAAW,CAAC,YAAY,GAAG,KAAK,OAAO,QAAQ,MAAM,GAAG;AAGtD,UAAM,MAAM,MAAM,OAAO,OAAO,UAAU,OAAO,eAAe,GAAG,GAAmB,WAAW,MAAM,CAAC,WAAW,SAAS,CAAC;AAC7H,SAAK,IAAI,YAAY,GAAG;AAAA,EAC1B;AACA,SAAO;AACT;AAcA,eAAsB,eACpB,aACA,MAC+B;AAC/B,QAAM,EAAE,aAAa,kBAAkB,UAAU,IAAI;AAErD,QAAM,SAAS,cAAc,WAAW;AACxC,MAAI,OAAO,eAAe,yBAAyB,OAAO,iBAAiB,QAAW;AACpF,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,QAAM,EAAE,SAAS,IAAI,MAAM,QAAQ,WAAW;AAC9C,QAAM,EAAE,MAAM,KAAK,IAAI,4BAA4B,QAAQ;AAM3D,QAAM,WAAW,MAAM,WAAW;AAQlC,QAAM,WAAW,MAAM,iBAAiB,IAAI,WAAW,SAAS,UAAU;AAC1E,MAAI,UAAU;AACZ,UAAM,QAAQ,KAAK,MAAM,SAAS,KAAK;AACvC,QAAI,MAAM,WAAW,KAAK,QAAQ;AAChC,YAAM,IAAI;AAAA,QACR,qBAAqB,KAAK,MAAM,oCAAoC,SAAS;AAAA,MAC/E;AAAA,IACF;AACA,UAAM,IAAI;AAAA,MACR,UAAU,SAAS,gDAAgD,MAAM,MAAM,6CACnC,KAAK,MAAM;AAAA,IAEzD;AAAA,EACF;AAQA,QAAM,kBAAkB,MAAM,iBAAiB,KAAK,WAAW,UAAU;AACzE,MAAI,gBAAgB,SAAS,GAAG;AAC9B,UAAM,IAAI;AAAA,MACR,UAAU,SAAS;AAAA,IAErB;AAAA,EACF;AAEA,QAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAM,iBAAiB,QAAQ,WAAW,OAAO,WAAW;AAI5D,MAAI,OAAO,WAAW;AACpB,eAAW,CAAC,YAAY,OAAO,KAAK,OAAO,QAAQ,OAAO,SAAS,GAAG;AACpE,iBAAW,CAAC,IAAI,QAAQ,KAAK,OAAO,QAAQ,OAAO,GAAG;AACpD,cAAM,iBAAiB,IAAI,WAAW,YAAY,IAAI,QAAQ;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,WAAW,EAAE,QAAQ,KAAK,QAAQ,WAAW,YAAY,MAAe,cAAc,KAAK;AACjG,QAAM,iBAAiB;AAAA,IAAI;AAAA,IAAW;AAAA,IAAS;AAAA,IAC7C;AAAA,MAAoB,EAAE,YAAY,SAAS,IAAI,YAAY,SAAS,EAAE;AAAA,MACpE,EAAE,IAAI,WAAW,IAAI,IAAI,MAAM,KAAK,UAAU,QAAQ,EAAE;AAAA,IAAC;AAAA,EAAC;AAE9D,SAAO,EAAE,WAAW,YAAY,MAAM,QAAQ,KAAK,OAAO;AAC5D;AAgCA,SAAS,UAAU,GAAuD;AACxE,SAAO,gBAAgB,KAAK,EAAE,eAAe;AAC/C;AAuBA,eAAsB,8BACpB,OACA,WACA,MAC4B;AAC5B,QAAM,EAAE,QAAQ,YAAY,IAAI;AAIhC,MAAI,UAAU,IAAI,KAAK,CAAC,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,YAAY,QAAQ,GAAG;AACzE,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAGA,QAAM,cAAc,MAAM,MAAM,IAAI,WAAW,SAAS,UAAU;AAClE,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI;AAAA,MACR,UAAU,SAAS;AAAA,IAErB;AAAA,EACF;AACA,QAAM,WAAW,KAAK,MAAM,YAAY,KAAK;AAI7C,MAAI,SAAS,eAAe,UAAa,SAAS,iBAAiB,QAAW;AAC5E,UAAM,IAAI;AAAA,MACR,UAAU,SAAS,qDAAqD,SAAS,UAAU;AAAA,IAC7F;AAAA,EACF;AAKA,QAAM,gBAAgB,MAAM,WAAW,SAAS,cAAc,WAAW;AAUzE,QAAM,kBAAkB,MAAM,MAAM,IAAI,WAAW,YAAY,MAAM;AACrE,QAAM,eAAe,MAAM,MAAM,KAAK,WAAW,UAAU,GAAG,OAAO,CAAC,MAAM,MAAM,MAAM;AACxF,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,IAAI;AAAA,MACR,UAAU,SAAS,uEAAuE,MAAM;AAAA,IAClG;AAAA,EACF;AAOA,QAAM,uBAAuB,CAAC,GAAG,cAAc,KAAK,CAAC;AACrD,QAAM,YAAY,kBAAmB,KAAK,MAAM,gBAAgB,KAAK,EAAkB,OAAO,CAAC;AAC/F,QAAM,cAAc,oBAAoB,QAAQ,qBAAqB,MAAM,CAAC,MAAM,KAAK,SAAS;AAChG,MAAI,CAAC,aAAa;AAMhB,UAAM,SAAS,UAAU,IAAI,IACzB,MAAM,qBAAqB,OAAO,WAAW,KAAK,UAAU,IAC5D,KAAK;AAGT,UAAM,WAAW,MAAM,mBAAmB,OAAO,WAAW,EAAE,QAAgB,OAAe,CAAC;AAG9F,UAAM,MAAM,MAAM,MAAM,IAAI,WAAW,YAAY,MAAM;AACzD,QAAI,CAAC,IAAK,OAAM,IAAI,mBAAmB,sBAAsB,MAAM,mBAAmB;AACtF,UAAM,cAAc,KAAK,MAAM,IAAI,KAAK;AACxC,UAAM,MAAM,SAAS;AACrB,QAAI,CAAC,IAAK,OAAM,IAAI,mBAAmB,sBAAsB,MAAM,2CAA2C;AAC9G,UAAM,aAAqC,EAAE,GAAG,YAAY,KAAK;AACjE,eAAW,CAAC,YAAY,GAAG,KAAK,eAAe;AAC7C,iBAAW,UAAU,IAAI,MAAM,QAAQ,KAAK,GAAG;AAAA,IACjD;AAUA,UAAM,SAAsB,EAAE,GAAG,aAAa,MAAM,WAAW;AAG/D,UAAM,YAAY,EAAE,GAAG,QAAQ,cAAc,gBAAgB,YAAY,YAAY,EAAE;AACvF,UAAM,YAAY,iBAAiB,UAAU,gBAAgB;AAC7D,UAAM,aAA0B,EAAE,GAAG,WAAW,YAAY,MAAM,cAAc,WAAW,SAAS,EAAE;AACtG,UAAM,MAAM,IAAI,WAAW,YAAY,QAAQ,EAAE,GAAG,KAAK,OAAO,KAAK,UAAU,UAAU,EAAE,CAAC;AAAA,EAC9F;AAMA,QAAM,YAAY,cAAc,IAAI,iBAAiB;AACrD,MAAI,WAAW;AACb,UAAM,SAAS,IAAI,YAAY;AAAA,MAC7B,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,MACX,QAAQ,YAAY;AAAA,MACpB,OAAO;AAAA,IACT,CAAC;AACD,UAAM,iBAAiB,yBAAyB,MAAM;AACtD,UAAM,iBAAiB,0BAA0B,SAAS,MAAM;AAIhE,UAAM,kBAAkB,IAAI,KAAK,MAAM,OAAO,eAAe,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AACpF,QAAI,CAAC,gBAAgB,IAAI,cAAc,GAAG;AACxC,YAAM,OAAO,OAAO,EAAE,IAAI,aAAa,YAAY,IAAI,IAAI,IAAI,SAAS,GAAG,OAAO,IAAI,aAAa,IAAI,QAAQ,eAAe,CAAC;AAAA,IACjI;AACA,QAAI,CAAC,gBAAgB,IAAI,cAAc,GAAG;AACxC,YAAM,OAAO,OAAO,EAAE,IAAI,aAAa,YAAY,IAAI,IAAI,IAAI,SAAS,GAAG,OAAO,IAAI,aAAa,IAAI,QAAQ,eAAe,CAAC;AAAA,IACjI;AAAA,EACF;AASA,MAAI,UAAU,IAAI,GAAG;AACnB,UAAM,EAAE,YAAY,IAAI,MAAM,OAAO,qBAAoB;AACzD,UAAM,KAAK,MAAM,YAAY;AAAA,MAC3B;AAAA,MACA,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,YAAY,KAAK;AAAA,MACjB,gBAAgB,KAAK;AAAA,IACvB,CAAC;AACD,UAAM,GAAG,KAAK,2BAA2B,WAAW,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACjF;AAMA,QAAM,WAAW,EAAE,QAAQ,SAAS,QAAQ,WAAW,SAAS,WAAW,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE;AAChH,QAAM,MAAM,IAAI,WAAW,SAAS,YAAY,EAAE,GAAG,aAAa,OAAO,KAAK,UAAU,QAAQ,EAAE,CAAC;AAEnG,SAAO,EAAE,WAAW,OAAO;AAC7B;;;ACtVA,eAAsB,0BACpB,aACA,aAC4C;AAC5C,QAAM,SAAS,cAAc,WAAW;AACxC,MAAI,OAAO,eAAe,yBAAyB,OAAO,iBAAiB,QAAW;AACpF,UAAM,IAAI,MAAM,kEAAkE;AAAA,EACpF;AACA,QAAM,EAAE,SAAS,IAAI,MAAM,QAAQ,WAAW;AAC9C,QAAM,EAAE,MAAM,KAAK,IAAI,4BAA4B,QAAQ;AAC3D,QAAM,OAAO,MAAM,WAAW,MAAM,WAAW;AAC/C,QAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAM,MAAyC,CAAC;AAChD,aAAW,CAAC,YAAY,IAAI,KAAK,OAAO,QAAQ,OAAO,WAAW,GAAG;AACnE,UAAM,MAAM,KAAK,IAAI,UAAU;AAC/B,QAAI,QAAQ,OAAW;AACvB,UAAM,OAA0B,CAAC;AACjC,eAAW,CAAC,IAAI,GAAG,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC5C,YAAM,YAAY,MAAM,iBAAiB,EAAE,YAAY,GAAG,GAAG,KAAK,GAAG;AACrE,YAAM,OAAO,KAAK,MAAM,SAAS;AACjC,WAAK,KAAK;AAAA,QACR;AAAA,QACA,QAAQ,EAAE,GAAG,MAAM,GAAG;AAAA,QACtB,IAAI,IAAI;AAAA,QACR,SAAS,IAAI;AAAA,QACb,GAAI,IAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,QAAQ,IAAI,CAAC;AAAA,QAC3D,GAAI,IAAI,cAAc,SAAY,EAAE,UAAU,IAAI,UAAU,IAAI,CAAC;AAAA,MACnE,CAAC;AAAA,IACH;AACA,QAAI,UAAU,IAAI;AAAA,EACpB;AACA,SAAO;AACT;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-lookup/search/collection-facade.ts"],"sourcesContent":["/**\n * Collection-level search / retrieval (`search` / `retrieve` / `similarTo` +\n * the lexical-index build/flush/warm machinery).\n *\n * This is the retrieval surface: a pure client-side lexical scan\n * (`search`), the persisted-or-in-memory inverted-index lifecycle\n * (`flushIndex` / `warmIndex` / `buildRetrievalDocs` + the dict-label / blob-\n * filename resolvers that feed it), and the lexical / semantic / hybrid\n * `retrieve` fan-out plus raw-vector kNN (`similarTo`). None of it touches the\n * write path — it reads the live eager cache and the encrypted `_vec` / `_ftindex`\n * side-cars.\n *\n * Every function takes a small {@link SearchContext} instead of `this`,\n * mirroring the `record-keys/` siblings.\n *\n * The `cache` is the SAME `Map` reference `Collection` owns (passed by\n * reference, never copied) so the index always builds over the live working\n * set. {@link buildPersistedIndexCallbacks} is special: it is invoked from the\n * `Collection` constructor *before* `this.codec` exists, so it takes a context\n * THUNK and resolves the context lazily at each callback invocation.\n *\n * Internal service — not exported as a `@noy-db/hub/*` subpath.\n */\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { RecordCodec } from '../../kernel/enclave/index.js'\nimport { stripI18nFilled, type I18nTextDescriptor } from '../../via/i18n/core.js'\nimport { isStaticDictDescriptor, type DictKeyDescriptor, type StaticDictDescriptor, type DictionaryHandle } from '../../via/i18n/dictionary.js'\nimport type { BlobSet } from '../../with-shape/blobs/blob-set.js'\nimport type { BlobFieldsConfig } from '../../with-shape/blobs/blob-compaction.js'\nimport type { Query } from '../../kernel/query/index.js'\nimport { embeddingSourceText, deriveChunkVectors, type VectorSet, type EmbeddingDescriptor, type StoredVector, type StoredChunk } from '../embeddings/index.js'\nimport { encodeVecId, decodeVecId, isVecIdFor } from '../embeddings/vec-id.js'\nimport { EmbeddingDimMismatchError } from '../../kernel/errors.js'\nimport { liveRecordIsElevated } from '../../kernel/tier-visibility.js'\nimport { searchScan, fuseRetrieval, segmentTokenizer, type SearchOptions, type SearchResult } from './index.js'\nimport type { IndexStore } from './index-store.js'\nimport type { PersistedIndexCallbacks } from './persisted-index-store.js'\nimport { extractSnippet } from './snippet.js'\nimport { buildStringFieldEntries, buildI18nFieldEntries, buildDictKeyFieldEntries, buildBlobFieldEntries } from './build-docs.js'\nimport type { IndexDoc, IndexHit, IndexBuildOptions } from './inverted-index.js'\nimport type { RetrieveOptions, RetrieveHit, SimilarToOptions } from './retrieve-types.js'\n\n/** Everything the moving search/retrieval methods touched on `this.*`. */\nexport interface SearchContext<T> {\n /** Collection name — error context + `_ftindex` row key. */\n readonly name: string\n /** Vault namespace the side-cars (`_vec`, `_ftindex`) live under. */\n readonly vault: string\n /** The ciphertext store. */\n readonly adapter: NoydbStore\n /** The record codec — decrypts the `_vec` / `_ftindex` side-car bodies. */\n readonly codec: RecordCodec<T>\n /** The eager working-set cache (SHARED `Map` reference, never copied). */\n readonly cache: Map<string, { record: T; version: number }>\n /** True in lazy mode — search/retrieve/warm require eager mode. */\n readonly lazy: boolean\n /** Declared text-index fields, or undefined. */\n readonly textIndexes: readonly string[] | undefined\n /** Subset of `textIndexes` recording token positions for phrase / proximity (#1354). */\n readonly textIndexPositions: readonly string[] | undefined\n /** Declared i18n fields, or undefined. */\n readonly i18nFields: Record<string, I18nTextDescriptor> | undefined\n /** Declared dictionary-key fields, or undefined. */\n readonly dictKeyFields: Record<string, DictKeyDescriptor | StaticDictDescriptor> | undefined\n /** Declared blob fields, or undefined. */\n readonly blobFields: BlobFieldsConfig | undefined\n /** Dictionary handle resolver (dynamic dict-key labels), or undefined. */\n readonly getDictionary: ((name: string) => Promise<DictionaryHandle>) | undefined\n /** The lexical inverted-index store (in-memory or persisted), or undefined. */\n readonly searchIndexStore: IndexStore | undefined\n /** The vector set for semantic retrieval, or undefined. */\n readonly vectorSet: VectorSet | undefined\n /** The embeddings descriptor, or undefined. */\n readonly embeddings: EmbeddingDescriptor | undefined\n /** Hydrate the eager cache before reading it. */\n ensureHydrated(): Promise<void>\n /** Open the blob facade for a record (used to list indexed blob filenames). */\n blob(id: string): BlobSet\n}\n\n/**\n * Client-side lexical scan over the live eager cache. Eager mode only;\n * nothing searchable is written to the store.\n */\nexport async function search<T>(ctx: SearchContext<T>, field: string, query: string, opts: SearchOptions = {}): Promise<SearchResult<T>[]> {\n if (ctx.lazy) {\n throw new Error(\n `Collection \"${ctx.name}\": search() (scan mode) requires eager mode (prefetch: true). ` +\n `A store-usable blind index for lazy / at-scale search is a separate gated opt-in (#308).`,\n )\n }\n await ctx.ensureHydrated()\n const entries: { id: string; record: T }[] = []\n // Strip the internal densify marker from the user-facing records.\n // Non-mutating: never touches the cached record object. The search index\n // is built over the same (marker-free) record, which is fine — the marker\n // is never a searchable field.\n for (const [id, e] of ctx.cache) entries.push({ id, record: stripI18nFilled(e.record as Record<string, unknown>) as T })\n return searchScan(entries, field, query, opts)\n}\n\n/** L1 — build IndexDoc[] for the configured text fields over the live cache. */\nexport function buildRetrievalDocs<T>(\n ctx: SearchContext<T>,\n labelMaps: Map<string, Map<string, Record<string, string>>>,\n blobFilenames: Map<string, Map<string, string[]>>,\n only?: readonly string[],\n): IndexDoc[] {\n const docs: IndexDoc[] = []\n for (const [id, e] of ctx.cache) {\n const rec = stripI18nFilled(e.record as Record<string, unknown>)\n const fields = buildStringFieldEntries(rec, ctx.textIndexes ?? [], only)\n if (ctx.i18nFields) fields.push(...buildI18nFieldEntries(rec, ctx.i18nFields, ctx.textIndexes ?? [], only))\n if (ctx.dictKeyFields) fields.push(...buildDictKeyFieldEntries(rec, ctx.dictKeyFields, labelMaps, ctx.textIndexes ?? [], only))\n const blobNames = blobFilenames.get(id)\n if (blobNames) fields.push(...buildBlobFieldEntries(blobNames))\n if (fields.length > 0) docs.push({ id, fields })\n }\n return docs\n}\n\n/**\n * L1 — the positional-postings opt-in for this collection (#1354), or\n * `undefined` when nothing opted in. Returning `undefined` rather than\n * `{ positions: [] }` keeps the no-opt-in path byte-identical to a caller that\n * never heard of positions.\n */\nexport function positionBuildOptions<T>(ctx: SearchContext<T>): IndexBuildOptions | undefined {\n const fields = ctx.textIndexPositions\n return fields && fields.length > 0 ? { positions: fields } : undefined\n}\n\n/** L1 — true iff any configured text index is also a blob field (gates ALL slot I/O). */\nexport function hasIndexedBlobFields<T>(ctx: SearchContext<T>, only?: readonly string[]): boolean {\n if (!ctx.blobFields || !ctx.textIndexes) return false\n const fields = only ? ctx.textIndexes.filter((f) => only.includes(f)) : ctx.textIndexes\n return fields.some((f) => f in ctx.blobFields!)\n}\n\n/**\n * L1 — resolve `recordId -> (blobField -> filenames[])` by listing slots\n * for the configured blob fields of each cached record. Blob slot metadata is\n * NOT inline on the record: it lives in a separate `_blob_slots_*` collection,\n * so this costs ONE `blob(id).list()` (a `listSlots`) per record at build time\n * — the heaviest indexing source. Fully gated by {@link hasIndexedBlobFields};\n * non-blob (and blob-but-not-indexed) collections do ZERO slot I/O.\n */\nasync function resolveBlobFilenames<T>(ctx: SearchContext<T>, only?: readonly string[]): Promise<Map<string, Map<string, string[]>>> {\n const out = new Map<string, Map<string, string[]>>()\n if (!hasIndexedBlobFields(ctx, only)) return out\n const indexed = (only ? ctx.textIndexes!.filter((f) => only.includes(f)) : ctx.textIndexes!)\n .filter((f) => f in ctx.blobFields!)\n const indexedSet = new Set(indexed)\n for (const id of ctx.cache.keys()) {\n let slots\n try {\n slots = await ctx.blob(id).list()\n } catch {\n continue\n }\n let byField: Map<string, string[]> | undefined\n for (const slot of slots) {\n if (!indexedSet.has(slot.name) || !slot.filename) continue\n if (!byField) { byField = new Map(); out.set(id, byField) }\n const names = byField.get(slot.name)\n if (names) names.push(slot.filename)\n else byField.set(slot.name, [slot.filename])\n }\n }\n return out\n}\n\n/** L1 — field -> (key -> {locale->label}) for dictKey fields; static from table, dynamic via getDictionary().list(). */\nasync function resolveDictLabelMaps<T>(ctx: SearchContext<T>): Promise<Map<string, Map<string, Record<string, string>>>> {\n const maps = new Map<string, Map<string, Record<string, string>>>()\n if (!ctx.dictKeyFields || !ctx.textIndexes) return maps\n for (const field of ctx.textIndexes) {\n const desc = ctx.dictKeyFields[field]\n if (!desc) continue\n const m = new Map<string, Record<string, string>>()\n if (isStaticDictDescriptor(desc)) {\n for (const [key, labels] of Object.entries(desc.table)) m.set(key, labels as Record<string, string>)\n } else {\n if (ctx.getDictionary) {\n const handle = await ctx.getDictionary(desc.name)\n for (const e of await handle.list()) m.set(e.key, e.labels)\n }\n }\n maps.set(field, m)\n }\n return maps\n}\n\n/** L1.5 — force-persist the lexical index now (e.g. on save/idle). Persists only when textIndexPersist is enabled; a no-op otherwise. */\nexport async function flushIndex<T>(ctx: SearchContext<T>): Promise<void> {\n if (!ctx.searchIndexStore) return\n await ctx.ensureHydrated()\n const labelMaps = await resolveDictLabelMaps(ctx)\n const blobFilenames = await resolveBlobFilenames(ctx)\n await ctx.searchIndexStore.ensureBuilt(() => buildRetrievalDocs(ctx, labelMaps, blobFilenames), positionBuildOptions(ctx))\n await ctx.searchIndexStore.flush?.()\n}\n\n/** L2 — load + decrypt all _vec sidecars into StoredVector[] for the VectorSet. */\nfunction buildVectorLoad<T>(ctx: SearchContext<T>): () => Promise<StoredVector[]> {\n return async () => {\n const ids = await ctx.adapter.list(ctx.vault, '_vec')\n const out: StoredVector[] = []\n for (const vecId of ids) {\n // #726: _vec is a vault-wide bucket namespaced by collection prefix —\n // skip rows belonging to other collections before touching this one's.\n if (!isVecIdFor(ctx.name, vecId)) continue\n const id = decodeVecId(ctx.name, vecId)!\n // #721 defense-in-depth: a _vec row carries no _tier of its own; the purge\n // on elevate is best-effort and cannot reach a legacy sidecar, so gate on\n // the owning record's live tier. Envelope peek, no decryption.\n if (await liveRecordIsElevated(ctx.adapter, ctx.vault, ctx.name, id)) continue\n const env = await ctx.adapter.get(ctx.vault, '_vec', vecId)\n if (!env) continue\n // #726 fails-safe: a row can pass isVecIdFor's prefix filter yet still\n // be undecryptable under THIS collection's DEK — a surviving legacy\n // bare-id row whose record id happens to start with `<thisCollection>/`,\n // or plain corruption. _vec sidecars are derived, re-derivable\n // artifacts (embedOnWrite regenerates them on the record's next put()),\n // so best-effort skip is correct: never let one poison row crash\n // similarTo()/retrieve() for the whole collection.\n let body: string | null\n try {\n body = await ctx.codec.decryptJsonString({ collection: '_vec', id: vecId }, env)\n } catch {\n continue\n }\n if (body === null) continue\n // #1360: a body carries EITHER `vec` (whole-record, the original shape,\n // still written whenever no `chunk` hook is declared) or `chunks`\n // (sub-document). Reading both shapes is what makes chunking adoptable\n // without a rewrite: existing `_vec` rows keep loading unchanged.\n const parsed = JSON.parse(body) as { vec?: number[]; chunks?: { id: string; start: number; end: number; vec: number[] }[]; model: string }\n if (parsed.chunks && parsed.chunks.length > 0) {\n const chunks: StoredChunk[] = parsed.chunks.map((c) => ({ id: c.id, start: c.start, end: c.end, vec: new Float32Array(c.vec) }))\n out.push({ id, model: parsed.model, chunks })\n } else if (parsed.vec) {\n out.push({ id, vec: new Float32Array(parsed.vec), model: parsed.model })\n }\n }\n return out\n }\n}\n\n/**\n * L1.5 — build the PersistedIndexCallbacks bridge: crypto lives here\n * (collection has getDEK / encryptJsonString / decryptJsonString / adapter),\n * the index store itself is crypto-free.\n *\n * Fingerprint encoding: body-wrap approach — save(json, fp) stores\n * JSON.stringify({ fp, idx: json }) as the encrypted body so the standard\n * EncryptedEnvelope shape is never extended. load() decrypts and JSON.parses\n * the wrapper back out.\n *\n * Cache shape: ctx.cache stores { record, version } — currentFingerprint()\n * iterates over e.version.\n *\n * NOTE: this factory is invoked from the `Collection` constructor *before*\n * `this.codec` is assigned, so it takes a context THUNK and resolves the\n * context lazily inside each callback — by the time a callback actually runs,\n * the codec and cache are fully wired.\n */\nexport function buildPersistedIndexCallbacks<T>(provideCtx: () => SearchContext<T>): PersistedIndexCallbacks {\n const FT = '_ftindex'\n return {\n load: async () => {\n const ctx = provideCtx()\n const env = await ctx.adapter.get(ctx.vault, FT, ctx.name)\n if (!env) return null\n const body = await ctx.codec.decryptJsonString({ collection: FT, id: ctx.name }, env)\n if (body === null) return null\n try {\n const wrapped = JSON.parse(body) as { fp: { count: number; maxVersion: number }; idx: string }\n return { json: wrapped.idx, fingerprint: wrapped.fp }\n } catch {\n return null\n }\n },\n save: async (json, fp, isStale) => {\n const ctx = provideCtx()\n const body = JSON.stringify({ fp, idx: json })\n const env = await ctx.codec.encryptJsonString({ collection: FT, id: ctx.name }, body, fp.count)\n // #725 review: a purge (removePersisted) can land while the encrypt above was\n // in flight — check right before the write and skip it rather than resurrect a\n // purged/forgotten record's blob. PersistedIndexStore's own post-save epoch\n // check is the backstop for every other ordering (incl. a purge landing during\n // this very put).\n if (isStale()) return\n await ctx.adapter.put(ctx.vault, FT, ctx.name, env)\n },\n remove: async () => { const ctx = provideCtx(); await ctx.adapter.delete(ctx.vault, FT, ctx.name) },\n currentFingerprint: () => {\n const ctx = provideCtx()\n let maxVersion = 0\n for (const e of ctx.cache.values()) if (e.version > maxVersion) maxVersion = e.version\n return { count: ctx.cache.size, maxVersion }\n },\n }\n}\n\n/** L1 — pre-build the lexical index (e.g. on open) so the first retrieve() pays no build scan. */\nexport async function warmIndex<T>(ctx: SearchContext<T>): Promise<void> {\n if (!ctx.searchIndexStore) return\n if (ctx.lazy) {\n throw new Error(\n `Collection \"${ctx.name}\": warmIndex() requires eager mode (prefetch: true).`,\n )\n }\n await ctx.ensureHydrated()\n const built = ctx.searchIndexStore.built\n const labelMaps = built ? new Map() : await resolveDictLabelMaps(ctx)\n const blobFilenames = built ? new Map() : await resolveBlobFilenames(ctx)\n await ctx.searchIndexStore.ensureBuilt(() => buildRetrievalDocs(ctx, labelMaps, blobFilenames), positionBuildOptions(ctx))\n}\n\n/**\n * Sentinel `limit` pushed into the mode branches when a `within` is present, so\n * the branch's own truncation (`InvertedIndex.query`'s slice, `cosineTopK`'s\n * `k`, `fuseRetrieval`'s `limit`) becomes a no-op and the caller's real `limit`\n * can be applied AFTER narrowing. `slice(0, Infinity)` is the whole list.\n */\nconst NO_LIMIT = Number.POSITIVE_INFINITY\n\n/**\n * Retrieval. mode: 'lexical' (default) | 'semantic' (L2) | 'hybrid' (L3).\n *\n * ## `within` — the typed half of retrieval (#1343)\n *\n * Money / number / date / boolean fields are NOT tokenised into the lexical\n * index and never will be: instead of a second (typed-token) index format, a\n * typed match is expressed as an ordinary `Query` and handed to `within`, so\n * the comparison runs in the query engine — over the sorted / compound range\n * indexes — in each field's CANONICAL space (money as its scaled integer, not\n * as a lexically-compared string). `retrieve()` contributes the text half and\n * intersects.\n *\n * Two ordering properties, both load-bearing:\n *\n * - **Scoring is global; narrowing is not.** BM25 runs over the WHOLE corpus\n * (df / N / avgdl unchanged by `within`), so a document's score is the same\n * whichever `within` it is paired with — a filter re-ranks nothing. Narrowing\n * the postings instead would make IDF filter-dependent and scores\n * incomparable between two calls. It also costs nothing to score first: the\n * index already scores every doc and only slices at the end.\n * - **`limit` counts hits from the NARROWED set.** Truncating corpus-wide first\n * and filtering after would under-return (`limit: 10` yielding 1), so the\n * mode branches run unlimited and the caller's `limit` is applied last.\n *\n * An empty `within` result set yields NO hits — never all of them.\n *\n * A `within` with an empty text query is **typed-only retrieval**: the typed\n * predicate is the whole query, returned in the usual hit shape (score 0,\n * `field: '(within)'`, empty snippet) so a typed and a text search are read the\n * same way. An empty query with NO `within` still matches nothing.\n */\nexport async function retrieve<T>(ctx: SearchContext<T>, query: string, opts: RetrieveOptions<T> = {}): Promise<RetrieveHit<T>[]> {\n if (opts.within === undefined) {\n return opts.mode === 'semantic' ? retrieveSemantic(ctx, query, opts)\n : opts.mode === 'hybrid' ? retrieveHybrid(ctx, query, opts)\n : retrieveLexical(ctx, query, opts)\n }\n // Typed-only: no query terms to score, so the whole answer is the id set.\n // Checked before the mode branches so it needs neither a textIndexes config\n // nor an embeddings config — a collection of pure money/number fields is a\n // legitimate `retrieve({ within })` target.\n if (segmentTokenizer(query).length === 0) return retrieveWithinOnly(ctx, opts, opts.within)\n const unlimited: RetrieveOptions<T> = { ...opts, limit: NO_LIMIT }\n const hits =\n opts.mode === 'semantic' ? await retrieveSemantic(ctx, query, unlimited)\n : opts.mode === 'hybrid' ? await retrieveHybrid(ctx, query, unlimited)\n : await retrieveLexical(ctx, query, unlimited)\n return applyWithin(hits, opts.within, opts.limit)\n}\n\n/**\n * L3 — keep only hits whose id matches the structured query, truncate to the\n * caller's `limit` (the mode branches ran unlimited), re-rank 1-based.\n */\nfunction applyWithin<T>(hits: RetrieveHit<T>[], within: Query<T>, limit: number | undefined): RetrieveHit<T>[] {\n const ids = new Set(within._idArray())\n const kept = hits.filter(h => ids.has(h.id))\n const limited = limit !== undefined ? kept.slice(0, limit) : kept\n return limited.map((h, i) => ({ ...h, rank: i + 1 }))\n}\n\n/**\n * #1343 — typed-only retrieval: `retrieve('', { within })`. The hits are the\n * `within` ids in query order, presented like any other hit so callers need no\n * second result shape. `score` is 0 and `field` is `'(within)'` because nothing\n * was scored — a structured predicate either matches or does not, and faking a\n * BM25 number would invite sorting by it.\n */\nasync function retrieveWithinOnly<T>(ctx: SearchContext<T>, opts: RetrieveOptions<T>, within: Query<T>): Promise<RetrieveHit<T>[]> {\n if (ctx.lazy) {\n throw new Error(`Collection \"${ctx.name}\": retrieve() requires eager mode (prefetch: true).`)\n }\n await ctx.ensureHydrated()\n const ids = within._idArray()\n const limited = opts.limit !== undefined ? ids.slice(0, opts.limit) : ids\n return limited.map((id, i) => {\n const base: RetrieveHit<T> = { id, score: 0, rank: i + 1, field: '(within)', snippet: '' }\n if (opts.includeRecord) {\n const e = ctx.cache.get(id)\n if (e) (base as { record?: T }).record = stripI18nFilled(e.record as Record<string, unknown>) as T\n }\n return base\n })\n}\n\n/** L1 — client-side lexical retrieval; ranked { id, score, field, snippet, locale? }. */\nasync function retrieveLexical<T>(ctx: SearchContext<T>, query: string, opts: RetrieveOptions<T>): Promise<RetrieveHit<T>[]> {\n if (!ctx.searchIndexStore) {\n throw new Error(`Collection \"${ctx.name}\": retrieve() requires a textIndexes config.`)\n }\n if (ctx.lazy) {\n throw new Error(\n `Collection \"${ctx.name}\": retrieve() requires eager mode (prefetch: true).`,\n )\n }\n await ctx.ensureHydrated()\n const built = ctx.searchIndexStore.built\n const labelMaps = built ? new Map() : await resolveDictLabelMaps(ctx)\n const blobFilenames = built ? new Map() : await resolveBlobFilenames(ctx)\n const index = await ctx.searchIndexStore.ensureBuilt(() => buildRetrievalDocs(ctx, labelMaps, blobFilenames), positionBuildOptions(ctx))\n const hits = index.query(query, {\n ...(opts.limit !== undefined ? { limit: opts.limit } : {}),\n ...(opts.match ? { match: opts.match } : {}),\n ...(opts.prefix ? { prefix: opts.prefix } : {}),\n ...(opts.fields ? { fields: opts.fields } : {}),\n ...(opts.boost ? { boost: opts.boost } : {}),\n })\n const window = opts.snippetWindow ?? 80\n return hits.map((h: IndexHit, i: number) => {\n const base: RetrieveHit<T> = {\n id: h.id,\n score: h.score,\n rank: i + 1,\n field: h.field,\n snippet: extractSnippet(h.text, h.offset, window),\n ...(h.locale !== undefined ? { locale: h.locale } : {}),\n ...(opts.includeRecord\n ? (() => {\n const e = ctx.cache.get(h.id)\n return e ? { record: stripI18nFilled(e.record as Record<string, unknown>) as T } : {}\n })()\n : {}),\n }\n return base\n })\n}\n\n/** L3 — hybrid: fuse lexical (L1) + semantic (L2) by RRF. Requires embeddings. */\nasync function retrieveHybrid<T>(ctx: SearchContext<T>, query: string, opts: RetrieveOptions<T>): Promise<RetrieveHit<T>[]> {\n if (!ctx.embeddings) {\n throw new Error(`Collection \"${ctx.name}\": retrieve({mode:'hybrid'}) requires an embeddings config.`)\n }\n const [lex, sem] = await Promise.all([\n retrieveLexical(ctx, query, opts),\n retrieveSemantic(ctx, query, opts),\n ])\n return fuseRetrieval([lex, sem], opts.limit !== undefined ? { limit: opts.limit } : {})\n}\n\n/** L2 — semantic branch of retrieve(): encode query → similarTo(). */\nasync function retrieveSemantic<T>(ctx: SearchContext<T>, query: string, opts: RetrieveOptions<T>): Promise<RetrieveHit<T>[]> {\n if (!ctx.embeddings) throw new Error(`Collection \"${ctx.name}\": retrieve({mode:'semantic'}) requires an embeddings config.`)\n if (ctx.lazy) throw new Error(`Collection \"${ctx.name}\": retrieve() requires eager mode (prefetch: true).`)\n const qVec = await ctx.embeddings.encode(query)\n return similarTo(ctx, qVec, {\n ...(opts.limit !== undefined ? { k: opts.limit } : {}),\n ...(opts.minScore !== undefined ? { minScore: opts.minScore } : {}),\n ...(opts.includeRecord ? { includeRecord: true } : {}),\n ...(opts.exact !== undefined ? { exact: opts.exact } : {}),\n ...(opts.nprobe !== undefined ? { nprobe: opts.nprobe } : {}),\n })\n}\n\n/** L2 — raw-vector kNN over the encrypted vector set (decrypted in the trusted tier).\n * Snippet is '' for vector hits in v1 (semantic match isn't span-located). */\nexport async function similarTo<T>(ctx: SearchContext<T>, vector: Float32Array, opts: SimilarToOptions = {}): Promise<RetrieveHit<T>[]> {\n if (!ctx.embeddings || !ctx.vectorSet) throw new Error(`Collection \"${ctx.name}\": similarTo() requires an embeddings config.`)\n if (ctx.lazy) throw new Error(`Collection \"${ctx.name}\": similarTo() requires eager mode (prefetch: true).`)\n await ctx.ensureHydrated()\n await ctx.vectorSet.ensureLoaded(buildVectorLoad(ctx))\n // #1360 part 2: `topK` picks exact vs approximate from the collection's\n // `embeddings.index` opt-in, the live vector count and this call's `exact`.\n // With no opt-in it IS `cosineTopK` — same function, same numbers.\n const hits = await ctx.vectorSet.topK(vector, opts.k ?? 10, {\n ...(opts.minScore !== undefined ? { minScore: opts.minScore } : {}),\n ...(opts.exact !== undefined ? { exact: opts.exact } : {}),\n ...(opts.nprobe !== undefined ? { nprobe: opts.nprobe } : {}),\n expectModel: ctx.embeddings.model,\n })\n return hits.map((h, i) => {\n const e = ctx.cache.get(h.id)\n // #1360: for a chunk hit the snippet IS the matched span — resolved by\n // re-deriving the record's joined source text and slicing it at the same\n // offsets the writer used, so the returned span and the returned snippet\n // cannot disagree. Unchunked hits keep snippet '' (a whole-record vector\n // match is not span-located).\n let snippet = ''\n if (h.chunk && e && ctx.embeddings) {\n snippet = embeddingSourceText(e.record as Record<string, unknown>, ctx.embeddings.source).slice(h.chunk.start, h.chunk.end)\n }\n const base: RetrieveHit<T> = { id: h.id, score: h.score, rank: i + 1, field: '(vector)', snippet, ...(h.chunk ? { chunk: h.chunk } : {}) }\n if (opts.includeRecord && e) (base as { record?: T }).record = stripI18nFilled(e.record as Record<string, unknown>) as T\n return base\n })\n}\n\n/**\n * L2 — the embedding write-hook: derive a record's embedding vector at\n * write and persist it as the encrypted `_vec` side-car. Routed through the\n * search strategy so it runs only when `withSearch()` is opted in — computing\n * vectors that no gated `similarTo` / semantic `retrieve` could query would be\n * dead weight, so the compute is paired with the search capability. The caller\n * (`Collection.put`) only invokes this when `embeddings` is declared.\n */\nexport async function embedOnWrite<T>(ctx: SearchContext<T>, id: string, record: T, version: number): Promise<void> {\n if (!ctx.embeddings) return\n const text = embeddingSourceText(record as Record<string, unknown>, ctx.embeddings.source)\n // #1360 — sub-document chunking. When a `chunk` hook is declared and yields\n // usable spans, the sidecar holds ONE VECTOR PER CHUNK and no whole-record\n // vector: the whole-record encode is the thing chunking exists to avoid (it\n // averages a long document's topics away, and can exceed the model's context\n // window — the very case that motivates chunking). No chunk hook, or no\n // usable span, and the body is byte-for-byte the pre-#1360 single-vector\n // shape. Both live in the SAME encrypted `_vec` sidecar: no new store\n // collection, no new plaintext surface.\n const chunks = await deriveChunkVectors(text, ctx.embeddings)\n let body: string\n let writtenVec: Float32Array | undefined\n if (chunks.length > 0) {\n for (const c of chunks) {\n if (c.vec.length !== ctx.embeddings.dim) throw new EmbeddingDimMismatchError('embeddings', ctx.embeddings.dim, c.vec.length)\n }\n body = JSON.stringify({\n chunks: chunks.map((c) => ({ id: c.id, start: c.start, end: c.end, vec: Array.from(c.vec) })),\n model: ctx.embeddings.model,\n dim: ctx.embeddings.dim,\n })\n } else {\n const vec = await ctx.embeddings.encode(text)\n if (vec.length !== ctx.embeddings.dim) throw new EmbeddingDimMismatchError('embeddings', ctx.embeddings.dim, vec.length)\n writtenVec = vec\n body = JSON.stringify({ vec: Array.from(vec), model: ctx.embeddings.model, dim: ctx.embeddings.dim })\n }\n const vecEnv = await ctx.codec.encryptJsonString({ collection: '_vec', id: encodeVecId(ctx.name, id) }, body, version)\n await ctx.adapter.put(ctx.vault, '_vec', encodeVecId(ctx.name, id), vecEnv)\n // #1360 part 2 — hand the just-computed vectors to the in-memory set rather\n // than dropping it. Equivalent to the old `markDirty()` by construction: the\n // sidecar we just wrote is exactly what a reload would decrypt back, so the\n // next query sees the same vectors either way. The difference is cost —\n // `markDirty()` re-reads and re-decrypts every sidecar in the collection on\n // the next query, and would additionally rebuild the approximate index from\n // scratch after every single `put()`.\n if (chunks.length > 0) {\n ctx.vectorSet?.upsert({ id, model: ctx.embeddings.model, chunks: chunks.map((c) => ({ id: c.id, start: c.start, end: c.end, vec: c.vec })) })\n } else {\n ctx.vectorSet?.upsert({ id, model: ctx.embeddings.model, vec: writtenVec! })\n }\n}\n\n/**\n * Force-re-derive every eligible tier-0 record's `_vec` sidecar once (#788).\n * Opt-in bulk repair for the #726 re-namespace: legacy bare-id rows are\n * unreachable to `buildVectorLoad` (it only recognises `<collection>/<id>`\n * keys) and otherwise self-heal only when a record is next `put()` — this\n * lets an adopter recover recall immediately instead of waiting on writes.\n *\n * **Skips elevated records** (`liveRecordIsElevated`) rather than refusing\n * the whole walk — the OPPOSITE of `_applyCutoverTransform`'s\n * `assertCutoverTierSafe` refuse-whole-batch precedent. An elevated record is\n * SUPPOSED to have no `_vec` (`syncTierSearch` purges it on elevate);\n * re-embedding one here would write searchable plaintext-derived data above\n * tier 0. Load-bearing: never write a `_vec` row for an elevated record.\n *\n * Tombstones, delete markers, and a raw `get` racing a delete between `list`\n * and `get` all decrypt to `null` (`decryptRecord` already folds\n * tombstone/delete-marker into that null) and are skipped the same way.\n * Unconditional re-derive, no already-migrated check — safe to re-run after\n * a partial failure (each id is independently idempotent).\n */\nexport async function rebuildEmbeddings<T>(ctx: SearchContext<T>): Promise<{ rebuilt: number; skipped: number }> {\n if (!ctx.embeddings) return { rebuilt: 0, skipped: 0 }\n const ids = await ctx.adapter.list(ctx.vault, ctx.name)\n let rebuilt = 0\n let skipped = 0\n for (const id of ids) {\n if (await liveRecordIsElevated(ctx.adapter, ctx.vault, ctx.name, id)) { skipped++; continue }\n const env = await ctx.adapter.get(ctx.vault, ctx.name, id)\n if (!env) { skipped++; continue }\n const decoded = await ctx.codec.decryptRecord({ collection: ctx.name, id }, env)\n if (decoded === null) { skipped++; continue }\n await embedOnWrite(ctx, id, decoded, env._v ?? 1)\n rebuilt++\n }\n return { rebuilt, skipped }\n}\n\n/**\n * Sync the collection's SEARCH artifacts after a tier move (#721). Both the\n * lexical `_ftindex` blob and the `_vec/<id>` embedding are encrypted under\n * the tier-0 DEK and hold the record's derived plaintext (full field text /\n * a text-invertible vector), so leaving them means elevation never hid what\n * the record was searchable by — the `forget()` precedent, unapplied to\n * elevate. `null` → the record left tier 0: purge its `_vec` sidecar (mirrors\n * `Collection._purgeVector`), and invalidate the `_ftindex` blob (mirrors\n * `Collection._purgeSearchIndex`: deletes the persisted blob when persisted,\n * else drops the in-memory index) so the next `retrieve()` rebuilds from the\n * elevated-free `ctx.cache`. A record → it is tier-0 again: re-embed it via\n * {@link embedOnWrite}, then invalidate `_ftindex` so the rebuild includes it\n * again. No-op fast when the collection has neither a lexical index nor a\n * vector set.\n */\nexport async function syncTierSearch<T>(\n ctx: SearchContext<T>,\n id: string,\n record: T | null,\n version?: number,\n): Promise<void> {\n if (!ctx.searchIndexStore && !ctx.vectorSet) return\n if (record === null) {\n await ctx.adapter.delete(ctx.vault, '_vec', encodeVecId(ctx.name, id))\n ctx.vectorSet?.removeRecord(id)\n } else {\n await embedOnWrite(ctx, id, record, version ?? 1)\n }\n const store = ctx.searchIndexStore\n if (store && 'removePersisted' in store) await (store as { removePersisted(): Promise<void> }).removePersisted()\n else store?.markDirty()\n}\n"],"mappings":"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","names":[]}