@noy-db/hub 0.4.0-pre.0 → 0.4.0-pre.10

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 (611) hide show
  1. package/README.md +3 -4
  2. package/dist/api-ZDKTWNBB.js +20 -0
  3. package/dist/attestation/index.js +18 -18
  4. package/dist/{backup-YPUEKIJC.js → backup-2UX7PRM5.js} +13 -13
  5. package/dist/blobs/index.js +30 -15
  6. package/dist/blobs/index.js.map +1 -1
  7. package/dist/broker/index.js +12 -12
  8. package/dist/broker/index.js.map +1 -1
  9. package/dist/cargo/index.js +58 -29
  10. package/dist/{chunk-IY23KFNZ.js → chunk-35NMVJ7I.js} +3 -3
  11. package/dist/{chunk-S3BFKGET.js → chunk-3HUYRN24.js} +4 -4
  12. package/dist/chunk-3HUYRN24.js.map +1 -0
  13. package/dist/{walk-IPLXE36W.js → chunk-3V3XR4S4.js} +4 -13
  14. package/dist/{walk-IPLXE36W.js.map → chunk-3V3XR4S4.js.map} +1 -1
  15. package/dist/{chunk-RTEK4C4I.js → chunk-4JQK3L4V.js} +2 -2
  16. package/dist/{chunk-3ELCWU56.js → chunk-4ZKNC6A6.js} +2 -2
  17. package/dist/{chunk-VQNJ7UZL.js → chunk-55LKVALY.js} +106 -10
  18. package/dist/chunk-55LKVALY.js.map +1 -0
  19. package/dist/{chunk-VAIS3SOM.js → chunk-55LX6FSX.js} +2 -2
  20. package/dist/chunk-5D2ALSM5.js +19 -0
  21. package/dist/chunk-5D2ALSM5.js.map +1 -0
  22. package/dist/{chunk-6HPG2YNX.js → chunk-5DRROK5W.js} +2 -2
  23. package/dist/{chunk-S6Q37VGE.js → chunk-5GQEU4DN.js} +2 -2
  24. package/dist/chunk-5JOFWL4G.js +25 -0
  25. package/dist/chunk-5JOFWL4G.js.map +1 -0
  26. package/dist/{chunk-ZXEZCY6F.js → chunk-63FE525P.js} +6 -6
  27. package/dist/chunk-6LJY7X5A.js +18 -0
  28. package/dist/chunk-6LJY7X5A.js.map +1 -0
  29. package/dist/{chunk-JVBO4SZL.js → chunk-6UBZ6I5Z.js} +2 -2
  30. package/dist/{chunk-BOKIKJJB.js → chunk-6ZNSB3H6.js} +5 -17
  31. package/dist/chunk-6ZNSB3H6.js.map +1 -0
  32. package/dist/{chunk-IK43HLDQ.js → chunk-7YVBQJ7K.js} +13 -355
  33. package/dist/chunk-7YVBQJ7K.js.map +1 -0
  34. package/dist/{chunk-VRBTNCM4.js → chunk-ABS7FMMD.js} +6 -6
  35. package/dist/chunk-AFHQATY4.js +47 -0
  36. package/dist/chunk-AFHQATY4.js.map +1 -0
  37. package/dist/{chunk-HOONYZY2.js → chunk-AKKRXT23.js} +7 -7
  38. package/dist/chunk-AKKRXT23.js.map +1 -0
  39. package/dist/{chunk-DDHM2TLX.js → chunk-AT2JJEKJ.js} +4 -4
  40. package/dist/chunk-AT2JJEKJ.js.map +1 -0
  41. package/dist/{chunk-M75ZZHSR.js → chunk-BLBAQRBM.js} +2 -2
  42. package/dist/{chunk-554KLAAN.js → chunk-BLCWSJLN.js} +88 -81
  43. package/dist/chunk-BLCWSJLN.js.map +1 -0
  44. package/dist/chunk-CADUYBQD.js +151 -0
  45. package/dist/chunk-CADUYBQD.js.map +1 -0
  46. package/dist/{chunk-WOM2HTML.js → chunk-CI32CAFS.js} +5 -5
  47. package/dist/{chunk-FH52K7EG.js → chunk-CUZLNGD5.js} +2 -6
  48. package/dist/{chunk-FH52K7EG.js.map → chunk-CUZLNGD5.js.map} +1 -1
  49. package/dist/{chunk-U6JMEH2P.js → chunk-CXQW2NWO.js} +145 -3
  50. package/dist/chunk-CXQW2NWO.js.map +1 -0
  51. package/dist/{chunk-EO43ANYJ.js → chunk-DABACXEF.js} +3 -3
  52. package/dist/{chunk-CJ5KKU6X.js → chunk-DMH3VZU5.js} +4 -4
  53. package/dist/chunk-DSDXAKIP.js +50 -0
  54. package/dist/chunk-DSDXAKIP.js.map +1 -0
  55. package/dist/{chunk-WZSHL6DQ.js → chunk-DZ2JXHWK.js} +5 -5
  56. package/dist/chunk-DZFQVICS.js +23 -0
  57. package/dist/chunk-DZFQVICS.js.map +1 -0
  58. package/dist/chunk-E23UGFVC.js +63 -0
  59. package/dist/chunk-E23UGFVC.js.map +1 -0
  60. package/dist/{chunk-Z4PD65VQ.js → chunk-EE7UHLKH.js} +4 -4
  61. package/dist/chunk-EIPVYUEP.js +654 -0
  62. package/dist/chunk-EIPVYUEP.js.map +1 -0
  63. package/dist/{chunk-RNG4VRP4.js → chunk-EKRSCRNM.js} +3 -3
  64. package/dist/{chunk-YMUGBZN2.js → chunk-FQ5CHJR7.js} +1 -1
  65. package/dist/chunk-FQ5CHJR7.js.map +1 -0
  66. package/dist/{chunk-RFI7GGFO.js → chunk-FQZ6XTQ7.js} +17 -20
  67. package/dist/chunk-FQZ6XTQ7.js.map +1 -0
  68. package/dist/{chunk-MT62YSPZ.js → chunk-FRMPM7WG.js} +4 -4
  69. package/dist/{chunk-W4TCUGQR.js → chunk-G2JUIDFA.js} +13 -9
  70. package/dist/{chunk-W4TCUGQR.js.map → chunk-G2JUIDFA.js.map} +1 -1
  71. package/dist/{chunk-T3GY65MA.js → chunk-G6WWZNJP.js} +13 -13
  72. package/dist/chunk-G6WWZNJP.js.map +1 -0
  73. package/dist/chunk-GJW4HX2O.js +1 -0
  74. package/dist/{chunk-KS6SAVCZ.js → chunk-GU3IP74F.js} +6 -6
  75. package/dist/{chunk-UHNGNKLV.js → chunk-GWWOGNBK.js} +308 -18
  76. package/dist/chunk-GWWOGNBK.js.map +1 -0
  77. package/dist/{chunk-5PFOMHTB.js → chunk-H2GPHUZD.js} +12 -13
  78. package/dist/chunk-H2GPHUZD.js.map +1 -0
  79. package/dist/{chunk-H2KSZMIX.js → chunk-HMU5P6RL.js} +5 -5
  80. package/dist/chunk-HMU5P6RL.js.map +1 -0
  81. package/dist/{chunk-JFCV5QIK.js → chunk-I5GEJX3R.js} +5 -5
  82. package/dist/chunk-I5GEJX3R.js.map +1 -0
  83. package/dist/{chunk-GWKAVKHI.js → chunk-IG6KUWBX.js} +2 -2
  84. package/dist/chunk-IG6KUWBX.js.map +1 -0
  85. package/dist/{chunk-LVGTA624.js → chunk-IPUG56GJ.js} +15 -5
  86. package/dist/{chunk-LVGTA624.js.map → chunk-IPUG56GJ.js.map} +1 -1
  87. package/dist/{chunk-SMIRGMS7.js → chunk-IXNCZTWA.js} +2 -2
  88. package/dist/{chunk-WQIYTNJV.js → chunk-IXTSNGYY.js} +3 -3
  89. package/dist/{chunk-WHXXI3W5.js → chunk-IYZISPGD.js} +7 -5
  90. package/dist/chunk-IYZISPGD.js.map +1 -0
  91. package/dist/{chunk-HQBM64EE.js → chunk-JDOMW53O.js} +145 -16
  92. package/dist/chunk-JDOMW53O.js.map +1 -0
  93. package/dist/{chunk-NCCBLYEB.js → chunk-JJZGIE4U.js} +6 -5
  94. package/dist/{chunk-DRWBEDYO.js → chunk-JYJFGD2H.js} +2 -2
  95. package/dist/{chunk-TAGR72IR.js → chunk-KRCBZYTZ.js} +26 -177
  96. package/dist/chunk-KRCBZYTZ.js.map +1 -0
  97. package/dist/{chunk-DSBQODMZ.js → chunk-L2DGWBCT.js} +14 -14
  98. package/dist/chunk-L2DGWBCT.js.map +1 -0
  99. package/dist/{chunk-VEALDPHW.js → chunk-L3YYLT4E.js} +15 -2
  100. package/dist/chunk-L3YYLT4E.js.map +1 -0
  101. package/dist/{chunk-FXZDAV62.js → chunk-LNPUSMZL.js} +29 -31
  102. package/dist/chunk-LNPUSMZL.js.map +1 -0
  103. package/dist/{chunk-XJMBYYQK.js → chunk-LNZKOUJR.js} +2 -2
  104. package/dist/{chunk-R3BLCFD2.js → chunk-MBKVNOJS.js} +6 -6
  105. package/dist/chunk-MCHBCNCE.js +367 -0
  106. package/dist/chunk-MCHBCNCE.js.map +1 -0
  107. package/dist/{chunk-VCBHAE4R.js → chunk-MHMLXJ3I.js} +2 -2
  108. package/dist/chunk-MHMLXJ3I.js.map +1 -0
  109. package/dist/{chunk-WNGGJWF7.js → chunk-MSMJJB4J.js} +2 -2
  110. package/dist/chunk-MSMJJB4J.js.map +1 -0
  111. package/dist/{chunk-T36RFODW.js → chunk-MXUT7FTE.js} +2 -2
  112. package/dist/chunk-MXUT7FTE.js.map +1 -0
  113. package/dist/{chunk-WG4QBZDN.js → chunk-N4BBNYIB.js} +11 -9
  114. package/dist/chunk-N4BBNYIB.js.map +1 -0
  115. package/dist/{chunk-PK6JIDQ4.js → chunk-NFIBXF4V.js} +1084 -1583
  116. package/dist/chunk-NFIBXF4V.js.map +1 -0
  117. package/dist/{chunk-VKCADNMR.js → chunk-NFNJLRY7.js} +368 -52
  118. package/dist/chunk-NFNJLRY7.js.map +1 -0
  119. package/dist/{chunk-N6WF5PKC.js → chunk-NFZGZ4AK.js} +2 -2
  120. package/dist/{chunk-JRBNCUNT.js → chunk-NGSJZFGL.js} +21 -4
  121. package/dist/chunk-NGSJZFGL.js.map +1 -0
  122. package/dist/chunk-NJ4DYYO5.js +81 -0
  123. package/dist/chunk-NJ4DYYO5.js.map +1 -0
  124. package/dist/chunk-NM5UYF6Q.js +24 -0
  125. package/dist/chunk-NM5UYF6Q.js.map +1 -0
  126. package/dist/{chunk-JBBSYJHP.js → chunk-NYEFP7DY.js} +4 -4
  127. package/dist/chunk-NYEFP7DY.js.map +1 -0
  128. package/dist/{chunk-GDG5VVYG.js → chunk-NZ24MX4J.js} +8 -46
  129. package/dist/chunk-NZ24MX4J.js.map +1 -0
  130. package/dist/{chunk-IFJTPAQQ.js → chunk-OLEPKFTW.js} +4 -4
  131. package/dist/chunk-OLEPKFTW.js.map +1 -0
  132. package/dist/{chunk-THVIPWRF.js → chunk-PEATRIYJ.js} +9 -9
  133. package/dist/chunk-PEOZ34EM.js +13 -0
  134. package/dist/chunk-PEOZ34EM.js.map +1 -0
  135. package/dist/{chunk-UC2ETWF3.js → chunk-PTHKZWEH.js} +2 -14
  136. package/dist/{chunk-UC2ETWF3.js.map → chunk-PTHKZWEH.js.map} +1 -1
  137. package/dist/chunk-PVEWA2DU.js +23 -0
  138. package/dist/chunk-PVEWA2DU.js.map +1 -0
  139. package/dist/{chunk-VCTXU4AT.js → chunk-QBAC3GXA.js} +1 -1
  140. package/dist/chunk-QBAC3GXA.js.map +1 -0
  141. package/dist/{chunk-RDXW3OBQ.js → chunk-QD46HPZG.js} +2 -2
  142. package/dist/chunk-QD46HPZG.js.map +1 -0
  143. package/dist/{chunk-YZQOGE2L.js → chunk-QDIH2744.js} +2 -2
  144. package/dist/{chunk-6HY2X62D.js → chunk-QJNCB5SK.js} +2 -2
  145. package/dist/{chunk-IAO54J4R.js → chunk-QX76XVQH.js} +6 -6
  146. package/dist/chunk-QX76XVQH.js.map +1 -0
  147. package/dist/{chunk-W2OG6JOL.js → chunk-QZWTRENR.js} +24 -7
  148. package/dist/chunk-QZWTRENR.js.map +1 -0
  149. package/dist/{chunk-6NQMXTYC.js → chunk-RO5NM6UI.js} +2 -2
  150. package/dist/chunk-RO5NM6UI.js.map +1 -0
  151. package/dist/chunk-SGDPRCC4.js +1075 -0
  152. package/dist/chunk-SGDPRCC4.js.map +1 -0
  153. package/dist/{chunk-2PCAVW2I.js → chunk-SHEEBRZ4.js} +2 -2
  154. package/dist/chunk-SMY5X46Z.js +14 -0
  155. package/dist/chunk-SMY5X46Z.js.map +1 -0
  156. package/dist/{chunk-ISMV2QVG.js → chunk-TG634KCO.js} +11 -2
  157. package/dist/chunk-TG634KCO.js.map +1 -0
  158. package/dist/{chunk-XUW3PXHU.js → chunk-TUXQSFHZ.js} +4 -16
  159. package/dist/{chunk-XUW3PXHU.js.map → chunk-TUXQSFHZ.js.map} +1 -1
  160. package/dist/{chunk-GULTLPFB.js → chunk-UADPR6F4.js} +3 -3
  161. package/dist/{chunk-GULTLPFB.js.map → chunk-UADPR6F4.js.map} +1 -1
  162. package/dist/{chunk-XTHCN77N.js → chunk-UDHBXYGG.js} +29 -19
  163. package/dist/chunk-UDHBXYGG.js.map +1 -0
  164. package/dist/{chunk-RA5VTXXG.js → chunk-V6UQZA2H.js} +2 -2
  165. package/dist/chunk-VG2ZSFYD.js +32 -0
  166. package/dist/chunk-VG2ZSFYD.js.map +1 -0
  167. package/dist/chunk-VKQCVU2Z.js +21 -0
  168. package/dist/chunk-VKQCVU2Z.js.map +1 -0
  169. package/dist/{chunk-QDPZWTN2.js → chunk-W3G5REIU.js} +2 -2
  170. package/dist/{chunk-A4FXFAKF.js → chunk-W3L64Y3J.js} +2 -2
  171. package/dist/{chunk-ONTBCHCP.js → chunk-W66AM5IB.js} +2 -2
  172. package/dist/chunk-W66AM5IB.js.map +1 -0
  173. package/dist/{chunk-A5BDDEXK.js → chunk-WBKZUO5V.js} +26 -43
  174. package/dist/chunk-WBKZUO5V.js.map +1 -0
  175. package/dist/{chunk-XDIAFPUM.js → chunk-WLXMTYGO.js} +2 -2
  176. package/dist/{chunk-RJ6K6K5M.js → chunk-X3F5YJG2.js} +2 -2
  177. package/dist/{chunk-WKGF2TJR.js → chunk-X45JYIAY.js} +3 -3
  178. package/dist/chunk-X45JYIAY.js.map +1 -0
  179. package/dist/{chunk-6XE5TMPR.js → chunk-X4MVWPHV.js} +3 -3
  180. package/dist/chunk-X4MVWPHV.js.map +1 -0
  181. package/dist/chunk-XZHEOSQ2.js +16 -0
  182. package/dist/chunk-XZHEOSQ2.js.map +1 -0
  183. package/dist/chunk-Y2NBBT6K.js +18 -0
  184. package/dist/chunk-Y2NBBT6K.js.map +1 -0
  185. package/dist/{chunk-CZTLE22N.js → chunk-Y2NJBNX3.js} +8 -8
  186. package/dist/{chunk-CZTLE22N.js.map → chunk-Y2NJBNX3.js.map} +1 -1
  187. package/dist/{chunk-YTGDPCPW.js → chunk-Y6B6HVKC.js} +2 -2
  188. package/dist/chunk-YMUFFJCO.js +23 -0
  189. package/dist/chunk-YMUFFJCO.js.map +1 -0
  190. package/dist/{chunk-SV6EAJEP.js → chunk-YWWQM7KP.js} +5 -14
  191. package/dist/chunk-YWWQM7KP.js.map +1 -0
  192. package/dist/{chunk-UDSR4X4L.js → chunk-YZJSJOUI.js} +239 -16
  193. package/dist/chunk-YZJSJOUI.js.map +1 -0
  194. package/dist/{chunk-5WZXVULX.js → chunk-ZTLOXFPB.js} +2 -2
  195. package/dist/chunk-ZTLOXFPB.js.map +1 -0
  196. package/dist/classified/index.js +5 -5
  197. package/dist/{classified-marker-QWMASIT7.js → classified-marker-7PKJBFCG.js} +3 -3
  198. package/dist/collection-facade-6I5K5IOD.js +45 -0
  199. package/dist/{computed-DSMPXG2Y.js → computed-AB45PS4V.js} +3 -3
  200. package/dist/consent/index.js +13 -9
  201. package/dist/consent/index.js.map +1 -1
  202. package/dist/cover/index.js +37 -0
  203. package/dist/crdt/index.js +4 -0
  204. package/dist/crdt/index.js.map +1 -1
  205. package/dist/custody/index.js +26 -0
  206. package/dist/{dead-filter-66VGI6JV.js → dead-filter-FL3WIUZS.js} +2 -2
  207. package/dist/{chunk-OCVM6KDH.js → delegation-QXZ4MPPH.js} +12 -6
  208. package/dist/{chunk-OCVM6KDH.js.map → delegation-QXZ4MPPH.js.map} +1 -1
  209. package/dist/derivations/index.js +12 -12
  210. package/dist/derive-VGS7FKE3.js +22 -0
  211. package/dist/directory/index.js +46 -0
  212. package/dist/dispatch-EGGC7UD7.js +72 -0
  213. package/dist/dispatch-EGGC7UD7.js.map +1 -0
  214. package/dist/dispatch-GDSTUTVP.js +194 -0
  215. package/dist/dispatch-GDSTUTVP.js.map +1 -0
  216. package/dist/{enclave-TKPN2G2U.js → enclave-SPEEDYZ4.js} +13 -11
  217. package/dist/executor-IEBLTBCW.js +9 -0
  218. package/dist/executor-OTF556PL.js +28 -0
  219. package/dist/executor-RM7XWQDU.js +9 -0
  220. package/dist/export-accessible-P42BQ4PB.js +23 -0
  221. package/dist/extract-partition-TCYXRWBN.js +38 -0
  222. package/dist/{fanout-sidecar-B5LVUMQX.js → fanout-sidecar-SSMRSC2L.js} +9 -9
  223. package/dist/find-OZMTRIK3.js +11 -0
  224. package/dist/forget/index.js +11 -11
  225. package/dist/forget/index.js.map +1 -1
  226. package/dist/guards/index.js +4 -4
  227. package/dist/history/index.js +16 -12
  228. package/dist/history/index.js.map +1 -1
  229. package/dist/i18n/index.js +16 -14
  230. package/dist/i18n/index.js.map +1 -1
  231. package/dist/index.d.ts +42 -42
  232. package/dist/index.js +332 -2106
  233. package/dist/index.js.map +1 -1
  234. package/dist/indexing/index.js +4 -2
  235. package/dist/indexing/index.js.map +1 -1
  236. package/dist/introspection/index.js +28 -0
  237. package/dist/issue-FCKN5O6C.js +19 -0
  238. package/dist/kernel/best-effort-revert.d.ts +9 -1
  239. package/dist/kernel/collection-config.d.ts +12 -70
  240. package/dist/kernel/collection.d.ts +11 -23
  241. package/dist/kernel/enclave/crypto.d.ts +8 -8
  242. package/dist/kernel/enclave/index.d.ts +4 -4
  243. package/dist/kernel/enclave/record-keys/envelope-body.d.ts +8 -0
  244. package/dist/kernel/errors.d.ts +41 -22
  245. package/dist/kernel/lazy.d.ts +30 -0
  246. package/dist/kernel/noydb.d.ts +81 -124
  247. package/dist/kernel/query/builder.d.ts +10 -10
  248. package/dist/kernel/query/index.d.ts +0 -6
  249. package/dist/kernel/query/scan-builder.d.ts +4 -4
  250. package/dist/kernel/sync-policy.d.ts +76 -4
  251. package/dist/kernel/types.d.ts +187 -86
  252. package/dist/kernel/validation.d.ts +33 -33
  253. package/dist/kernel/vault.d.ts +41 -229
  254. package/dist/kernel/via/dispatch.d.ts +7 -0
  255. package/dist/{ledger-PTK7XZYT.js → ledger-6XBEVWYP.js} +11 -11
  256. package/dist/liberate-CYUOS4NC.js +24 -0
  257. package/dist/{link-set-SLJHDNRB.js → link-set-NK6L2A5S.js} +11 -11
  258. package/dist/materialized-views/index.js +22 -19
  259. package/dist/money/index.js +31 -0
  260. package/dist/noydb-FZZIMLCI.js +81 -0
  261. package/dist/overlay-views/index.js +4 -4
  262. package/dist/periods/index.js +21 -17
  263. package/dist/pod/index.js +26 -12
  264. package/dist/{policy-YDLCOX4P.js → policy/index.js} +6 -6
  265. package/dist/port/with/archive-strategy.d.ts +31 -0
  266. package/dist/port/with/blob-strategy.d.ts +15 -5
  267. package/dist/port/with/collection-options.d.ts +171 -0
  268. package/dist/port/with/collection-registries.d.ts +62 -0
  269. package/dist/port/with/strategies.d.ts +134 -0
  270. package/dist/port/with/team-strategy.d.ts +3 -2
  271. package/dist/portability/index.js +8 -8
  272. package/dist/{post-register-2YHSADFI.js → post-register-VFIYISUK.js} +6 -6
  273. package/dist/query/index.js +6 -46
  274. package/dist/{aggregate → reduce}/index.js +24 -17
  275. package/dist/reduce/index.js.map +1 -0
  276. package/dist/register-UKU2J6MN.js +25 -0
  277. package/dist/registry-2VCWVHZS.js +19 -0
  278. package/dist/registry-7Y5WF7GS.js +9 -0
  279. package/dist/{registry-VW6P6A3J.js → registry-BWD2ED52.js} +2 -2
  280. package/dist/registry-DTWRJLSF.js +18 -0
  281. package/dist/request-withdrawal-JYDZ5XY2.js +31 -0
  282. package/dist/{reveal-HWO4YMTJ.js → reveal-CL2BCW6A.js} +6 -6
  283. package/dist/revoke-2TSAD2T4.js +24 -0
  284. package/dist/satellites/index.js +1 -1
  285. package/dist/schema-update/index.js +26 -0
  286. package/dist/sealed-record/index.js +12 -12
  287. package/dist/search/index.js +36 -0
  288. package/dist/{seed-3ZMF5EQA.js → seed-NUVJEL7T.js} +12 -12
  289. package/dist/seed-NUVJEL7T.js.map +1 -0
  290. package/dist/sequence/index.js +30 -0
  291. package/dist/session/index.js +13 -9
  292. package/dist/session/index.js.map +1 -1
  293. package/dist/shadow/index.js +6 -2
  294. package/dist/shadow/index.js.map +1 -1
  295. package/dist/share-link/index.d.ts +19 -0
  296. package/dist/share-link/index.js +201 -0
  297. package/dist/share-link/index.js.map +1 -0
  298. package/dist/share-link/share-link.d.ts +123 -0
  299. package/dist/signer-K7Z2EOMG.js +25 -0
  300. package/dist/snapshots/index.js +14 -10
  301. package/dist/snapshots/index.js.map +1 -1
  302. package/dist/{stale-VZL4OGUB.js → stale-A4J26OIC.js} +11 -10
  303. package/dist/storage-YVHZUISN.js +23 -0
  304. package/dist/store/index.js +22 -0
  305. package/dist/{store-coordination-provider-FKX3F6TQ.js → store-coordination-provider-EWRZL7XF.js} +3 -3
  306. package/dist/sync/index.js +15 -11
  307. package/dist/sync/index.js.map +1 -1
  308. package/dist/team/index.js +33 -47
  309. package/dist/tiers/index.js +10 -10
  310. package/dist/to/index.js +1 -1
  311. package/dist/{tx → transactions}/index.js +7 -3
  312. package/dist/transactions/index.js.map +1 -0
  313. package/dist/util/index.js +1 -1
  314. package/dist/{verify-26Y5GNMK.js → verify-7XWHJWUN.js} +5 -5
  315. package/dist/via/blob/active.d.ts +23 -7
  316. package/dist/via/blob/binding.d.ts +1 -1
  317. package/dist/via/blob/index.d.ts +7 -1
  318. package/dist/via/i18n/core.d.ts +1 -1
  319. package/dist/via/i18n/index.d.ts +2 -0
  320. package/dist/via/money/money-reducer.d.ts +2 -2
  321. package/dist/walk-P5D6CF4V.js +21 -0
  322. package/dist/with-audit/consent/index.d.ts +2 -0
  323. package/dist/with-audit/forget/active.d.ts +3 -3
  324. package/dist/with-audit/forget/index.d.ts +6 -5
  325. package/dist/with-audit/forget/strategy.d.ts +3 -3
  326. package/dist/with-audit/guards/executor.d.ts +4 -4
  327. package/dist/with-audit/guards/immutable-guard.d.ts +3 -3
  328. package/dist/with-audit/guards/index.d.ts +1 -1
  329. package/dist/with-audit/guards/registry.d.ts +3 -3
  330. package/dist/with-audit/guards/transition-guard.d.ts +3 -3
  331. package/dist/with-audit/guards/types.d.ts +10 -10
  332. package/dist/with-audit/guards/with-guard.d.ts +2 -2
  333. package/dist/with-audit/periods/index.d.ts +2 -0
  334. package/dist/with-audit/periods/periods.d.ts +1 -16
  335. package/dist/with-audit/periods/vault-facade.d.ts +6 -0
  336. package/dist/with-audit/periods/window.d.ts +16 -0
  337. package/dist/with-audit/portability/export-accessible.d.ts +4 -4
  338. package/dist/with-audit/portability/request-withdrawal.d.ts +3 -3
  339. package/dist/with-audit/portability/withdraw-accessible.d.ts +2 -2
  340. package/dist/with-audit/sealed-record/strategy.d.ts +1 -1
  341. package/dist/with-audit/tiers/index.d.ts +1 -0
  342. package/dist/with-cargo/adopt-partition.d.ts +7 -7
  343. package/dist/{legacy/kernel.d.ts → with-cargo/floor.d.ts} +4 -4
  344. package/dist/with-cargo/index.d.ts +15 -2
  345. package/dist/with-commit/crdt/index.d.ts +2 -0
  346. package/dist/with-commit/history/index.d.ts +2 -0
  347. package/dist/with-commit/history/time-machine.d.ts +2 -2
  348. package/dist/with-commit/numbering/descriptor.d.ts +16 -6
  349. package/dist/with-commit/tx/active.d.ts +5 -5
  350. package/dist/with-commit/tx/index.d.ts +2 -2
  351. package/dist/with-commit/tx/invariants.d.ts +1 -1
  352. package/dist/with-commit/tx/strategy.d.ts +3 -3
  353. package/dist/with-fork/shadow/index.d.ts +2 -0
  354. package/dist/with-fork/snapshots/active.d.ts +2 -2
  355. package/dist/with-fork/snapshots/index.d.ts +3 -1
  356. package/dist/with-fork/snapshots/noydb-facade.d.ts +3 -3
  357. package/dist/with-fork/snapshots/strategy.d.ts +2 -2
  358. package/dist/with-formula/derivations/dispatch.d.ts +98 -0
  359. package/dist/with-formula/derivations/executor.d.ts +2 -2
  360. package/dist/with-formula/derivations/index.d.ts +4 -1
  361. package/dist/with-formula/derivations/registry.d.ts +3 -3
  362. package/dist/with-formula/derivations/stale.d.ts +2 -2
  363. package/dist/with-formula/derivations/types.d.ts +3 -3
  364. package/dist/with-formula/derivations/with-derivation.d.ts +2 -2
  365. package/dist/with-formula/derivations/with-rollup.d.ts +15 -8
  366. package/dist/with-formula/materialized-views/dependency-analyzer.d.ts +3 -3
  367. package/dist/with-formula/materialized-views/dispatch.d.ts +75 -0
  368. package/dist/with-formula/materialized-views/index.d.ts +2 -1
  369. package/dist/with-formula/materialized-views/registry.d.ts +4 -4
  370. package/dist/with-formula/materialized-views/types.d.ts +11 -11
  371. package/dist/with-formula/materialized-views/with-materialized-view.d.ts +2 -2
  372. package/dist/with-formula/overlay-views/index.d.ts +1 -1
  373. package/dist/with-formula/overlay-views/registry.d.ts +4 -4
  374. package/dist/with-formula/overlay-views/types.d.ts +3 -3
  375. package/dist/with-formula/overlay-views/virtual-collection.d.ts +2 -2
  376. package/dist/with-formula/overlay-views/with-overlayed-view.d.ts +2 -2
  377. package/dist/with-lookup/indexing/active.d.ts +5 -5
  378. package/dist/with-lookup/indexing/index.d.ts +3 -1
  379. package/dist/with-lookup/indexing/strategy.d.ts +3 -3
  380. package/dist/with-lookup/{aggregate → reduce}/active.d.ts +8 -8
  381. package/dist/with-lookup/{aggregate → reduce}/groupby.d.ts +21 -21
  382. package/dist/with-lookup/reduce/index.d.ts +34 -0
  383. package/dist/with-lookup/{aggregate → reduce}/reducers.d.ts +11 -12
  384. package/dist/with-lookup/{aggregate/aggregation.d.ts → reduce/reduction.d.ts} +21 -21
  385. package/dist/with-lookup/{aggregate → reduce}/strategy.d.ts +15 -15
  386. package/dist/with-lookup/search/build-docs.d.ts +7 -2
  387. package/dist/with-lookup/search/index.d.ts +1 -1
  388. package/dist/with-party/broker/index.d.ts +1 -0
  389. package/dist/with-party/broker/seed.d.ts +1 -1
  390. package/dist/with-party/custody/liberate.d.ts +3 -3
  391. package/dist/with-party/directory/cover/index.d.ts +1 -5
  392. package/dist/with-party/directory/cover/schema.d.ts +0 -6
  393. package/dist/with-party/directory/cover/storage.d.ts +0 -8
  394. package/dist/with-party/directory/cover/types.d.ts +0 -21
  395. package/dist/with-party/directory/index.d.ts +2 -0
  396. package/dist/with-party/session/dev-unlock.d.ts +2 -2
  397. package/dist/with-party/session/index.d.ts +2 -0
  398. package/dist/with-party/session/session.d.ts +2 -2
  399. package/dist/with-party/team/deed.d.ts +8 -8
  400. package/dist/with-party/team/index.d.ts +4 -7
  401. package/dist/with-party/team/keyring.d.ts +83 -35
  402. package/dist/with-party/team/{managed-passphrase.d.ts → managed-secret.d.ts} +27 -27
  403. package/dist/with-party/team/noydb-facade.d.ts +43 -42
  404. package/dist/with-party/team/peer-recover.d.ts +13 -13
  405. package/dist/with-party/team/recovery.d.ts +5 -5
  406. package/dist/with-party/team/rotate-recover.d.ts +30 -30
  407. package/dist/with-pod/bundle.d.ts +30 -32
  408. package/dist/with-pod/format.d.ts +2 -2
  409. package/dist/with-pod/index.d.ts +2 -1
  410. package/dist/with-shape/blobs/blob-compaction.d.ts +43 -3
  411. package/dist/with-shape/blobs/blob-pinning.d.ts +112 -0
  412. package/dist/with-shape/blobs/blob-set.d.ts +86 -2
  413. package/dist/with-shape/introspection/index.d.ts +6 -0
  414. package/dist/with-shape/schema-update/index.d.ts +1 -0
  415. package/dist/with-store/index.d.ts +3 -2
  416. package/dist/with-store/route-store.d.ts +1 -1
  417. package/dist/{with-party/team/sync-active.d.ts → with-sync/active.d.ts} +1 -1
  418. package/dist/{with-party/team/sync-credentials.d.ts → with-sync/credentials.d.ts} +3 -3
  419. package/dist/{with-party/team/sync.d.ts → with-sync/engine.d.ts} +17 -4
  420. package/dist/{with-party/sync → with-sync}/index.d.ts +4 -2
  421. package/dist/with-sync/period-scope.d.ts +78 -0
  422. package/dist/{with-party/team → with-sync}/presence.d.ts +2 -2
  423. package/dist/{with-party/team/sync-strategy.d.ts → with-sync/strategy.d.ts} +6 -6
  424. package/dist/{with-party/team/sync-transaction.d.ts → with-sync/transaction.d.ts} +3 -3
  425. package/dist/withdraw-accessible-A2PWF5MZ.js +28 -0
  426. package/dist/withdraw-accessible-A2PWF5MZ.js.map +1 -0
  427. package/package.json +49 -36
  428. package/dist/aggregate/index.js.map +0 -1
  429. package/dist/api-3A3S4SHH.js +0 -20
  430. package/dist/as/index.js +0 -30
  431. package/dist/at/index.js +0 -1
  432. package/dist/bundle/index.js +0 -153
  433. package/dist/bundle/index.js.map +0 -1
  434. package/dist/by/index.js +0 -11
  435. package/dist/chunk-554KLAAN.js.map +0 -1
  436. package/dist/chunk-5PFOMHTB.js.map +0 -1
  437. package/dist/chunk-5WZXVULX.js.map +0 -1
  438. package/dist/chunk-6NQMXTYC.js.map +0 -1
  439. package/dist/chunk-6XE5TMPR.js.map +0 -1
  440. package/dist/chunk-A5BDDEXK.js.map +0 -1
  441. package/dist/chunk-ANATMTOA.js +0 -205
  442. package/dist/chunk-ANATMTOA.js.map +0 -1
  443. package/dist/chunk-BOKIKJJB.js.map +0 -1
  444. package/dist/chunk-CHFZDSE4.js +0 -1
  445. package/dist/chunk-DDHM2TLX.js.map +0 -1
  446. package/dist/chunk-DSBQODMZ.js.map +0 -1
  447. package/dist/chunk-EWPOHMN2.js +0 -255
  448. package/dist/chunk-EWPOHMN2.js.map +0 -1
  449. package/dist/chunk-FXZDAV62.js.map +0 -1
  450. package/dist/chunk-GDG5VVYG.js.map +0 -1
  451. package/dist/chunk-GWKAVKHI.js.map +0 -1
  452. package/dist/chunk-H2KSZMIX.js.map +0 -1
  453. package/dist/chunk-HOONYZY2.js.map +0 -1
  454. package/dist/chunk-HQBM64EE.js.map +0 -1
  455. package/dist/chunk-IAO54J4R.js.map +0 -1
  456. package/dist/chunk-IFJTPAQQ.js.map +0 -1
  457. package/dist/chunk-IK43HLDQ.js.map +0 -1
  458. package/dist/chunk-ISMV2QVG.js.map +0 -1
  459. package/dist/chunk-JBBSYJHP.js.map +0 -1
  460. package/dist/chunk-JFCV5QIK.js.map +0 -1
  461. package/dist/chunk-JRBNCUNT.js.map +0 -1
  462. package/dist/chunk-LB7FD65R.js +0 -163
  463. package/dist/chunk-LB7FD65R.js.map +0 -1
  464. package/dist/chunk-ONTBCHCP.js.map +0 -1
  465. package/dist/chunk-PK6JIDQ4.js.map +0 -1
  466. package/dist/chunk-RDXW3OBQ.js.map +0 -1
  467. package/dist/chunk-RFI7GGFO.js.map +0 -1
  468. package/dist/chunk-S3BFKGET.js.map +0 -1
  469. package/dist/chunk-SV6EAJEP.js.map +0 -1
  470. package/dist/chunk-T36RFODW.js.map +0 -1
  471. package/dist/chunk-T3GY65MA.js.map +0 -1
  472. package/dist/chunk-TAGR72IR.js.map +0 -1
  473. package/dist/chunk-TIGPQDYP.js +0 -14
  474. package/dist/chunk-TIGPQDYP.js.map +0 -1
  475. package/dist/chunk-U6JMEH2P.js.map +0 -1
  476. package/dist/chunk-UDSR4X4L.js.map +0 -1
  477. package/dist/chunk-UHNGNKLV.js.map +0 -1
  478. package/dist/chunk-VCBHAE4R.js.map +0 -1
  479. package/dist/chunk-VCTXU4AT.js.map +0 -1
  480. package/dist/chunk-VEALDPHW.js.map +0 -1
  481. package/dist/chunk-VKCADNMR.js.map +0 -1
  482. package/dist/chunk-VQNJ7UZL.js.map +0 -1
  483. package/dist/chunk-W2OG6JOL.js.map +0 -1
  484. package/dist/chunk-WG4QBZDN.js.map +0 -1
  485. package/dist/chunk-WHXXI3W5.js.map +0 -1
  486. package/dist/chunk-WKGF2TJR.js.map +0 -1
  487. package/dist/chunk-WNGGJWF7.js.map +0 -1
  488. package/dist/chunk-XTHCN77N.js.map +0 -1
  489. package/dist/chunk-YCH5GBN6.js +0 -99
  490. package/dist/chunk-YCH5GBN6.js.map +0 -1
  491. package/dist/chunk-YMUGBZN2.js.map +0 -1
  492. package/dist/collection-facade-KMBWNCYP.js +0 -44
  493. package/dist/cover-QJRXINN5.js +0 -52
  494. package/dist/delegation-SU6CV3W7.js +0 -25
  495. package/dist/derive-6RWU4EOA.js +0 -21
  496. package/dist/executor-KGNLJD6M.js +0 -9
  497. package/dist/executor-UX4FASYZ.js +0 -26
  498. package/dist/executor-XGIS6CR7.js +0 -9
  499. package/dist/export-accessible-LKW3QV22.js +0 -23
  500. package/dist/extract-partition-UP2ARO75.js +0 -38
  501. package/dist/find-YS6XWB46.js +0 -11
  502. package/dist/in/index.js +0 -1
  503. package/dist/issue-3ACYX7LO.js +0 -19
  504. package/dist/legacy/bundle.d.ts +0 -30
  505. package/dist/liberate-MQQXBUCY.js +0 -24
  506. package/dist/noydb-ANIW5FLE.js +0 -69
  507. package/dist/on/index.js +0 -17
  508. package/dist/port/as/index.d.ts +0 -26
  509. package/dist/port/at/index.d.ts +0 -18
  510. package/dist/port/in/index.d.ts +0 -21
  511. package/dist/port/on/index.d.ts +0 -27
  512. package/dist/port/ui/index.d.ts +0 -21
  513. package/dist/port/with/index.d.ts +0 -18
  514. package/dist/register-PAM3ASRZ.js +0 -23
  515. package/dist/registry-4S2U2TR4.js +0 -19
  516. package/dist/registry-NPOWJHC4.js +0 -9
  517. package/dist/registry-VZ3CCI2R.js +0 -18
  518. package/dist/request-withdrawal-DI3H5WUI.js +0 -31
  519. package/dist/revoke-R5CZSVUT.js +0 -24
  520. package/dist/seed-3ZMF5EQA.js.map +0 -1
  521. package/dist/signer-MBVHW4WE.js +0 -25
  522. package/dist/storage-3MEJUENZ.js +0 -23
  523. package/dist/tx/index.js.map +0 -1
  524. package/dist/ui/index.js +0 -1
  525. package/dist/with/index.js +0 -31
  526. package/dist/with-lookup/aggregate/index.d.ts +0 -23
  527. package/dist/withdraw-accessible-TZDATQVR.js +0 -28
  528. /package/dist/{api-3A3S4SHH.js.map → api-ZDKTWNBB.js.map} +0 -0
  529. /package/dist/{backup-YPUEKIJC.js.map → backup-2UX7PRM5.js.map} +0 -0
  530. /package/dist/{chunk-IY23KFNZ.js.map → chunk-35NMVJ7I.js.map} +0 -0
  531. /package/dist/{chunk-RTEK4C4I.js.map → chunk-4JQK3L4V.js.map} +0 -0
  532. /package/dist/{chunk-3ELCWU56.js.map → chunk-4ZKNC6A6.js.map} +0 -0
  533. /package/dist/{chunk-VAIS3SOM.js.map → chunk-55LX6FSX.js.map} +0 -0
  534. /package/dist/{chunk-6HPG2YNX.js.map → chunk-5DRROK5W.js.map} +0 -0
  535. /package/dist/{chunk-S6Q37VGE.js.map → chunk-5GQEU4DN.js.map} +0 -0
  536. /package/dist/{chunk-ZXEZCY6F.js.map → chunk-63FE525P.js.map} +0 -0
  537. /package/dist/{chunk-JVBO4SZL.js.map → chunk-6UBZ6I5Z.js.map} +0 -0
  538. /package/dist/{chunk-VRBTNCM4.js.map → chunk-ABS7FMMD.js.map} +0 -0
  539. /package/dist/{chunk-M75ZZHSR.js.map → chunk-BLBAQRBM.js.map} +0 -0
  540. /package/dist/{chunk-WOM2HTML.js.map → chunk-CI32CAFS.js.map} +0 -0
  541. /package/dist/{chunk-EO43ANYJ.js.map → chunk-DABACXEF.js.map} +0 -0
  542. /package/dist/{chunk-CJ5KKU6X.js.map → chunk-DMH3VZU5.js.map} +0 -0
  543. /package/dist/{chunk-WZSHL6DQ.js.map → chunk-DZ2JXHWK.js.map} +0 -0
  544. /package/dist/{chunk-Z4PD65VQ.js.map → chunk-EE7UHLKH.js.map} +0 -0
  545. /package/dist/{chunk-RNG4VRP4.js.map → chunk-EKRSCRNM.js.map} +0 -0
  546. /package/dist/{chunk-MT62YSPZ.js.map → chunk-FRMPM7WG.js.map} +0 -0
  547. /package/dist/{as/index.js.map → chunk-GJW4HX2O.js.map} +0 -0
  548. /package/dist/{chunk-KS6SAVCZ.js.map → chunk-GU3IP74F.js.map} +0 -0
  549. /package/dist/{chunk-SMIRGMS7.js.map → chunk-IXNCZTWA.js.map} +0 -0
  550. /package/dist/{chunk-WQIYTNJV.js.map → chunk-IXTSNGYY.js.map} +0 -0
  551. /package/dist/{chunk-NCCBLYEB.js.map → chunk-JJZGIE4U.js.map} +0 -0
  552. /package/dist/{chunk-DRWBEDYO.js.map → chunk-JYJFGD2H.js.map} +0 -0
  553. /package/dist/{chunk-XJMBYYQK.js.map → chunk-LNZKOUJR.js.map} +0 -0
  554. /package/dist/{chunk-R3BLCFD2.js.map → chunk-MBKVNOJS.js.map} +0 -0
  555. /package/dist/{chunk-N6WF5PKC.js.map → chunk-NFZGZ4AK.js.map} +0 -0
  556. /package/dist/{chunk-THVIPWRF.js.map → chunk-PEATRIYJ.js.map} +0 -0
  557. /package/dist/{chunk-YZQOGE2L.js.map → chunk-QDIH2744.js.map} +0 -0
  558. /package/dist/{chunk-6HY2X62D.js.map → chunk-QJNCB5SK.js.map} +0 -0
  559. /package/dist/{chunk-2PCAVW2I.js.map → chunk-SHEEBRZ4.js.map} +0 -0
  560. /package/dist/{chunk-RA5VTXXG.js.map → chunk-V6UQZA2H.js.map} +0 -0
  561. /package/dist/{chunk-QDPZWTN2.js.map → chunk-W3G5REIU.js.map} +0 -0
  562. /package/dist/{chunk-A4FXFAKF.js.map → chunk-W3L64Y3J.js.map} +0 -0
  563. /package/dist/{chunk-XDIAFPUM.js.map → chunk-WLXMTYGO.js.map} +0 -0
  564. /package/dist/{chunk-RJ6K6K5M.js.map → chunk-X3F5YJG2.js.map} +0 -0
  565. /package/dist/{chunk-YTGDPCPW.js.map → chunk-Y6B6HVKC.js.map} +0 -0
  566. /package/dist/{classified-marker-QWMASIT7.js.map → classified-marker-7PKJBFCG.js.map} +0 -0
  567. /package/dist/{at/index.js.map → collection-facade-6I5K5IOD.js.map} +0 -0
  568. /package/dist/{by/index.js.map → computed-AB45PS4V.js.map} +0 -0
  569. /package/dist/{in → cover}/index.js.map +0 -0
  570. /package/dist/{on → custody}/index.js.map +0 -0
  571. /package/dist/{dead-filter-66VGI6JV.js.map → dead-filter-FL3WIUZS.js.map} +0 -0
  572. /package/dist/{chunk-CHFZDSE4.js.map → derive-VGS7FKE3.js.map} +0 -0
  573. /package/dist/{ui → directory}/index.js.map +0 -0
  574. /package/dist/{collection-facade-KMBWNCYP.js.map → enclave-SPEEDYZ4.js.map} +0 -0
  575. /package/dist/{computed-DSMPXG2Y.js.map → executor-IEBLTBCW.js.map} +0 -0
  576. /package/dist/{cover-QJRXINN5.js.map → executor-OTF556PL.js.map} +0 -0
  577. /package/dist/{delegation-SU6CV3W7.js.map → executor-RM7XWQDU.js.map} +0 -0
  578. /package/dist/{derive-6RWU4EOA.js.map → export-accessible-P42BQ4PB.js.map} +0 -0
  579. /package/dist/{enclave-TKPN2G2U.js.map → extract-partition-TCYXRWBN.js.map} +0 -0
  580. /package/dist/{fanout-sidecar-B5LVUMQX.js.map → fanout-sidecar-SSMRSC2L.js.map} +0 -0
  581. /package/dist/{executor-KGNLJD6M.js.map → find-OZMTRIK3.js.map} +0 -0
  582. /package/dist/{with → introspection}/index.js.map +0 -0
  583. /package/dist/{executor-UX4FASYZ.js.map → issue-FCKN5O6C.js.map} +0 -0
  584. /package/dist/{executor-XGIS6CR7.js.map → ledger-6XBEVWYP.js.map} +0 -0
  585. /package/dist/{export-accessible-LKW3QV22.js.map → liberate-CYUOS4NC.js.map} +0 -0
  586. /package/dist/{extract-partition-UP2ARO75.js.map → link-set-NK6L2A5S.js.map} +0 -0
  587. /package/dist/{find-YS6XWB46.js.map → money/index.js.map} +0 -0
  588. /package/dist/{issue-3ACYX7LO.js.map → noydb-FZZIMLCI.js.map} +0 -0
  589. /package/dist/{ledger-PTK7XZYT.js.map → policy/index.js.map} +0 -0
  590. /package/dist/{post-register-2YHSADFI.js.map → post-register-VFIYISUK.js.map} +0 -0
  591. /package/dist/{liberate-MQQXBUCY.js.map → register-UKU2J6MN.js.map} +0 -0
  592. /package/dist/{link-set-SLJHDNRB.js.map → registry-2VCWVHZS.js.map} +0 -0
  593. /package/dist/{noydb-ANIW5FLE.js.map → registry-7Y5WF7GS.js.map} +0 -0
  594. /package/dist/{policy-YDLCOX4P.js.map → registry-BWD2ED52.js.map} +0 -0
  595. /package/dist/{register-PAM3ASRZ.js.map → registry-DTWRJLSF.js.map} +0 -0
  596. /package/dist/{registry-4S2U2TR4.js.map → request-withdrawal-JYDZ5XY2.js.map} +0 -0
  597. /package/dist/{reveal-HWO4YMTJ.js.map → reveal-CL2BCW6A.js.map} +0 -0
  598. /package/dist/{registry-NPOWJHC4.js.map → revoke-2TSAD2T4.js.map} +0 -0
  599. /package/dist/{registry-VW6P6A3J.js.map → schema-update/index.js.map} +0 -0
  600. /package/dist/{registry-VZ3CCI2R.js.map → search/index.js.map} +0 -0
  601. /package/dist/{request-withdrawal-DI3H5WUI.js.map → sequence/index.js.map} +0 -0
  602. /package/dist/{revoke-R5CZSVUT.js.map → signer-K7Z2EOMG.js.map} +0 -0
  603. /package/dist/{signer-MBVHW4WE.js.map → stale-A4J26OIC.js.map} +0 -0
  604. /package/dist/{stale-VZL4OGUB.js.map → storage-YVHZUISN.js.map} +0 -0
  605. /package/dist/{storage-3MEJUENZ.js.map → store/index.js.map} +0 -0
  606. /package/dist/{store-coordination-provider-FKX3F6TQ.js.map → store-coordination-provider-EWRZL7XF.js.map} +0 -0
  607. /package/dist/{verify-26Y5GNMK.js.map → verify-7XWHJWUN.js.map} +0 -0
  608. /package/dist/{withdraw-accessible-TZDATQVR.js.map → walk-P5D6CF4V.js.map} +0 -0
  609. /package/dist/with-lookup/{aggregate → reduce}/canonical-key.d.ts +0 -0
  610. /package/dist/{with-party → with-sync}/tab-coordination.d.ts +0 -0
  611. /package/dist/{with-party → with-sync}/tab-write-relay.d.ts +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-party/team/magic-link-grant.ts","../src/port/with/team-strategy.ts","../src/with-party/team/rotate-recover.ts","../src/with-party/team/peer-recover.ts","../src/with-party/team/authenticators.ts"],"sourcesContent":["/**\n * Magic-link-bound cross-user delegation grants.\n *\n * This module is the **core storage + encryption layer** that lets a\n * grantor issue a tier-DEK to a user whose KEK they do not know. The\n * trust bridge is provided by the `@noy-db/on-magic-link` package:\n *\n * 1. Grantor picks a grantee identity (user id + email handle).\n * 2. Grantor mints a magic-link token (ULID) via `createMagicLinkToken`.\n * 3. Grantor derives a **content key** + a **KEK** from\n * `(serverSecret, token, vault)` using HKDF-SHA256 with separate\n * `info` tags — both callers (grantor and grantee) can derive the\n * same keys given the same inputs.\n * 4. Grantor persists a record in `_magic_link_grants/<token>`:\n * - envelope `_data` is AES-GCM encrypted under the content key\n * - the inner `wrappedDek` is AES-KW wrapped under the KEK\n * 5. Grantee receives the URL, derives the same content key + KEK,\n * loads the grant, decrypts the envelope, unwraps the tier DEK.\n *\n * ## Why a separate collection from `_delegations`\n *\n * `_delegations` envelopes are encrypted under a DEK shared across\n * every vault user (audit-visibility). External auditors / client\n * portal users have NO pre-existing keyring, so they cannot read that\n * DEK. Magic-link grants live in their own collection whose envelope\n * encryption is derived purely from the magic-link URL + server secret\n * — nothing else is required to decrypt.\n *\n * ## Batch grants\n *\n * One magic-link token may point to MULTIPLE grants (e.g. the client\n * portal case: invoices + payments + etax all share one link). Each\n * grant is persisted under a distinct record id:\n *\n * `<token>` for the single-grant / primary entry\n * `<token>:<index>` for subsequent entries\n *\n * `listMagicLinkGrants(store, vault, token)` enumerates every record\n * whose id begins with `<token>` so the claimant can materialize all\n * DEKs in one pass.\n *\n * ## Revocation\n *\n * `store.delete(vault, _magic_link_grants, <token>)` immediately\n * invalidates the link — even if the URL was captured and the server\n * secret leaked, no payload remains to decrypt.\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { encrypt, openEnvelopeJson, wrapKey, unwrapKey, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { dekKey } from './tiers.js'\nimport { DelegationTargetMissingError } from '../../kernel/errors.js'\n\n/** Reserved collection holding magic-link grant envelopes. */\nexport const MAGIC_LINK_GRANTS_COLLECTION = '_magic_link_grants'\n\n/** HKDF `info` for the AES-GCM content key. Version-namespaced. */\nexport const MAGIC_LINK_CONTENT_INFO_PREFIX = 'noydb-magic-link-content-v1:'\n\n/** HKDF `info` for the AES-KW KEK. Matches `@noy-db/on-magic-link`. */\nexport const MAGIC_LINK_KEK_INFO_PREFIX = 'noydb-magic-link-v1:'\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\n/**\n * Decrypted payload of a magic-link grant record. Mirrors\n * `DelegationToken` in `team/delegation.ts` but tracked separately\n * because the two flows persist under different collections + envelope\n * encryption schemes.\n */\nexport interface MagicLinkGrantPayload {\n readonly id: string\n readonly toUser: string\n readonly fromUser: string\n readonly tier: number\n /** Collection name or `null` for the vault-wide tier DEK. */\n readonly collection: string | null\n /** Optional specific record id scope. */\n readonly record?: string\n /** ISO timestamp — grant expires at this instant. */\n readonly until: string\n /** AES-KW-wrapped tier DEK, unwrap with the magic-link KEK. */\n readonly wrappedDek: string\n /** ISO timestamp the grant was issued. */\n readonly createdAt: string\n /** Optional caller-provided label (surfaced in audit UIs). */\n readonly note?: string\n}\n\nexport interface IssueMagicLinkGrantOptions {\n readonly toUser: string\n readonly tier: number\n readonly collection?: string\n readonly record?: string\n readonly until: Date | string\n readonly note?: string\n}\n\nexport interface MagicLinkGrantRecord {\n /** Store record id — `<token>` or `<token>:<index>` for batch entries. */\n readonly recordId: string\n readonly payload: MagicLinkGrantPayload\n}\n\n// ─── Key derivation ─────────────────────────────────────────────────────\n\n/**\n * Derive the AES-GCM content key from the same HKDF inputs used for\n * the magic-link KEK. Different `info` suffix → domain-separated key.\n *\n * Exported so the `@noy-db/on-magic-link` package can share the exact\n * derivation path without cross-dependency between the two modules.\n */\nexport async function deriveMagicLinkContentKey(\n serverSecret: string | Uint8Array<ArrayBuffer>,\n token: string,\n vault: string,\n): Promise<EnclaveKey> {\n const subtle = globalThis.crypto.subtle\n const ikmBytes =\n serverSecret instanceof Uint8Array\n ? serverSecret\n : new TextEncoder().encode(serverSecret)\n const tokenBytes = new TextEncoder().encode(token)\n const saltBuffer = await subtle.digest('SHA-256', tokenBytes)\n const info = new TextEncoder().encode(MAGIC_LINK_CONTENT_INFO_PREFIX + vault)\n const ikm = await subtle.importKey('raw', ikmBytes, 'HKDF', false, ['deriveKey'])\n return subtle.deriveKey(\n { name: 'HKDF', hash: 'SHA-256', salt: saltBuffer, info },\n ikm,\n { name: 'AES-GCM', length: 256 },\n false,\n ['encrypt', 'decrypt'],\n )\n}\n\n// ─── Issue ──────────────────────────────────────────────────────────────\n\n/**\n * Persist a magic-link grant record. Caller derives + provides both\n * the content key and the KEK; this function performs the wrap/encrypt\n * and writes the envelope.\n *\n * `recordId` lets the caller use either the bare token (primary grant)\n * or a suffixed id (batch entry). The writer is responsible for\n * collision-avoidance across batch entries.\n */\nexport async function writeMagicLinkGrant(\n store: NoydbStore,\n vault: string,\n grantor: UnlockedKeyring,\n contentKey: EnclaveKey,\n grantKek: EnclaveKey,\n recordId: string,\n opts: IssueMagicLinkGrantOptions,\n): Promise<MagicLinkGrantRecord> {\n const collectionName = opts.collection ?? null\n const sourceKey = collectionName\n ? dekKey(collectionName, opts.tier)\n : `__any#${opts.tier}`\n const sourceDek = grantor.deks.get(sourceKey)\n if (!sourceDek) {\n throw new DelegationTargetMissingError(\n `grantor cannot find tier ${opts.tier} DEK for ${collectionName ?? '(any)'}`,\n )\n }\n const wrappedDek = await wrapKey(sourceDek, grantKek)\n\n const until = typeof opts.until === 'string' ? opts.until : opts.until.toISOString()\n const createdAt = new Date().toISOString()\n const payload: MagicLinkGrantPayload = {\n id: recordId,\n toUser: opts.toUser,\n fromUser: grantor.userId,\n tier: opts.tier,\n collection: collectionName,\n ...(opts.record && { record: opts.record }),\n until,\n wrappedDek,\n createdAt,\n ...(opts.note && { note: opts.note }),\n }\n\n const { iv, data } = await encrypt(JSON.stringify(payload), contentKey)\n const envelope: EncryptedEnvelope = {\n _noydb: 1,\n _v: 1,\n _ts: createdAt,\n _iv: iv,\n _data: data,\n _by: grantor.userId,\n }\n await store.put(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId, envelope)\n return { recordId, payload }\n}\n\n// ─── Claim ──────────────────────────────────────────────────────────────\n\n/**\n * Fetch + decrypt a single magic-link grant record by id. Returns null\n * when the record is absent OR when decryption fails (wrong server\n * secret, wrong vault, tampered envelope) — callers treat a null as\n * \"this URL is not valid for this server\".\n *\n * The returned payload's `wrappedDek` is still AES-KW-wrapped; the\n * caller unwraps it with the magic-link KEK to obtain the tier DEK.\n */\nexport async function readMagicLinkGrantRecord(\n store: NoydbStore,\n vault: string,\n contentKey: EnclaveKey,\n recordId: string,\n): Promise<MagicLinkGrantPayload | null> {\n const env = await store.get(vault, MAGIC_LINK_GRANTS_COLLECTION, recordId)\n if (!env) return null\n try {\n const json = await openEnvelopeJson(env, contentKey)\n return JSON.parse(json) as MagicLinkGrantPayload\n } catch {\n return null\n }\n}\n\n/**\n * Enumerate every grant record sharing the magic-link `token` prefix\n * (i.e. the primary `<token>` entry plus any `<token>:*` batch entries).\n * Expired grants are still returned — the caller filters on `until`.\n */\nexport async function listMagicLinkGrants(\n store: NoydbStore,\n vault: string,\n contentKey: EnclaveKey,\n token: string,\n): Promise<MagicLinkGrantPayload[]> {\n const ids = await store.list(vault, MAGIC_LINK_GRANTS_COLLECTION)\n const matching = ids.filter(id => id === token || id.startsWith(`${token}:`))\n const out: MagicLinkGrantPayload[] = []\n for (const id of matching) {\n const payload = await readMagicLinkGrantRecord(store, vault, contentKey, id)\n if (payload) out.push(payload)\n }\n return out\n}\n\n/**\n * Unwrap the tier DEK from a grant payload using the magic-link KEK.\n * Thin wrapper around `unwrapKey` — provided so the claimant can avoid\n * importing `crypto.js` directly.\n */\nexport async function unwrapMagicLinkGrant(\n payload: MagicLinkGrantPayload,\n grantKek: EnclaveKey,\n): Promise<EnclaveKey> {\n return unwrapKey(payload.wrappedDek, grantKek)\n}\n\n/**\n * Delete a magic-link grant (primary + every batch entry sharing the\n * token). Safe to call when nothing exists.\n */\nexport async function revokeMagicLinkGrant(\n store: NoydbStore,\n vault: string,\n token: string,\n): Promise<number> {\n const ids = await store.list(vault, MAGIC_LINK_GRANTS_COLLECTION)\n const matching = ids.filter(id => id === token || id.startsWith(`${token}:`))\n for (const id of matching) {\n await store.delete(vault, MAGIC_LINK_GRANTS_COLLECTION, id)\n }\n return matching.length\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────\n\n/**\n * Compose the batch-entry record id. `index === 0` → bare token.\n * Subsequent entries use `<token>:<index>` so `store.list()` can\n * enumerate them all by common prefix.\n */\nexport function magicLinkGrantRecordId(token: string, index: number): string {\n return index === 0 ? token : `${token}:${index}`\n}\n\n/**\n * True when the payload's `until` is in the past relative to `now`.\n * Kept here (rather than inlined) so the semantics stay aligned with\n * the canonical `DelegationToken` expiry check.\n */\nexport function isMagicLinkGrantExpired(\n payload: MagicLinkGrantPayload,\n now: Date = new Date(),\n): boolean {\n return payload.until <= now.toISOString()\n}\n","/**\n * Multi-user team capability contract (#267 Track A tail — the\n * keyring-grant → team split). Lives on the `/with` port (the one seam the\n * kernel spine may import statically) so `Noydb` can hold the `NO_TEAM`\n * floor default without a spine→service static import.\n *\n * The active engine (`withTeam()` in `with-party/team/active.ts`)\n * statically links the grant/revoke/rotate keyring engines from\n * `keyring.ts` — bundle-charged to consumers of the `@noy-db/hub/team`\n * subpath, never to the single-user floor. {@link NO_TEAM} (the floor\n * default) throws {@link TeamNotEnabledError}.\n *\n * `Noydb.grant` / `Noydb.revoke` / `Noydb.rotate` delegate here, passing\n * their `TeamFacade` — the facade owns the policy-gate + keyring plumbing\n * (`runGrant` / `runRevoke` / `runRotate`) and receives the engine as an\n * argument from the strategy.\n *\n * Deliberately NOT gated (single-user primitives): owner keyring creation,\n * unlock, `listUsers`, `updateUser`, passphrase rotate/recover, and the\n * `createDeedOwner` free function (no createNoydb instance to gate against —\n * the same carve-out as `liberateVault`).\n * @internal\n */\nimport type { GrantOptions, RevokeOptions, FactorProofBundle } from '../../kernel/types.js'\nimport { TeamNotEnabledError } from '../../kernel/errors.js'\nimport type { TeamFacade } from '../../with-party/team/noydb-facade.js'\n\nexport interface TeamStrategy {\n grant(team: TeamFacade, vault: string, options: GrantOptions, factors?: FactorProofBundle): Promise<void>\n revoke(team: TeamFacade, vault: string, options: RevokeOptions, factors?: FactorProofBundle): Promise<void>\n rotate(team: TeamFacade, vault: string, collections: string[]): Promise<void>\n}\n\n/**\n * No-op stub — the floor default. Every multi-user team operation throws\n * {@link TeamNotEnabledError}; opt in with `teamStrategy: withTeam()` in\n * createNoydb. @internal\n */\nexport const NO_TEAM: TeamStrategy = {\n async grant() { throw new TeamNotEnabledError() },\n async revoke() { throw new TeamNotEnabledError() },\n async rotate() { throw new TeamNotEnabledError() },\n}\n","/**\n * Tier-1 change flows — `rotatePassphrase` (user remembers old) and\n * `recoverPassphrase` (user supplies a recovery proof).\n *\n * The two flows share the post-verification half — fresh salt, fresh\n * KEK, rewrap every DEK — and differ only in how they re-derive the\n * old KEK:\n *\n * - **Rotate**: derive from the supplied `oldPassphrase`.\n * - **Recover (paper)**: unwrap from a `RecoveryCodeEntry` using a\n * user-supplied recovery code. The entry is burned on success.\n *\n * The non-paper recovery profiles (Shamir, multi-channel,\n * admin-mediated) are not yet wired — calling them throws\n * {@link RecoveryProfileNotImplementedError} with a tracking link.\n *\n * @module\n */\nimport type { NoydbStore, KeyringFile } from '../../kernel/types.js'\nimport { NOYDB_KEYRING_VERSION } from '../../kernel/types.js'\nimport {\n deriveKey,\n generateSalt,\n wrapKey,\n unwrapKey,\n bufferToBase64,\n base64ToBuffer,\n type EnclaveKey,\n} from '../../kernel/enclave/index.js'\nimport { InvalidKeyError, NoAccessError, RecoveryProfileNotImplementedError } from '../../kernel/errors.js'\nimport {\n loadPaperRecoveryEntries,\n burnPaperRecoveryEntry,\n unwrapDeksFromPaperEntry,\n loadShamirRecoveryEntries,\n unwrapDeksFromShamirEntry,\n type PaperRecoveryEntry,\n type ShamirRecoveryEntry,\n} from './recovery.js'\nimport type { ShamirRecoveryProvider } from './shamir-recovery-provider.js'\nimport { assertStrongPassphrase, type PassphrasePolicy } from '../../kernel/validation.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { mintKeyringCanary } from './keyring.js'\nimport type { KeyringAuthenticator } from '../../kernel/types.js'\nimport type { EnrollAuthenticatorOptions } from './authenticators.js'\nimport { ValidationError } from '../../kernel/errors.js'\n\n/**\n * Context handed to a {@link SlotRewrapCeremony} when `rotatePassphrase`\n * preserves a tier-2 slot. The ceremony's job is to re-derive its\n * method-specific wrapping material (PRF assertion, PBKDF2 of the\n * password, etc.) and wrap the freshly rewrapped DEK set under\n * the new wrapping key.\n *\n * Two surfaces are exposed:\n *\n * - `newDeks` — the rewrapped (extractable) DEK set the slot will\n * wrap. This is what `mintPaperRecoveryEntry` / `enrollPassword-\n * Authenticator` / `wrapKeyringSummary` (in `@noy-db/on-webauthn`)\n * all consume; effectively the canonical input for every\n * post-Path C tier-2 ceremony.\n *\n * - `newKek` — the freshly-derived KEK (extractable for the\n * ceremony scope only). Only relevant for forward-compatibility\n * with a hypothetical future on-* package that wants to wrap the\n * KEK itself under a method-derived key. None of the shipped\n * on-* packages need this; they all operate on `newDeks`.\n *\n * The ceremony MUST preserve `oldSlot.id` and `oldSlot.method` in the\n * returned `EnrollAuthenticatorOptions`. Hub validates these — a\n * mismatch throws `ValidationError` (prevents slot-type swap mid-\n * rotation, e.g. converting a webauthn slot to a password slot under\n * cover of preservation).\n */\nexport interface SlotRewrapContext {\n readonly newKek: EnclaveKey\n readonly newDeks: Map<string, EnclaveKey>\n readonly oldSlot: KeyringAuthenticator\n}\n\n/**\n * Callback that re-enrolls one tier-2 slot during `rotatePassphrase`.\n * Returns the new slot's `EnrollAuthenticatorOptions` — same shape\n * the consumer would pass to `db.enrollAuthenticator` for a fresh\n * enrollment. Hub persists the result atomically with the rotation.\n */\nexport type SlotRewrapCeremony = (\n ctx: SlotRewrapContext,\n) => Promise<EnrollAuthenticatorOptions>\n\n/** Caller payload for {@link rotatePassphrase}. */\nexport interface RotatePassphraseInput {\n readonly oldPassphrase: string\n readonly newPassphrase: string\n readonly passphrasePolicy?: PassphrasePolicy\n readonly allowWeakPassphrase?: boolean\n /**\n * Map of slot id → re-enrolment ceremony. Slots whose id appears\n * here are PRESERVED across rotation (the ceremony re-derives the\n * method-specific wrapping under the new keyring); slots whose id\n * is absent are DROPPED (the pre-slot-ceremony behavior).\n *\n * Without this map, `rotatePassphrase` wipes every tier-2 slot. Consumers building a\n * \"rotate without losing my biometric\" flow supply ceremonies for\n * each slot they want to keep.\n *\n * If a ceremony throws, the entire rotation throws — no partial\n * state. Callers wrap individual ceremonies in try/catch + return\n * a sentinel if they want graceful degradation per slot.\n *\n * Added when slot-ceremony rewrapping landed.\n */\n readonly slotCeremonies?: { readonly [slotId: string]: SlotRewrapCeremony }\n}\n\n/**\n * Re-derive the user's KEK from `oldPassphrase`, rewrap every DEK\n * under a freshly-derived KEK from `newPassphrase`, and persist.\n *\n * Tier-2 authenticator slots are dropped UNLESS the caller supplies\n * a `slotCeremonies` map — each ceremony re-derives its\n * method-specific wrapping under the new keyring, and hub persists\n * the rewrapped slots atomically with the rotation. Slots whose id\n * isn't in the map are still dropped.\n *\n * @throws `InvalidKeyError` if `oldPassphrase` does not unwrap the keyring.\n * @throws `WeakPassphraseError` if `newPassphrase` fails the strength rule.\n * @throws `ValidationError` if a ceremony's result mismatches the\n * slot's id or method (anti-slot-swap guard).\n */\nexport async function rotatePassphrase(\n store: NoydbStore,\n vault: string,\n userId: string,\n input: RotatePassphraseInput,\n): Promise<UnlockedKeyring> {\n if (!input.allowWeakPassphrase) {\n assertStrongPassphrase(input.newPassphrase, input.passphrasePolicy)\n }\n\n const env = await store.get(vault, '_keyring', userId)\n if (!env) {\n throw new NoAccessError(`No keyring found for user \"${userId}\" in vault \"${vault}\".`)\n }\n const file = JSON.parse(env._data) as KeyringFile\n const oldSalt = base64ToBuffer(file.salt)\n const oldKek = await deriveKey(input.oldPassphrase, oldSalt)\n\n // Unwrap every DEK with the OLD KEK first — this also validates the\n // passphrase (a bad KEK throws InvalidKeyError on the first unwrap).\n const deks = new Map<string, EnclaveKey>()\n for (const [coll, wrapped] of Object.entries(file.deks)) {\n deks.set(coll, await unwrapKey(wrapped, oldKek))\n }\n\n const newSalt = generateSalt()\n const newKek = await deriveKey(input.newPassphrase, newSalt)\n\n // Rewrap with the new KEK.\n const wrappedDeks: Record<string, string> = {}\n for (const [coll, dek] of deks) {\n wrappedDeks[coll] = await wrapKey(dek, newKek)\n }\n\n // Slot rewrap. Without slotCeremonies, we drop every existing\n // slot. With a ceremony map, slots whose id appears in the map\n // are preserved; the rest are dropped.\n const oldSlots = file.authenticators ?? []\n const newSlots: KeyringAuthenticator[] = []\n if (input.slotCeremonies && oldSlots.length > 0) {\n for (const oldSlot of oldSlots) {\n const ceremony = input.slotCeremonies[oldSlot.id]\n if (!ceremony) continue // drop — not in slotCeremonies map\n\n const result = await ceremony({ newKek, newDeks: deks, oldSlot })\n\n // Anti-slot-swap guard. The ceremony MUST preserve identity —\n // a mismatch would let the consumer convert a webauthn slot to\n // a password slot mid-rotation, which would silently change\n // the security profile of the slot under cover of \"rotation.\"\n if (result.id !== oldSlot.id) {\n throw new ValidationError(\n `slotCeremonies['${oldSlot.id}'] returned id=\"${result.id}\". ` +\n 'The id must match the rotated slot — a ceremony cannot ' +\n 'change a slot\\'s identity.',\n )\n }\n if (result.method !== oldSlot.method) {\n throw new ValidationError(\n `slotCeremonies['${oldSlot.id}'] returned method=\"${result.method}\", ` +\n `expected \"${oldSlot.method}\". The method must match the rotated ` +\n 'slot — a ceremony cannot change the auth method (e.g. webauthn ' +\n '→ password) under cover of rotation.',\n )\n }\n // wrapKind absent on legacy slots / wrap-KEK enroll inputs; treat as 'kek'.\n const oldWrapKind = oldSlot.wrapKind ?? 'kek'\n const newWrapKind = result.wrapKind ?? 'kek'\n if (oldWrapKind !== newWrapKind) {\n throw new ValidationError(\n `slotCeremonies['${oldSlot.id}'] returned wrapKind=\"${newWrapKind}\", ` +\n `expected \"${oldWrapKind}\". The wrap format must match the rotated ` +\n 'slot — a ceremony cannot change the wrap shape (e.g. wrap-KEK → ' +\n 'wrap-DEKs) under cover of rotation, since that would silently ' +\n 'change the session tier produced at unlock.',\n )\n }\n\n // Build the persisted slot from the ceremony result. Mirrors\n // the same construction `enrollAuthenticator` does — wrap-DEKs\n // variants carry { wrapped_deks, iv }; wrap-KEK variants\n // carry { wrapped_kek }.\n const baseFields = {\n id: result.id,\n method: result.method,\n // Preserve original enrolled_at — rotation is rewrapping, not\n // re-enrollment. The slot's enrolment timestamp tracks when\n // the user originally added the slot, not when it was last\n // rewrapped. Forensics consumers reading enrolled_at are\n // tracking the slot's ORIGIN, not its CURRENT wrapping.\n enrolled_at: oldSlot.enrolled_at,\n enrolled_via_tier: result.enrolled_via_tier ?? oldSlot.enrolled_via_tier,\n meta: result.meta,\n } as const\n const newSlot: KeyringAuthenticator = result.wrapKind === 'deks'\n ? {\n ...baseFields,\n wrapKind: 'deks',\n wrapped_deks: result.wrapped_deks,\n iv: result.iv,\n }\n : {\n ...baseFields,\n wrapped_kek: result.wrapped_kek,\n }\n newSlots.push(newSlot)\n }\n }\n\n const canary = await mintKeyringCanary(newKek)\n const next: KeyringFile = {\n ...file,\n _noydb_keyring: NOYDB_KEYRING_VERSION,\n deks: wrappedDeks,\n salt: bufferToBase64(newSalt),\n authenticators: newSlots,\n canary,\n }\n\n await writeKeyringFile(store, vault, userId, next)\n\n return {\n userId: file.user_id,\n displayName: file.display_name,\n role: file.role,\n permissions: file.permissions,\n deks,\n kek: newKek,\n salt: newSalt,\n authenticators: newSlots,\n ...(file.export_capability !== undefined && { exportCapability: file.export_capability }),\n ...(file.import_capability !== undefined && { importCapability: file.import_capability }),\n }\n}\n\n/**\n * Caller payload for {@link recoverPassphrase}.\n *\n * `paper` and `shamir` are wired end-to-end.\n * The remaining two profiles (`multi-channel`, `admin-mediated`)\n * stay outside the union and throw\n * {@link RecoveryProfileNotImplementedError} at the runtime guard\n * when bypassed via `as unknown as RecoveryProof`.\n */\nexport type RecoveryProof =\n | { readonly profile: 'paper'; readonly payload: { readonly code: string } }\n | { readonly profile: 'shamir'; readonly payload: {\n /** Optional disambiguator when multiple Shamir entries are enrolled.\n * When omitted, hub tries each entry until one combines. */\n readonly entryId?: string\n /** K or more opaque share strings, as returned by `ShamirRecoveryProvider.splitToShares`. */\n readonly shares: ReadonlyArray<string>\n } }\n\nexport interface RecoverPassphraseInput {\n readonly newPassphrase: string\n readonly recoveryProof: RecoveryProof\n readonly passphrasePolicy?: PassphrasePolicy\n readonly allowWeakPassphrase?: boolean\n /**\n * After a successful paper-recovery, replace ALL remaining recovery\n * entries with freshly-minted ones. Defaults to `true` (defensive).\n *\n * Rationale: the user just demonstrated they had access\n * to AT LEAST one code. The remaining codes from the same printed\n * sheet may also be compromised — photographed, leaked via a\n * screen-share slip, or in the hands of whoever stole the sheet.\n * Auto-rotation closes the window without requiring consumer action.\n *\n * Set to `false` to preserve the original behavior (only the matched\n * code is burned; the rest stay valid).\n *\n * Hub-side orchestration is non-atomic with the recovery itself:\n * if the rotation step fails after a successful burn, the user\n * falls back to the pre-rotation state (remaining codes still\n * valid). Strictly safer than the previous default — a failed\n * rotation degrades gracefully rather than leaving the vault\n * locked or codes dual-existing.\n */\n readonly rotateRemainingCodes?: boolean\n /**\n * Number of fresh codes to mint when `rotateRemainingCodes` is on.\n * Defaults to the count of remaining entries POST-burn (e.g. if\n * the user enrolled 8 originally and just consumed 1, defaults to\n * 7). Pass an explicit number to mint a different count — useful\n * when the consumer wants to refresh to a target N regardless of\n * how many were left.\n */\n readonly newCodeCount?: number\n /**\n * Override the default raw-code generator. The default is hub's\n * {@link generateULID} — uppercase Crockford-Base32, 26 chars,\n * passes through `normalizePaperCode` untouched.\n *\n * Pass `() => generateRawCode()` from `@noy-db/on-recovery` when\n * the consumer prefers the Base32 + checksum format with hyphenated\n * display. The `mintPaperRecoveryEntry` helper accepts any string —\n * the generator just needs to produce a high-entropy unique value.\n */\n readonly codeGenerator?: () => string\n}\n\n/**\n * Return shape of `db.recoverPassphrase`. `newCodes` is populated when\n * `rotateRemainingCodes` was enabled and at least one entry was\n * rotated; an empty array means no rotation happened (rotation\n * disabled, or no remaining codes after burn). Show the codes to the\n * user once — they are the canonical credential for future recovery\n * and CANNOT be retrieved again.\n */\nexport interface RecoverPassphraseResult {\n readonly newCodes: readonly string[]\n}\n\n/**\n * Input for {@link Noydb.rotateRecovery} — deliberate\n * recovery-credential regeneration when the user knows their\n * passphrase but wants a fresh sheet (paper) or fresh shares\n * (shamir). Symmetric to {@link RotatePassphraseInput}.\n */\nexport type RotateRecoveryOptions =\n | {\n readonly profile: 'paper'\n /** How many fresh codes to mint. Default: existing sheet size. */\n readonly count?: number\n /** Optional code generator — see {@link RecoverPassphraseInput.codeGenerator}. */\n readonly codeGenerator?: () => string\n }\n | {\n readonly profile: 'shamir'\n /** New threshold. */\n readonly k: number\n /** New total share count. */\n readonly n: number\n /** Disambiguator when multiple Shamir entries exist; required if there are 2+. */\n readonly entryId?: string\n /** Optional updated label. */\n readonly label?: string\n }\n\n/**\n * Result of {@link Noydb.rotateRecovery}. Shape varies by profile:\n *\n * - `paper` → `{ newCodes: string[] }` (and `entryId === 'paper-batch'`)\n * - `shamir` → `{ newShares: string[], entryId }`\n *\n * `newCodes` is populated for paper rotations; `newShares` for\n * Shamir rotations. Both are show-once — the hub does not\n * retain them.\n */\nexport interface RotateRecoveryResult {\n readonly newCodes?: readonly string[]\n readonly newShares?: readonly string[]\n readonly entryId?: string\n}\n\n/**\n * Result of {@link Noydb.enrollRecovery}. Shape varies by profile:\n *\n * - `paper` → `{ entryId: 'paper-batch' }` (caller minted the\n * entries; this is a sentinel since paper enrollments are batch-shaped).\n * - `shamir` → `{ entryId, shares: string[] }` — shares are\n * show-once; the hub does not retain them.\n */\nexport interface EnrollRecoveryResult {\n readonly entryId: string\n readonly shares?: readonly string[]\n}\n\n/**\n * Input shape for {@link Noydb.enrollRecovery} and\n * {@link Noydb.openVaultAndEnrollRecovery}. Discriminated\n * union over recovery profiles.\n *\n * - `paper`: caller pre-mints entries (typically via\n * `mintPaperRecoveryEntry` or `@noy-db/on-recovery`'s\n * `generateRecoveryCodeSet`) and passes them in. The hub stores\n * them and surfaces an opaque batch id.\n * - `shamir`: hub mints the recovery secret + the shares at\n * enrollment time. The shares are returned in\n * {@link EnrollRecoveryResult.shares} (show-once); the hub never\n * retains them.\n *\n * Multi-channel and admin-mediated will be added when the respective\n * dispatch slices ship.\n */\nexport type RecoveryEnrollmentInput =\n | { readonly profile: 'paper'; readonly entries: ReadonlyArray<PaperRecoveryEntry> }\n | {\n readonly profile: 'shamir'\n readonly k: number\n readonly n: number\n readonly label?: string\n readonly entryId?: string\n }\n\n/**\n * Reset the user's passphrase using a recovery proof.\n * Supports `'paper'` and `'shamir'` profiles. The other profiles throw\n * {@link RecoveryProfileNotImplementedError}.\n *\n * On success, the used recovery entry is burned (deleted from the\n * stored set).\n */\nexport async function recoverPassphrase(\n provider: ShamirRecoveryProvider | undefined,\n store: NoydbStore,\n vault: string,\n userId: string,\n input: RecoverPassphraseInput,\n): Promise<UnlockedKeyring> {\n if (!input.allowWeakPassphrase) {\n assertStrongPassphrase(input.newPassphrase, input.passphrasePolicy)\n }\n\n // Runtime defense-in-depth: the type narrows to 'paper' | 'shamir',\n // but a consumer bypassing TS via\n // `as unknown as RecoveryProof` should still hit a clear error\n // rather than silently fall into a handler with a malformed payload.\n const profile = (input.recoveryProof as { profile: string }).profile\n if (profile === 'paper') {\n return recoverViaPaperCode(store, vault, userId, input)\n }\n if (profile === 'shamir') {\n return recoverViaShamir(provider, store, vault, userId, input)\n }\n throw new RecoveryProfileNotImplementedError(\n profile,\n 'https://github.com/vLannaAi/noy-db/issues/196',\n )\n}\n\nasync function recoverViaPaperCode(\n store: NoydbStore,\n vault: string,\n userId: string,\n input: RecoverPassphraseInput,\n): Promise<UnlockedKeyring> {\n if (input.recoveryProof.profile !== 'paper') throw new Error('unreachable')\n const { code } = input.recoveryProof.payload\n\n const env = await store.get(vault, '_keyring', userId)\n if (!env) {\n throw new NoAccessError(`No keyring found for user \"${userId}\" in vault \"${vault}\".`)\n }\n const file = JSON.parse(env._data) as KeyringFile\n\n const entries = await loadPaperRecoveryEntries(store, vault)\n if (entries.length === 0) {\n throw new NoAccessError(\n `No paper-recovery entries enrolled for vault \"${vault}\". ` +\n 'Enroll via `db.enrollRecovery({ profile: \"paper\", entries })` before relying on recovery.',\n )\n }\n\n const normalized = normalizePaperCode(code)\n let recovered: { deks: Map<string, EnclaveKey>; entry: PaperRecoveryEntry } | undefined\n for (const entry of entries) {\n try {\n const deks = await unwrapDeksFromPaperEntry(entry, normalized)\n recovered = { deks, entry }\n break\n } catch {\n // wrong code for this entry — try the next one\n }\n }\n if (!recovered) {\n throw new InvalidKeyError(\n 'Recovery code does not match any enrolled paper entry. The code may have been ' +\n 'previously used (single-use) or typed incorrectly.',\n )\n }\n\n const deks = recovered.deks\n\n // Fresh salt + KEK from the new passphrase, rewrap.\n const newSalt = generateSalt()\n const newKek = await deriveKey(input.newPassphrase, newSalt)\n const wrappedDeks: Record<string, string> = {}\n for (const [coll, dek] of deks) {\n wrappedDeks[coll] = await wrapKey(dek, newKek)\n }\n\n const canary = await mintKeyringCanary(newKek)\n const next: KeyringFile = {\n ...file,\n _noydb_keyring: NOYDB_KEYRING_VERSION,\n deks: wrappedDeks,\n salt: bufferToBase64(newSalt),\n authenticators: [], // tier-2 slots wrap old KEK, drop them\n canary,\n }\n\n // Burn first, then rewrite the keyring. The two writes are not\n // atomic — if the second fails, the safer ordering is:\n //\n // 1. Code burned, keyring untouched: user keeps their old passphrase\n // and loses one recovery code (recoverable: contact admin / use\n // another code).\n //\n // 2. Keyring rewritten, code unburned: user has rotated, but the\n // consumed code REMAINS VALID. Anyone with access to the paper\n // sheet can use it again. Security regression.\n //\n // Burning first picks (1) over (2).\n await burnPaperRecoveryEntry(store, vault, recovered.entry.codeId)\n await writeKeyringFile(store, vault, userId, next)\n\n return {\n userId: file.user_id,\n displayName: file.display_name,\n role: file.role,\n permissions: file.permissions,\n deks,\n kek: newKek,\n salt: newSalt,\n authenticators: [],\n ...(file.export_capability !== undefined && { exportCapability: file.export_capability }),\n ...(file.import_capability !== undefined && { importCapability: file.import_capability }),\n }\n}\n\n/**\n * Mirror of `@noy-db/on-recovery/parseRecoveryCode`. Inlined so the\n * hub does not gain a peer dep on on-recovery — both implementations\n * follow the same RFC 4648 Base32 + checksum format and round-trip\n * through the same KDF.\n *\n * Accepts hyphenated, lowercase, or whitespace-padded input.\n */\nfunction normalizePaperCode(input: string): string {\n return input.toUpperCase().replace(/[\\s\\-_]/g, '')\n}\n\n/**\n * Recover the user's keyring via the Shamir profile.\n *\n * 1. Decode each supplied share string into a {@link RawShare}.\n * 2. Load `_meta/recovery-shamir` entries.\n * 3. If `payload.entryId` is supplied, restrict to that entry; else\n * iterate over all entries and try each until one combines.\n * 4. For each candidate: filter shares to those whose `(k, n)`\n * match the entry's parameters, then attempt\n * `unwrapDeksFromShamirEntry`. AES-GCM auth-tag failure means\n * the combined secret doesn't match — try the next entry.\n * 5. With unwrapped DEKs: derive fresh KEK from `newPassphrase` +\n * fresh salt, rewrap, write the keyring.\n * 6. Shamir entries are NOT burned on recovery (shares reusable);\n * explicit {@link Noydb.rotateRecovery} is the refresh ceremony.\n */\nasync function recoverViaShamir(\n provider: ShamirRecoveryProvider | undefined,\n store: NoydbStore,\n vault: string,\n userId: string,\n input: RecoverPassphraseInput,\n): Promise<UnlockedKeyring> {\n if (input.recoveryProof.profile !== 'shamir') throw new Error('unreachable')\n const { entryId: requestedEntryId, shares: shareStrings } = input.recoveryProof.payload\n\n if (shareStrings.length === 0) {\n throw new ValidationError(\n 'Shamir recovery requires at least one share; received an empty array.',\n )\n }\n\n const env = await store.get(vault, '_keyring', userId)\n if (!env) {\n throw new NoAccessError(`No keyring found for user \"${userId}\" in vault \"${vault}\".`)\n }\n const file = JSON.parse(env._data) as KeyringFile\n\n const allEntries = await loadShamirRecoveryEntries(store, vault)\n if (allEntries.length === 0) {\n throw new NoAccessError(\n `No Shamir-recovery entries enrolled for vault \"${vault}\". `\n + 'Enroll via `db.enrollRecovery({ profile: \"shamir\", k, n })` before relying on recovery.',\n )\n }\n\n if (!provider) {\n throw new Error(\n \"shamir recovery requires a ShamirRecoveryProvider — pass \"\n + \"shamirRecovery: shamirRecoveryProvider() from '@noy-db/on-shamir' to createNoydb()\",\n )\n }\n\n // Restrict to a specific entry when entryId supplied.\n let candidates: ReadonlyArray<ShamirRecoveryEntry>\n if (requestedEntryId !== undefined) {\n candidates = allEntries.filter(e => e.entryId === requestedEntryId)\n if (candidates.length === 0) {\n throw new NoAccessError(\n `No Shamir-recovery entry with entryId=\"${requestedEntryId}\" found `\n + `in vault \"${vault}\". Available entries: `\n + allEntries.map(e => `\"${e.entryId}\"`).join(', '),\n )\n }\n } else {\n candidates = allEntries\n }\n\n // Try each candidate entry. Pass all share strings to the provider;\n // provider.combineShares validates and throws on mismatch — the\n // AES-GCM auth-tag is an additional guard.\n let recoveredDeks: Map<string, EnclaveKey> | undefined\n for (const entry of candidates) {\n if (shareStrings.length < entry.k) {\n // Not enough shares for this entry — could still match another.\n continue\n }\n try {\n const deks = await unwrapDeksFromShamirEntry(provider, entry, shareStrings)\n recoveredDeks = deks\n break\n } catch {\n // provider.combineShares threw (malformed/mismatched shares) or\n // AES-GCM auth-tag failure → try the next entry.\n }\n }\n\n if (!recoveredDeks) {\n // Distinguish \"below-threshold\" from \"no entry matches\" so the\n // error message is actionable.\n const minK = Math.min(...candidates.map(e => e.k))\n if (shareStrings.length < minK) {\n throw new InvalidKeyError(\n `Insufficient Shamir shares to combine: the smallest enrolled threshold is ${minK}, `\n + `but only ${shareStrings.length} share${shareStrings.length === 1 ? ' was' : 's were'} provided.`,\n )\n }\n throw new InvalidKeyError(\n 'Shamir shares do not match any enrolled entry. Possible causes: '\n + 'shares were tampered with, came from a different enrollment, '\n + 'or the entry was rotated after these shares were distributed.',\n )\n }\n\n // Mint fresh KEK from new passphrase, rewrap DEKs (mirrors paper).\n const newSalt = generateSalt()\n const newKek = await deriveKey(input.newPassphrase, newSalt)\n const wrappedDeks: Record<string, string> = {}\n for (const [coll, dek] of recoveredDeks) {\n wrappedDeks[coll] = await wrapKey(dek, newKek)\n }\n\n const canary = await mintKeyringCanary(newKek)\n const next: KeyringFile = {\n ...file,\n _noydb_keyring: NOYDB_KEYRING_VERSION,\n deks: wrappedDeks,\n salt: bufferToBase64(newSalt),\n authenticators: [], // tier-2 slots wrap old KEK, drop them on recovery\n canary,\n }\n\n // No burn: Shamir entries persist across recoveries. Explicit\n // rotateRecovery is the refresh ceremony.\n await writeKeyringFile(store, vault, userId, next)\n\n return {\n userId: file.user_id,\n displayName: file.display_name,\n role: file.role,\n permissions: file.permissions,\n deks: recoveredDeks,\n kek: newKek,\n salt: newSalt,\n authenticators: [],\n ...(file.export_capability !== undefined && { exportCapability: file.export_capability }),\n ...(file.import_capability !== undefined && { importCapability: file.import_capability }),\n }\n}\n\nasync function writeKeyringFile(\n store: NoydbStore,\n vault: string,\n userId: string,\n file: KeyringFile,\n): Promise<void> {\n const envelope = {\n _noydb: 1 as const,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(file),\n }\n await store.put(vault, '_keyring', userId, envelope)\n}\n","/**\n * Atomic peer-recovery primitive.\n *\n * `recoverUser` is a SEPARATE operation from `revoke + grant`. It\n * exists because peer-recovery has different semantics than account\n * removal-then-reissue:\n *\n * 1. **Same identity preserved.** `userId`, `role`, `permissions`,\n * capability bits, user envelope (if any), policy override (if\n * any) all survive. Only the wrapping changes.\n * 2. **No key rotation.** The existing DEKs stay valid — every\n * OTHER principal in the vault keeps their access. Rotating\n * keys would invalidate every co-user's wrapping.\n * 3. **Atomic by construction.** A single `store.put` overwrites\n * `_keyring/<userId>` with the recovered file. No revoke step\n * means no partial-failure window.\n * 4. **Owner→owner natively allowed.** Two co-owners recovering\n * each other is the explicitly-intentional case (a partner\n * forgot the master phrase). The existing `canRevoke` rule that\n * blocks owner→owner is correct for `revoke` (which is account\n * *removal*) and intentionally NOT replicated here. The policy\n * gate `peer-recover-user` carries the freshness requirement.\n * 5. **Tier-2 slots dropped.** The slots wrap the OLD KEK under\n * method-derived keys; after recovery the KEK is re-derived\n * from the new temp passphrase. Match `rotatePassphrase`'s\n * precedent — the recovered user re-enrols slots after picking\n * their own phrase.\n *\n * Caller must be at least as privileged as the target. The hub\n * `db.recoverUser` method gates this with the `peer-recover-user`\n * policy gate (the `peer-recover-user` factor-proof requirement); the function below\n * enforces only the role + anti-privilege-escalation invariants.\n *\n * @module\n */\nimport type { NoydbStore, KeyringFile, Role } from '../../kernel/types.js'\nimport { NOYDB_KEYRING_VERSION } from '../../kernel/types.js'\nimport { deriveKey, generateSalt, wrapKey, bufferToBase64 } from '../../kernel/enclave/index.js'\nimport { NoAccessError, PermissionDeniedError, PrivilegeEscalationError } from '../../kernel/errors.js'\nimport { assertStrongPassphrase, type PassphrasePolicy } from '../../kernel/validation.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { mintKeyringCanary } from './keyring.js'\n\n// FR-6: 'custodian' is deliberately ABSENT — an admin cannot peer-recover a\n// custodian (mirrors ADMIN_GRANTABLE_TARGETS: custodians are owner-managed\n// only). The owner CAN recover one (canRecover returns true for owner). As a\n// CALLER, a custodian recovers nobody (it is neither owner nor admin → false).\nconst ADMIN_RECOVERABLE_TARGETS: readonly Role[] = ['operator', 'viewer', 'client', 'admin']\n\n/**\n * Whether `callerRole` may recover `targetRole`.\n *\n * Differs from `canRevoke` (in `keyring.ts`) in one critical place:\n * **owner→owner IS allowed**. Peer recovery is the explicitly\n * intentional case (a co-owner forgot their phrase); the freshness\n * binding lives in the `peer-recover-user` policy gate, not in the\n * permission predicate.\n *\n * Admins can recover everyone they could grant (operator / viewer /\n * client / admin) but NOT owners — that boundary stays as a hard\n * structural rule even under recovery.\n */\nfunction canRecover(callerRole: Role, targetRole: Role): boolean {\n if (callerRole === 'owner') return true\n if (callerRole === 'admin') return ADMIN_RECOVERABLE_TARGETS.includes(targetRole)\n return false\n}\n\n/** Input shape for {@link recoverUser}. */\nexport interface RecoverUserOptions {\n /** Target user id whose keyring is being recovered. */\n readonly userId: string\n /**\n * Temporary passphrase under which the new keyring is wrapped.\n * The recipient should call `db.rotatePassphrase` immediately on\n * acceptance to choose their own phrase — this temp acts as a\n * single-use bridge in invite / peer-recovery flows.\n */\n readonly passphrase: string\n /** Override the target's role. Defaults to the existing target's role. */\n readonly role?: Role\n /** Override the target's display name. Defaults to existing. */\n readonly displayName?: string\n /** Validate phrase strength against the configured policy. */\n readonly validatePassphrase?: boolean\n /**\n * Skip phrase strength validation even when `validatePassphrase` is\n * set. The escape hatch matches `grant`'s shape — used when the\n * temp phrase is a high-entropy one-shot string that doesn't need\n * to satisfy the human-typeable rules.\n */\n readonly allowWeakPassphrase?: boolean\n /**\n * Optional explicit phrase policy override (passed through to\n * `assertStrongPassphrase`). Mirrors how `grant` accepts a custom\n * `PassphrasePolicy` for app-specific tightening.\n */\n readonly passphrasePolicy?: PassphrasePolicy\n}\n\n/**\n * Atomically rewrap the target user's keyring under a fresh temp\n * passphrase. Single store write; no revoke step; no key rotation.\n *\n * Caller's responsibilities (NOT enforced here):\n * - Run the `peer-recover-user` policy gate first via\n * `Noydb.checkGate` to enforce the freshness factor proof.\n * - Communicate the temp passphrase to the recipient via a secure\n * channel (URL fragment, in-person, etc.) — the hub does not\n * transport secrets.\n */\nexport async function recoverUser(\n store: NoydbStore,\n vault: string,\n callerKeyring: UnlockedKeyring,\n options: RecoverUserOptions,\n): Promise<void> {\n // 1. Load the target's existing keyring file (plaintext header).\n const env = await store.get(vault, '_keyring', options.userId)\n if (!env) {\n throw new NoAccessError(\n `recoverUser: user \"${options.userId}\" has no keyring in vault \"${vault}\".`,\n )\n }\n const target = JSON.parse(env._data) as KeyringFile\n const targetRole = options.role ?? target.role\n\n // 2. Permission check — caller must be allowed to recover this role.\n // Owner→owner natively allowed; admin→admin allowed; admin→owner blocked.\n if (!canRecover(callerKeyring.role, targetRole)) {\n throw new PermissionDeniedError(\n `Role \"${callerKeyring.role}\" cannot recover role \"${targetRole}\"`,\n )\n }\n // Also guard against role-uplift via the override — admin cannot\n // promote a target to owner under cover of recovery.\n if (!canRecover(callerKeyring.role, target.role)) {\n throw new PermissionDeniedError(\n `Role \"${callerKeyring.role}\" cannot recover role \"${target.role}\"`,\n )\n }\n\n // 3. Anti-privilege-escalation. Every collection the target had\n // access to must be in the caller's DEK set — the recoverer\n // cannot give the recovered user access to a collection the\n // recoverer themselves can't read. Mirrors `grant()`'s check.\n for (const coll of Object.keys(target.deks)) {\n if (!callerKeyring.deks.has(coll)) {\n throw new PrivilegeEscalationError(coll)\n }\n }\n\n // 4. Optional phrase strength validation (mirrors `grant` opt-in).\n if (options.validatePassphrase && !options.allowWeakPassphrase) {\n assertStrongPassphrase(options.passphrase, options.passphrasePolicy)\n }\n\n // 5. Mint a fresh salt + KEK from the temp passphrase. The DEKs\n // themselves are unchanged — only the wrapping is replaced.\n const newSalt = generateSalt()\n const newKek = await deriveKey(options.passphrase, newSalt)\n\n const wrappedDeks: Record<string, string> = {}\n for (const coll of Object.keys(target.deks)) {\n const callerDek = callerKeyring.deks.get(coll)\n if (!callerDek) {\n // Already caught by the anti-privilege-escalation loop above.\n // This branch is defensive belt-and-braces; if it ever fires,\n // the target had a collection the caller's deks Map disagrees\n // with — fail loud rather than silently dropping access.\n throw new PrivilegeEscalationError(coll)\n }\n wrappedDeks[coll] = await wrapKey(callerDek, newKek)\n }\n\n // 6. Build the recovered keyring file. Identity preserved; wrapping\n // refreshed; tier-2 slots dropped (they wrap the OLD KEK and\n // can't survive a tier-1 phrase change — same precedent as\n // rotatePassphrase). Mint a fresh canary under newKek; the\n // OLD canary on the spread `...target` would fail to verify against\n // the new KEK and trip KeyringCorruptError on next load.\n const canary = await mintKeyringCanary(newKek)\n const next: KeyringFile = {\n ...target,\n _noydb_keyring: NOYDB_KEYRING_VERSION,\n role: targetRole,\n display_name: options.displayName ?? target.display_name,\n deks: wrappedDeks,\n salt: bufferToBase64(newSalt),\n granted_by: callerKeyring.userId,\n authenticators: [],\n canary,\n }\n\n // 7. Single atomic write — overwrites the existing envelope.\n // Backend `put` is the canonical write primitive across every\n // `to-*` store; no partial-failure window between revoke + grant.\n const envelope = {\n _noydb: 1 as const,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(next),\n }\n await store.put(vault, '_keyring', options.userId, envelope)\n}\n","/**\n * Tier-2 authenticator slot management.\n *\n * Each slot independently wraps the SAME KEK under a method-specific\n * derived key (LUKS pattern). Enrolling adds a slot; removing drops\n * one. Both are constant-time keyring writes — no DEK re-keying.\n *\n * The crypto for each method lives in its `@noy-db/on-*` package\n * (`on-webauthn`, `on-oidc`, `on-password`); this module accepts the\n * package's `wrapped_kek` ciphertext + `meta` payload and persists it.\n *\n * @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/session-tiers.md → Tier 2 — Authenticate\n *\n * @module\n */\nimport type { NoydbStore, KeyringAuthenticator } from '../../kernel/types.js'\nimport { NoAccessError, ValidationError } from '../../kernel/errors.js'\nimport type { UnlockedKeyring } from './keyring.js'\nimport { persistKeyring } from './keyring.js'\n\n/** Fields shared across both wrap-KEK and wrap-DEKs enroll inputs. */\ninterface EnrollAuthenticatorBase {\n readonly id: string\n readonly method: KeyringAuthenticator['method']\n /** Method-specific metadata (cred id, salt, …). */\n readonly meta: Record<string, unknown>\n /** Tier the active session held when enrolling. Defaults to 1. */\n readonly enrolled_via_tier?: 1 | 2\n}\n\n/** Wrap-KEK enroll input (WebAuthn, OIDC). */\nexport interface EnrollAuthenticatorWrappingKEKOptions extends EnrollAuthenticatorBase {\n /** Already-wrapped KEK ciphertext (base64) — produced by the on-* package. */\n readonly wrapped_kek: string\n readonly wrapKind?: 'kek'\n}\n\n/** Wrap-DEKs enroll input (password, future on-* using the unified wrap-DEKs primitive). */\nexport interface EnrollAuthenticatorWrappingDEKsOptions extends EnrollAuthenticatorBase {\n readonly wrapKind: 'deks'\n /** Base64 AES-GCM ciphertext of `{ deks: { collection: base64rawDek } }`. */\n readonly wrapped_deks: string\n /** Base64 AES-GCM IV used for the `wrapped_deks` ciphertext. */\n readonly iv: string\n}\n\n/** Discriminated union over the two enroll input shapes. */\nexport type EnrollAuthenticatorOptions =\n | EnrollAuthenticatorWrappingKEKOptions\n | EnrollAuthenticatorWrappingDEKsOptions\n\n/**\n * Append a new authenticator slot to the keyring file. Throws\n * `ValidationError` if a slot with the same id already exists — the\n * caller decides whether to remove + re-enroll.\n *\n * Accepts either wrap-KEK (WebAuthn, OIDC) or wrap-DEKs (password)\n * input. The variant is preserved verbatim into `KeyringAuthenticator`.\n */\nexport async function enrollAuthenticator(\n store: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n options: EnrollAuthenticatorOptions,\n): Promise<UnlockedKeyring> {\n const existing = keyring.authenticators.find((a) => a.id === options.id)\n if (existing) {\n throw new ValidationError(\n `enrollAuthenticator: slot id \"${options.id}\" already exists in vault \"${vault}\". ` +\n 'Remove the slot first or pick a unique id.',\n )\n }\n\n const base = {\n id: options.id,\n method: options.method,\n enrolled_at: new Date().toISOString(),\n enrolled_via_tier: options.enrolled_via_tier ?? 1,\n meta: options.meta,\n } as const\n\n const slot: KeyringAuthenticator = options.wrapKind === 'deks'\n ? {\n ...base,\n wrapKind: 'deks',\n wrapped_deks: options.wrapped_deks,\n iv: options.iv,\n }\n : {\n ...base,\n wrapped_kek: options.wrapped_kek,\n }\n\n const next = appendSlot(keyring, slot)\n await persistKeyring(store, vault, next)\n return next\n}\n\n/**\n * Caller payload for {@link updateAuthenticator}. Mutates only\n * `meta` — the slot's id, method, and wrap material are immutable\n * through this primitive, preserving the anti-slot-swap guard.\n *\n * `meta` is **merged** at the top level: keys absent from the patch\n * are preserved, keys present overwrite. To clear a meta key, pass\n * `null` for that key explicitly. (Same top-level merge semantics as\n * `UserApi.updateMe`, non-recursive — meta is a flat label bag.)\n */\nexport interface UpdateAuthenticatorOptions {\n readonly meta?: Record<string, unknown>\n}\n\n/**\n * Mutate a tier-2 authenticator slot's `meta` blob (slot rename,\n * label changes). The slot's `id`, `method`, and wrap material\n * (`wrapped_kek` for wrap-KEK; `wrapped_deks` + `iv` for wrap-DEKs)\n * are immutable through this entry point — the anti-slot-swap guard\n * is structural, not gate-driven, so even if the policy gate is\n * weakened a future caller cannot use this path to swap one slot's\n * crypto for another's.\n *\n * `meta` patch semantics:\n * - Top-level merge — absent keys preserved, present keys overwrite\n * - `null` value — delete that meta key\n * - Non-object values (string, number, boolean, array) — replace verbatim\n *\n * @throws `NoAccessError` when no slot with the given id exists.\n * @throws `ValidationError` when no patch field is provided.\n *\n */\nexport async function updateAuthenticator(\n store: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n slotId: string,\n options: UpdateAuthenticatorOptions,\n): Promise<UnlockedKeyring> {\n if (options.meta === undefined) {\n throw new ValidationError(\n `updateAuthenticator: at least one of meta must be provided ` +\n `(slotId: \"${slotId}\").`,\n )\n }\n\n const idx = keyring.authenticators.findIndex((a) => a.id === slotId)\n if (idx === -1) {\n throw new NoAccessError(\n `updateAuthenticator: slot \"${slotId}\" not found in vault \"${vault}\".`,\n )\n }\n const existing = keyring.authenticators[idx]!\n\n // Merge at the top level. Absent keys preserved (non-recursive —\n // meta is a flat label bag in practice, no consumer nests it).\n const mergedMeta: Record<string, unknown> = { ...existing.meta }\n for (const [k, v] of Object.entries(options.meta)) {\n if (v === undefined) continue // skip\n if (v === null) {\n delete mergedMeta[k]\n continue\n }\n mergedMeta[k] = v\n }\n\n // Reconstruct the slot preserving wrapKind discrimination. The\n // immutable fields (id, method, wrapped_kek / wrapped_deks + iv,\n // enrolled_at, enrolled_via_tier) all flow through ...existing.\n const next: KeyringAuthenticator = { ...existing, meta: mergedMeta }\n const nextSlots = [...keyring.authenticators]\n nextSlots[idx] = next\n\n const nextKeyring: UnlockedKeyring = {\n ...keyring,\n authenticators: nextSlots,\n }\n await persistKeyring(store, vault, nextKeyring)\n return nextKeyring\n}\n\n/**\n * Drop a slot by id. No-op if the slot doesn't exist (idempotent —\n * removing a non-existent slot is a recoverable retry, not an error).\n */\nexport async function removeAuthenticator(\n store: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n slotId: string,\n): Promise<UnlockedKeyring> {\n const filtered = keyring.authenticators.filter((a) => a.id !== slotId)\n if (filtered.length === keyring.authenticators.length) {\n return keyring // idempotent — nothing to do\n }\n const next: UnlockedKeyring = {\n ...keyring,\n authenticators: filtered,\n }\n await persistKeyring(store, vault, next)\n return next\n}\n\n/**\n * Look up a slot by id. Returns `undefined` when no slot matches.\n * Used by tier-2 unlock dispatchers to fetch the wrapped KEK + meta\n * before invoking the method-specific verifier.\n */\nexport function findAuthenticator(\n keyring: UnlockedKeyring,\n slotId: string,\n): KeyringAuthenticator | undefined {\n return keyring.authenticators.find((a) => a.id === slotId)\n}\n\nfunction appendSlot(\n keyring: UnlockedKeyring,\n slot: KeyringAuthenticator,\n): UnlockedKeyring {\n return {\n ...keyring,\n authenticators: [...keyring.authenticators, slot],\n }\n}\n"],"mappings":"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","names":["deks"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/kernel/sync-policy.ts"],"sourcesContent":["/**\n * Sync scheduling policy.\n *\n * ## What it controls\n *\n * A {@link SyncPolicy} has two halves:\n * - **push** ({@link PushPolicy}) — when dirty local writes are sent to the remote.\n * - **pull** ({@link PullPolicy}) — when the remote is polled for new data.\n *\n * ## Choosing a policy\n *\n * The right policy depends on the backend's operational characteristics:\n *\n * | Backend type | Recommended policy |\n * |---|---|\n * | Per-record (DynamoDB, S3, IDB) | {@link INDEXED_STORE_POLICY} — `on-change` push, `manual` pull |\n * | Bundle (Drive, WebDAV, Git) | {@link POD_STORE_POLICY} — `debounce` push, `interval` pull |\n *\n * Consumers can override via `createNoydb({ syncPolicy: { ... } })`:\n *\n * ```ts\n * const db = await createNoydb({\n * store: jsonFile({ dir: './data' }),\n * syncPolicy: {\n * push: { mode: 'debounce', debounceMs: 5_000 },\n * pull: { mode: 'on-focus' },\n * },\n * })\n * ```\n *\n * ## Scheduler lifecycle\n *\n * {@link SyncScheduler} owns all timers, debounce logic, and browser lifecycle\n * hooks (`visibilitychange`, `pagehide`, `beforeExit`). Call `scheduler.start()`\n * after opening a vault and `scheduler.stop()` when closing it. The scheduler\n * delegates actual push/pull work to {@link SyncSchedulerCallbacks} provided\n * by the {@link SyncEngine}.\n *\n * @module\n */\n\n// ─── Policy types ───────────────────────────────────────────────────────\n\n/**\n * When push operations are triggered automatically.\n *\n * - `'manual'` — only on explicit `sync.push()` calls.\n * - `'on-change'` — immediately after every local write (respecting `minIntervalMs`).\n * - `'debounce'` — after `debounceMs` of inactivity following a write.\n * - `'interval'` — on a fixed timer regardless of writes.\n */\nexport type PushMode = 'manual' | 'on-change' | 'debounce' | 'interval'\n\n/**\n * When pull operations are triggered automatically.\n *\n * - `'manual'` — only on explicit `sync.pull()` calls.\n * - `'interval'` — on a fixed `intervalMs` timer.\n * - `'on-focus'` — when the browser tab regains visibility.\n */\nexport type PullMode = 'manual' | 'interval' | 'on-focus'\n\n/**\n * Push half of a sync policy. Controls the trigger mode and timing guards\n * for outbound sync operations.\n */\nexport interface PushPolicy {\n /** Push trigger mode. */\n readonly mode: PushMode\n /** Debounce delay in ms. Only used when `mode: 'debounce'`. Default: 30_000. */\n readonly debounceMs?: number\n /** Interval in ms between automatic pushes. Used by `'interval'` and as floor for `'debounce'`. */\n readonly intervalMs?: number\n /**\n * Hard floor between pushes regardless of mode. Prevents burst writes\n * from hammering the remote. Default: 0 (no floor).\n */\n readonly minIntervalMs?: number\n /**\n * Force a push on page unload (`pagehide` / `visibilitychange → hidden`)\n * in browsers, `beforeExit` in Node. Default: true for non-manual modes.\n */\n readonly onUnload?: boolean\n}\n\n/**\n * Pull half of a sync policy. Controls when and how often inbound sync\n * operations are triggered.\n */\nexport interface PullPolicy {\n /** Pull trigger mode. */\n readonly mode: PullMode\n /** Interval in ms between automatic pulls. Used by `'interval'` mode. Default: 60_000. */\n readonly intervalMs?: number\n}\n\n/**\n * Combined push + pull sync scheduling policy for a vault.\n *\n * Pass via `createNoydb({ syncPolicy })` to override the default policy\n * derived from the active store type. Pre-built defaults are available\n * as `INDEXED_STORE_POLICY` and `POD_STORE_POLICY`.\n */\nexport interface SyncPolicy {\n readonly push: PushPolicy\n readonly pull: PullPolicy\n}\n\n// ─── Default policies by store category ─────────────────────────────────\n\n/** Default for per-record stores (DynamoDB, S3, file, IDB). */\nexport const INDEXED_STORE_POLICY: SyncPolicy = {\n push: { mode: 'on-change', minIntervalMs: 0, onUnload: true },\n pull: { mode: 'manual' },\n}\n\n/** Default for bundle stores (Drive, WebDAV, Git). */\nexport const POD_STORE_POLICY: SyncPolicy = {\n push: { mode: 'debounce', debounceMs: 30_000, minIntervalMs: 120_000, onUnload: true },\n pull: { mode: 'interval', intervalMs: 60_000 },\n}\n\n/** @deprecated Use `POD_STORE_POLICY`. */\nexport const BUNDLE_STORE_POLICY = POD_STORE_POLICY\n\n// ─── Sync scheduler ─────────────────────────────────────────────────────\n\n/**\n * Current operational state of the `SyncScheduler`.\n *\n * - `'idle'` — no pending or active sync operations.\n * - `'pending'` — local writes are queued, waiting for debounce/interval to fire.\n * - `'pushing'` — push in progress.\n * - `'pulling'` — pull in progress.\n * - `'error'` — last sync operation failed; `lastError` holds the cause.\n */\nexport type SyncSchedulerState = 'idle' | 'pending' | 'pushing' | 'pulling' | 'error'\n\n/**\n * Snapshot of the sync scheduler's state, returned by `SyncScheduler.status`.\n * Safe to expose in a reactive UI status indicator.\n */\nexport interface SyncSchedulerStatus {\n readonly state: SyncSchedulerState\n readonly lastPushAt: string | null\n readonly lastPullAt: string | null\n readonly lastError: Error | null\n readonly pendingWrites: number\n}\n\n/**\n * Callbacks injected into `SyncScheduler` by the SyncEngine.\n *\n * The scheduler owns timers and lifecycle hooks; it delegates actual push/pull\n * work to these callbacks to stay decoupled from the sync implementation.\n */\nexport interface SyncSchedulerCallbacks {\n push(): Promise<void>\n pull(): Promise<void>\n getDirtyCount(): number\n}\n\n/**\n * Manages sync timing according to a `SyncPolicy`.\n *\n * The scheduler owns all timers and lifecycle hooks. It delegates actual\n * push/pull work to callbacks provided by the SyncEngine.\n */\nexport class SyncScheduler {\n private readonly policy: SyncPolicy\n private readonly callbacks: SyncSchedulerCallbacks\n\n private _state: SyncSchedulerState = 'idle'\n private _lastPushAt: string | null = null\n private _lastPullAt: string | null = null\n private _lastError: Error | null = null\n private _lastPushTime = 0 // monotonic ms for minIntervalMs enforcement\n\n // Timers\n private debounceTimer: ReturnType<typeof setTimeout> | null = null\n private pushIntervalTimer: ReturnType<typeof setInterval> | null = null\n private pullIntervalTimer: ReturnType<typeof setInterval> | null = null\n\n // Bound handlers for cleanup\n private readonly boundOnVisibilityChange: (() => void) | null = null\n private readonly boundOnBeforeExit: (() => void) | null = null\n private readonly boundOnPageHide: (() => void) | null = null\n\n private started = false\n\n constructor(policy: SyncPolicy, callbacks: SyncSchedulerCallbacks) {\n this.policy = policy\n this.callbacks = callbacks\n\n // Pre-bind handlers\n if (this.shouldRegisterUnload()) {\n this.boundOnVisibilityChange = this.handleVisibilityChange.bind(this)\n this.boundOnPageHide = this.handlePageHide.bind(this)\n this.boundOnBeforeExit = this.handleBeforeExit.bind(this)\n }\n }\n\n /** Current scheduler status snapshot. */\n get status(): SyncSchedulerStatus {\n return {\n state: this._state,\n lastPushAt: this._lastPushAt,\n lastPullAt: this._lastPullAt,\n lastError: this._lastError,\n pendingWrites: this.callbacks.getDirtyCount(),\n }\n }\n\n /** Start the scheduler — registers timers, event listeners. */\n start(): void {\n if (this.started) return\n this.started = true\n\n // Push: interval mode\n if (this.policy.push.mode === 'interval' && this.policy.push.intervalMs) {\n this.pushIntervalTimer = setInterval(() => {\n void this.executePush()\n }, this.policy.push.intervalMs)\n }\n\n // Pull: interval mode\n if (this.policy.pull.mode === 'interval' && this.policy.pull.intervalMs) {\n this.pullIntervalTimer = setInterval(() => {\n void this.executePull()\n }, this.policy.pull.intervalMs)\n }\n\n // Pull: on-focus mode\n if (this.policy.pull.mode === 'on-focus' && typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', this.handleFocusPull)\n }\n\n // Unload hooks\n if (this.shouldRegisterUnload()) {\n if (typeof document !== 'undefined' && this.boundOnVisibilityChange) {\n document.addEventListener('visibilitychange', this.boundOnVisibilityChange)\n }\n if (typeof globalThis.addEventListener === 'function' && this.boundOnPageHide) {\n globalThis.addEventListener('pagehide', this.boundOnPageHide)\n }\n if (typeof process !== 'undefined' && this.boundOnBeforeExit) {\n process.on('beforeExit', this.boundOnBeforeExit)\n }\n }\n }\n\n /** Stop the scheduler — clears timers, removes event listeners. */\n stop(): void {\n if (!this.started) return\n this.started = false\n\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer)\n this.debounceTimer = null\n }\n if (this.pushIntervalTimer) {\n clearInterval(this.pushIntervalTimer)\n this.pushIntervalTimer = null\n }\n if (this.pullIntervalTimer) {\n clearInterval(this.pullIntervalTimer)\n this.pullIntervalTimer = null\n }\n\n // Focus pull\n if (this.policy.pull.mode === 'on-focus' && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.handleFocusPull)\n }\n\n // Unload hooks\n if (typeof document !== 'undefined' && this.boundOnVisibilityChange) {\n document.removeEventListener('visibilitychange', this.boundOnVisibilityChange)\n }\n if (typeof globalThis.removeEventListener === 'function' && this.boundOnPageHide) {\n globalThis.removeEventListener('pagehide', this.boundOnPageHide)\n }\n if (typeof process !== 'undefined' && this.boundOnBeforeExit) {\n process.removeListener('beforeExit', this.boundOnBeforeExit)\n }\n }\n\n /**\n * Notify the scheduler that a local write occurred.\n * For `on-change` mode: triggers immediate push (respecting minIntervalMs).\n * For `debounce` mode: resets the debounce timer.\n * For `manual` / `interval`: no-op.\n */\n notifyChange(): void {\n if (!this.started) return\n\n if (this.policy.push.mode === 'on-change') {\n void this.executePush()\n } else if (this.policy.push.mode === 'debounce') {\n this.resetDebounce()\n }\n }\n\n /** Force an immediate push, bypassing the scheduler. */\n async forcePush(): Promise<void> {\n await this.executePush()\n }\n\n /** Force an immediate pull, bypassing the scheduler. */\n async forcePull(): Promise<void> {\n await this.executePull()\n }\n\n // ─── Internal ─────────────────────────────────────────────────────\n\n private async executePush(): Promise<void> {\n if (this._state === 'pushing') return // already in progress\n\n // minIntervalMs enforcement\n const minInterval = this.policy.push.minIntervalMs ?? 0\n if (minInterval > 0) {\n const elapsed = Date.now() - this._lastPushTime\n if (elapsed < minInterval) {\n // Schedule for later if debounce mode\n if (this.policy.push.mode === 'debounce') {\n this.scheduleDebounce(minInterval - elapsed)\n }\n return\n }\n }\n\n // Nothing to push\n if (this.callbacks.getDirtyCount() === 0) {\n this._state = 'idle'\n return\n }\n\n this._state = 'pushing'\n try {\n await this.callbacks.push()\n this._lastPushAt = new Date().toISOString()\n this._lastPushTime = Date.now()\n this._lastError = null\n this._state = this.callbacks.getDirtyCount() > 0 ? 'pending' : 'idle'\n } catch (err) {\n this._lastError = err instanceof Error ? err : new Error(String(err))\n this._state = 'error'\n }\n }\n\n private async executePull(): Promise<void> {\n if (this._state === 'pulling') return\n\n const previousState = this._state\n this._state = 'pulling'\n try {\n await this.callbacks.pull()\n this._lastPullAt = new Date().toISOString()\n this._lastError = null\n this._state = previousState === 'pending' ? 'pending' : 'idle'\n } catch (err) {\n this._lastError = err instanceof Error ? err : new Error(String(err))\n this._state = 'error'\n }\n }\n\n private resetDebounce(): void {\n if (this.debounceTimer) clearTimeout(this.debounceTimer)\n const ms = this.policy.push.debounceMs ?? 30_000\n this._state = 'pending'\n this.scheduleDebounce(ms)\n }\n\n private scheduleDebounce(ms: number): void {\n if (this.debounceTimer) clearTimeout(this.debounceTimer)\n this.debounceTimer = setTimeout(() => {\n this.debounceTimer = null\n void this.executePush()\n }, ms)\n }\n\n private shouldRegisterUnload(): boolean {\n const onUnload = this.policy.push.onUnload\n if (onUnload !== undefined) return onUnload\n return this.policy.push.mode !== 'manual'\n }\n\n // ─── Event handlers ───────────────────────────────────────────────\n\n private handleVisibilityChange(): void {\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') {\n this.fireUnloadPush()\n }\n }\n\n private handlePageHide(): void {\n this.fireUnloadPush()\n }\n\n private handleBeforeExit(): void {\n this.fireUnloadPush()\n }\n\n private handleFocusPull = (): void => {\n if (typeof document !== 'undefined' && document.visibilityState === 'visible') {\n void this.executePull()\n }\n }\n\n private fireUnloadPush(): void {\n if (this.callbacks.getDirtyCount() === 0) return\n // Best-effort synchronous-ish push on unload\n void this.callbacks.push().catch(() => {})\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/kernel/constants.ts","../src/kernel/errors.ts"],"sourcesContent":["/**\n * Hub-core constants that must be referenceable without pulling any\n * service chunk. Kept import-free.\n */\n\n/** Reserved fleet-wide control-plane vault name. Hub reserves it for an outward orchestration framework's state/control-plane vault. */\nexport const STATE_VAULT_NAME = '__noydb_state__'\n\n/**\n * Soft cap on the JSON-serialized user-envelope payload size. Generous (a\n * typical profile + preferences + small app annex is ~1 KiB); rejects\n * accidental \"stuff app state in here\" anti-patterns.\n */\nexport const USER_ENVELOPE_MAX_BYTES = 64 * 1024\n\n/**\n * Reserved store collection name for user envelopes. Starts with `_` so the\n * keyring grant machinery propagates the DEK to every granted user via the\n * existing system-collection DEK propagation path in `team/keyring.ts`.\n */\nexport const USER_ENVELOPE_COLLECTION = '_users'\n","/**\n * All NOYDB error classes — a single import surface for `catch` blocks and\n * `instanceof` checks.\n *\n * ## Class hierarchy\n *\n * ```\n * Error\n * └─ NoydbError (code: string)\n * ├─ Crypto errors\n * │ ├─ DecryptionError — AES-GCM tag failure\n * │ ├─ TamperedError — ciphertext modified after write\n * │ └─ InvalidKeyError — wrong passphrase / corrupt keyring\n * ├─ Access errors\n * │ ├─ NoAccessError — no DEK for this collection\n * │ ├─ ReadOnlyError — ro permission, write attempted\n * │ ├─ PermissionDeniedError — role too low for operation\n * │ ├─ PrivilegeEscalationError — grant wider than grantor holds\n * │ └─ StoreCapabilityError — optional store method missing\n * ├─ Sync errors\n * │ ├─ ConflictError — optimistic-lock version mismatch\n * │ ├─ PodVersionConflictError — bundle push rejected by remote\n * │ └─ NetworkError — push/pull network failure\n * ├─ Data errors\n * │ ├─ NotFoundError — get(id) on missing record\n * │ ├─ ValidationError — application-level guard failed\n * │ └─ SchemaValidationError — Standard Schema v1 rejection\n * ├─ Query errors\n * │ ├─ JoinTooLargeError — join row ceiling exceeded\n * │ ├─ CrossJoinTooLargeError — cross-join row ceiling exceeded\n * │ ├─ CrossJoinSourceUnknownError — target collection not in vault\n * │ ├─ DanglingReferenceError — strict ref() points at nothing\n * │ ├─ GroupCardinalityError — groupBy bucket cap exceeded\n * │ ├─ IndexRequiredError — lazy-mode query touches unindexed field\n * │ ├─ IndexWriteFailureError — index side-car put/delete failed post-main\n * │ ├─ UniqueConstraintError — duplicate value on unique index\n * │ ├─ UnsupportedIndexOptionError — unique+lazy or unique+crdt at registration\n * │ └─ FieldNotQueryableError — field's Via posture is queryable: 'none'\n * ├─ i18n / Dictionary errors\n * │ ├─ ReservedCollectionNameError\n * │ ├─ DictKeyMissingError\n * │ ├─ DictKeyInUseError\n * │ ├─ RestrictRefUnresolvableError — restrict edge's compare-key unresolvable (#654)\n * │ ├─ MissingTranslationError\n * │ ├─ LocaleNotSpecifiedError\n * │ ├─ ScriptViolationError\n * │ ├─ StaticDictReadonlyError\n * │ ├─ UnknownDictCodeError\n * │ ├─ UnknownLookupKeyError\n * │ └─ TranslatorNotConfiguredError\n * ├─ Backup errors\n * │ ├─ BackupLedgerError — hash-chain verification failed\n * │ └─ BackupCorruptedError — envelope hash mismatch in dump\n * ├─ Bundle errors\n * │ └─ BundleIntegrityError — .noydb body sha256 mismatch\n * ├─ Session errors\n * │ ├─ SessionExpiredError\n * │ ├─ SessionNotFoundError\n * │ └─ SessionPolicyError\n * ├─ Snapshot errors\n * │ └─ SnapshotNotFoundError — snapshot key absent from snapshot store\n * └─ Computed field errors\n * └─ ComputedFieldError — computed function threw during a write\n * └─ Erasure errors\n * └─ ForgetStrategyNotConfiguredError — vault.forget() with no withForgetCascade\n * ├─ Sealed-record errors (record-scoped CEK sealing)\n * │ ├─ SealedRecordExpiredError — sealed CEK binding past expiresAt\n * │ ├─ SealedRecordMismatchError — CEK sealed for record A used on record B\n * │ └─ RecordCekNotFoundError — record missing or no per-record `_cek`\n * └─ Embedding errors\n * ├─ EmbeddingDimMismatchError — stored vector dim ≠ descriptor dim\n * └─ EmbeddingModelMismatchError — stored vector model tag ≠ descriptor model\n * ```\n *\n * ## Catching all NOYDB errors\n *\n * ```ts\n * import { NoydbError, InvalidKeyError, ConflictError } from '@noy-db/hub'\n *\n * try {\n * await vault.unlock(passphrase)\n * } catch (e) {\n * if (e instanceof InvalidKeyError) { showBadPassphraseUI(); return }\n * if (e instanceof NoydbError) { logToSentry(e.code, e); return }\n * throw e // unexpected — re-throw\n * }\n * ```\n *\n * @module\n */\n\nimport { USER_ENVELOPE_MAX_BYTES } from './constants.js'\nimport type { GateName, GatePolicy } from './types.js'\n\n/**\n * Base class for all NOYDB errors.\n *\n * Every error thrown by `@noy-db/hub` extends this class, so consumers can\n * catch all NOYDB errors in a single `catch (e) { if (e instanceof NoydbError) ... }`\n * block. The `code` field is a machine-readable string (e.g. `'DECRYPTION_FAILED'`)\n * suitable for `switch` statements and logging pipelines.\n */\nexport class NoydbError extends Error {\n /** Machine-readable error code. Stable across library versions. */\n readonly code: string\n\n constructor(code: string, message: string) {\n super(message)\n this.name = 'NoydbError'\n this.code = code\n }\n}\n\n// ─── Debug-mode Errors ─────────────────────────────────────────────────\n\n/**\n * Thrown at construction when `debugPlaintext: true` is combined with\n * encryption (`encrypt` not `false`). Debug-plaintext writes records in\n * cleartext laid out for native store inspection; it is meaningless and\n * unsafe under encryption, so the coupling is rejected loudly rather than\n * silently ignored.\n */\nexport class DebugPlaintextError extends NoydbError {\n constructor(message = 'debugPlaintext requires encrypt: false') {\n super('DEBUG_PLAINTEXT_REQUIRES_UNENCRYPTED', message)\n this.name = 'DebugPlaintextError'\n }\n}\n\n/**\n * Thrown when a record written under `debugPlaintext` carries a top-level\n * field whose name starts with `_`. Debug mode inlines record fields beside\n * the reserved `_`-prefixed envelope metadata, so a `_`-prefixed record field\n * would collide with metadata. The `_` namespace is reserved by NOYDB\n * regardless; rename the field.\n */\nexport class DebugReservedFieldError extends NoydbError {\n constructor(collection: string, field: string) {\n super(\n 'DEBUG_RESERVED_FIELD',\n `Record in \"${collection}\" has reserved field \"${field}\": the _ prefix is reserved under debugPlaintext mode`,\n )\n this.name = 'DebugReservedFieldError'\n }\n}\n\n// ─── Crypto Errors ─────────────────────────────────────────────────────\n\n/**\n * Thrown when AES-GCM decryption fails.\n *\n * The most common cause is a wrong passphrase or a corrupted ciphertext.\n * A `DecryptionError` at the wrong passphrase level is caught internally\n * and re-thrown as `InvalidKeyError` — so in practice this surfaces for\n * per-record corruption rather than authentication failures.\n */\nexport class DecryptionError extends NoydbError {\n constructor(message = 'Decryption failed') {\n super('DECRYPTION_FAILED', message)\n this.name = 'DecryptionError'\n }\n}\n\n/**\n * Thrown when GCM tag verification fails, indicating the ciphertext was\n * modified after encryption.\n *\n * AES-256-GCM is authenticated encryption — the tag over the ciphertext\n * is checked on every decrypt. If any byte was flipped (accidental\n * corruption or deliberate tampering), decryption throws this error.\n * Treat it as a security alert: the stored bytes are not what NOYDB wrote.\n */\nexport class TamperedError extends NoydbError {\n constructor(message = 'Data integrity check failed — record may have been tampered with') {\n super('TAMPERED', message)\n this.name = 'TamperedError'\n }\n}\n\n/**\n * Thrown when key unwrapping fails, typically because the passphrase is wrong\n * or the keyring file is corrupted.\n *\n * NOYDB uses AES-KW (RFC 3394) to wrap DEKs with the KEK. If AES-KW\n * unwrapping fails, it means either the KEK was derived from the wrong\n * passphrase (PBKDF2 with 600K iterations) or the keyring bytes are\n * corrupted. This is the error shown to the user on a failed unlock attempt.\n */\nexport class InvalidKeyError extends NoydbError {\n constructor(message = 'Invalid key — wrong passphrase or corrupted keyring') {\n super('INVALID_KEY', message)\n this.name = 'InvalidKeyError'\n }\n}\n\n/**\n * Thrown when a keyring's wrapped-DEK set unwraps partially — at least\n * one DEK succeeds (proving the KEK is correct) but at least one fails.\n * The passphrase is right; the failed entries are corrupted.\n *\n * This is distinct from {@link InvalidKeyError} so that\n * `NoydbOptions.onInvalidKey: 'reset'` does NOT fire — resetting on\n * partial corruption would destroy the still-valid DEKs and the data\n * they protect, which is silent data loss in response to a feature\n * designed for stale-credential recovery.\n */\nexport class KeyringCorruptError extends NoydbError {\n readonly failedCollections: readonly string[]\n readonly intactCount: number\n constructor(opts: { failedCollections: readonly string[]; intactCount: number; message?: string }) {\n super(\n 'KEYRING_CORRUPT',\n opts.message ??\n `Keyring has ${opts.failedCollections.length} corrupted wrapped DEK(s) ` +\n `(${opts.failedCollections.join(', ')}); ${opts.intactCount} other DEK(s) ` +\n `unwrapped successfully — the passphrase is correct, the entries are damaged. ` +\n `Do NOT use onInvalidKey: 'reset' here — that would destroy the intact DEKs.`,\n )\n this.name = 'KeyringCorruptError'\n this.failedCollections = opts.failedCollections\n this.intactCount = opts.intactCount\n }\n}\n\n// ─── Access Errors ─────────────────────────────────────────────────────\n\n/**\n * Thrown when the authenticated user does not have a DEK for the requested\n * collection — i.e. the collection is not in their keyring at all.\n *\n * This is the \"no key for this door\" error. It is different from\n * `ReadOnlyError` (user has a key but it only grants ro) and from\n * `PermissionDeniedError` (user's role doesn't allow the operation).\n */\nexport class NoAccessError extends NoydbError {\n constructor(message = 'No access — user does not have a key for this collection') {\n super('NO_ACCESS', message)\n this.name = 'NoAccessError'\n }\n}\n\n/**\n * Thrown when a user with read-only (`ro`) permission attempts a write\n * operation (`put` or `delete`) on a collection.\n *\n * The user has a DEK for the collection (they can decrypt and read), but\n * their keyring grants only `ro`. To fix: re-grant the user with `rw`\n * permission, or do not attempt writes as a viewer/client role.\n */\nexport class ReadOnlyError extends NoydbError {\n constructor(message = 'Read-only — user has ro permission on this collection') {\n super('READ_ONLY', message)\n this.name = 'ReadOnlyError'\n }\n}\n\n/**\n * Thrown when a write is attempted against a historical view produced\n * by `vault.at(timestamp)`. Time-machine views are read-only by\n * contract — mutating the past would require either the shadow-vault\n * mechanism or a ledger-history rewrite (which breaks\n * the tamper-evidence guarantee).\n *\n * Distinct from {@link ReadOnlyError} (keyring-level) and\n * {@link PermissionDeniedError} (role-level): this error is about the\n * *view* being historical, independent of the caller's permissions.\n */\nexport class ReadOnlyAtInstantError extends NoydbError {\n constructor(operation: string, timestamp: string) {\n super(\n 'READ_ONLY_AT_INSTANT',\n `Cannot ${operation}() on a vault view anchored at ${timestamp} — time-machine views are read-only`,\n )\n this.name = 'ReadOnlyAtInstantError'\n }\n}\n\n/**\n * Thrown when a write is attempted against a shadow-vault frame\n * produced by `vault.frame()`. Frames are read-only by contract —\n * the use case is screen-sharing / demos / compliance review where\n * the operator wants to prevent accidental edits.\n *\n * Behavioural enforcement only — the underlying keyring still holds\n * write-capable DEKs. See {@link VaultFrame} for the full caveat.\n */\nexport class ReadOnlyFrameError extends NoydbError {\n constructor(operation: string) {\n super(\n 'READ_ONLY_FRAME',\n `Cannot ${operation}() on a vault frame — frames are read-only presentations of the current vault`,\n )\n this.name = 'ReadOnlyFrameError'\n }\n}\n\n/**\n * Thrown when the authenticated user's role does not permit the requested\n * operation — e.g. a `viewer` calling `grantAccess()`, or an `operator`\n * calling `rotateKeys()`.\n *\n * This is a role-level check (what the user's role allows), distinct from\n * `NoAccessError` (collection not in keyring) and `ReadOnlyError` (in\n * keyring, but write not allowed).\n */\nexport class PermissionDeniedError extends NoydbError {\n constructor(message = 'Permission denied — insufficient role for this operation') {\n super('PERMISSION_DENIED', message)\n this.name = 'PermissionDeniedError'\n }\n}\n\n/**\n * Thrown when an `@noy-db/as-*` export is attempted without the\n * required capability bit on the invoking keyring.\n *\n * Two sub-cases discriminated by the `tier` field:\n *\n * - `tier: 'plaintext'` — a plaintext-tier export (`as-xlsx`,\n * `as-csv`, `as-blob`, `as-zip`, …) was attempted but the\n * keyring's `exportCapability.plaintext` does not include the\n * requested `format` (nor the `'*'` wildcard). Default for every\n * role is `plaintext: []` — the owner must positively grant.\n * - `tier: 'bundle'` — an encrypted `as-noydb` bundle export was\n * attempted but the keyring's `exportCapability.bundle` is\n * `false`. Default for `owner`/`admin` is `true`; for\n * `operator`/`viewer`/`client` it is `false`.\n *\n * Distinct from `PermissionDeniedError` (role-level check) and\n * `NoAccessError` (collection not readable). Surfaces separately so\n * UI layers can show a \"request the export capability from your\n * admin\" flow rather than a generic permission error.\n */\nexport class ExportCapabilityError extends NoydbError {\n readonly tier: 'plaintext' | 'bundle'\n readonly format?: string\n readonly userId: string\n\n constructor(opts: {\n tier: 'plaintext' | 'bundle'\n userId: string\n format?: string\n message?: string\n }) {\n const msg =\n opts.message ??\n (opts.tier === 'plaintext'\n ? `Export capability denied — keyring \"${opts.userId}\" is not granted plaintext-export capability for format \"${opts.format ?? '<unknown>'}\". Ask a vault owner or admin to grant it via vault.grant({ exportCapability: { plaintext: ['${opts.format ?? '<format>'}'] } }).`\n : `Export capability denied — keyring \"${opts.userId}\" is not granted encrypted-bundle export capability. Ask a vault owner or admin to grant it via vault.grant({ exportCapability: { bundle: true } }).`)\n super('EXPORT_CAPABILITY', msg)\n this.name = 'ExportCapabilityError'\n this.tier = opts.tier\n this.userId = opts.userId\n if (opts.format !== undefined) this.format = opts.format\n }\n}\n\n/**\n * Thrown when a keyring file's `expires_at` cutoff has passed.\n * Surfaced by `loadKeyring` before any DEK unwrap is attempted —\n * past the cutoff the slot refuses to open even with the right\n * passphrase. Distinct from PBKDF2 / unwrap errors so consumer code\n * can show a precise \"this bundle slot has expired\" message instead\n * of the generic decryption-failure UX.\n *\n * Used predominantly on `BundleRecipient` slots produced by\n * `writePod({ recipients: [...] })` to time-box audit access.\n */\nexport class KeyringExpiredError extends NoydbError {\n readonly userId: string\n readonly expiresAt: string\n constructor(opts: { userId: string; expiresAt: string }) {\n super(\n 'KEYRING_EXPIRED',\n `Keyring \"${opts.userId}\" expired at ${opts.expiresAt}. ` +\n 'The slot refuses to unlock past its expiry timestamp.',\n )\n this.name = 'KeyringExpiredError'\n this.userId = opts.userId\n this.expiresAt = opts.expiresAt\n }\n}\n\n/**\n * Thrown when an `@noy-db/as-*` import is attempted but the invoking\n * keyring lacks the required import-capability bit.\n *\n * - `tier: 'plaintext'` — a plaintext-tier import (`as-csv`, `as-json`,\n * `as-ndjson`, `as-zip`, …) was attempted but the keyring's\n * `importCapability.plaintext` does not include the requested\n * `format` (nor the `'*'` wildcard).\n * - `tier: 'bundle'` — a `.noydb` bundle import was attempted but the\n * keyring's `importCapability.bundle` is not `true`.\n *\n * Default for every role on every dimension is closed — owners and\n * admins must positively grant the capability. Distinct from\n * `PermissionDeniedError` and `NoAccessError` so UI layers can show a\n * specific \"request the import capability\" flow.\n */\nexport class ImportCapabilityError extends NoydbError {\n readonly tier: 'plaintext' | 'bundle'\n readonly format?: string\n readonly userId: string\n\n constructor(opts: {\n tier: 'plaintext' | 'bundle'\n userId: string\n format?: string\n message?: string\n }) {\n const msg =\n opts.message ??\n (opts.tier === 'plaintext'\n ? `Import capability denied — keyring \"${opts.userId}\" is not granted plaintext-import capability for format \"${opts.format ?? '<unknown>'}\". Ask a vault owner or admin to grant it via vault.grant({ importCapability: { plaintext: ['${opts.format ?? '<format>'}'] } }).`\n : `Import capability denied — keyring \"${opts.userId}\" is not granted encrypted-bundle import capability. Ask a vault owner or admin to grant it via vault.grant({ importCapability: { bundle: true } }).`)\n super('IMPORT_CAPABILITY', msg)\n this.name = 'ImportCapabilityError'\n this.tier = opts.tier\n this.userId = opts.userId\n if (opts.format !== undefined) this.format = opts.format\n }\n}\n\n/**\n * Thrown when a grant would give the grantee a permission the grantor\n * does not themselves hold — the \"admin cannot grant what admin cannot\n * do\" rule from the admin-delegation work.\n *\n * Distinct from `PermissionDeniedError` so callers can tell the two\n * cases apart in logs and tests:\n *\n * - `PermissionDeniedError` — \"you are not allowed to perform this\n * operation at all\" (wrong role).\n * - `PrivilegeEscalationError` — \"you are allowed to grant, but not\n * with these specific permissions\" (widening attempt).\n *\n * Under the admin model the grantee of an admin-grants-admin call\n * inherits the caller's entire DEK set by construction, so this error\n * is structurally unreachable in typical flows. The check and error\n * class exist so that future per-collection admin scoping cannot\n * accidentally bypass the subset rule — the guard is already wired in.\n *\n * `offendingCollection` carries the first collection name that failed\n * the subset check, to make the violation actionable in error output.\n */\n/**\n * Thrown when a caller invokes an API that requires an optional\n * store capability the active store does not implement.\n *\n * Today the only call site is `Noydb.listAccessibleVaults()`,\n * which depends on the optional `NoydbStore.listVaults()`\n * method. The error message names the missing method and the calling\n * API so consumers know exactly which combination is unsupported,\n * and the `capability` field is machine-readable so library code can\n * pattern-match in catch blocks (e.g. fall back to a candidate-list\n * shape).\n *\n * The class lives in `errors.ts` rather than as a generic\n * `ValidationError` because the diagnostic shape is different: a\n * `ValidationError` says \"the inputs you passed are wrong\"; this\n * error says \"the inputs are fine, but the store you wired up\n * doesn't support what you're asking for.\" Different fix, different\n * documentation.\n */\nexport class StoreCapabilityError extends NoydbError {\n /** The store method/capability that was missing. */\n readonly capability: string\n\n constructor(capability: string, callerApi: string, storeName?: string) {\n super(\n 'STORE_CAPABILITY',\n `${callerApi} requires the optional store capability \"${capability}\" ` +\n `but the active store${storeName ? ` (${storeName})` : ''} does not implement it. ` +\n `Use a store that supports \"${capability}\" (store-memory, store-file) or pass an explicit ` +\n `vault list to bypass enumeration.`,\n )\n this.name = 'StoreCapabilityError'\n this.capability = capability\n }\n}\n\nexport class PrivilegeEscalationError extends NoydbError {\n readonly offendingCollection: string\n\n constructor(offendingCollection: string, message?: string) {\n super(\n 'PRIVILEGE_ESCALATION',\n message ??\n `Privilege escalation: grantor has no DEK for collection \"${offendingCollection}\" and cannot grant access to it.`,\n )\n this.name = 'PrivilegeEscalationError'\n this.offendingCollection = offendingCollection\n }\n}\n\n/**\n * Thrown when a reserved internal vault name (e.g. `__noydb_state__`) is used\n * as a group name or partition key.\n *\n * Internal vault names are prefixed or surrounded with double-underscores to\n * avoid collisions with user-defined vault names. Attempting to use one as a\n * group name or partition key bypasses the naming policy and is rejected\n * eagerly so the mis-configuration is surfaced immediately.\n */\nexport class ReservedVaultNameError extends NoydbError {\n /** The rejected vault name. */\n readonly vaultName: string\n\n constructor(vaultName: string) {\n super(\n 'RESERVED_VAULT_NAME',\n `\"${vaultName}\" is a reserved internal vault name and cannot be used as a group name or partition key`,\n )\n this.name = 'ReservedVaultNameError'\n this.vaultName = vaultName\n }\n}\n\n/**\n * Thrown by `Collection.put` / `.delete` when the target record's\n * envelope `_ts` falls within a closed accounting period.\n *\n * Distinct from `ReadOnlyError` (keyring-level), `ReadOnlyAtInstantError`\n * (historical view), and `ReadOnlyFrameError` (shadow vault): this\n * error is about the STORED RECORD being sealed by an operator call\n * to `vault.closePeriod()`, independent of caller permissions or\n * view type. The `periodName` and `endDate` fields name the sealing\n * period so audit UIs can surface a \"this record is locked in\n * FY2026-Q1 (closed 2026-03-31)\" message without parsing the error\n * string.\n *\n * To apply a correction after close, book a compensating entry in a\n * new period rather than unlocking the old one. Re-opening a closed\n * period is deliberately unsupported.\n */\nexport class PeriodClosedError extends NoydbError {\n readonly periodName: string\n readonly endDate: string\n readonly recordTs: string\n\n constructor(periodName: string, endDate: string, recordTs: string) {\n super(\n 'PERIOD_CLOSED',\n `Cannot modify record (last written ${recordTs}) — sealed by closed period ` +\n `\"${periodName}\" (endDate: ${endDate}). Post a compensating entry in a ` +\n `new period instead.`,\n )\n this.name = 'PeriodClosedError'\n this.periodName = periodName\n this.endDate = endDate\n this.recordTs = recordTs\n }\n}\n\n/**\n * Thrown when a `put()` or `delete()` is rejected by a guard's `check`\n * function. The `reason` is the message the guard supplied — typically a\n * short business description (e.g. \"invoice is issued\"). The full\n * collection + id are surfaced so audit UIs can link back to the record.\n */\nexport class RecordLockedError extends NoydbError {\n readonly collection: string\n readonly id: string\n readonly reason: string\n\n constructor(collection: string, id: string, reason: string) {\n super(\n 'RECORD_LOCKED',\n `Cannot modify ${collection}/${id} — locked by guard: ${reason}. ` +\n `Use withTransactions({ amendment: true, reason }) with admin/owner role to override.`,\n )\n this.name = 'RecordLockedError'\n this.collection = collection\n this.id = id\n this.reason = reason\n }\n}\n\n/**\n * Thrown when a `put()` changes one or more fields that are frozen by a\n * `frozenFields` guard. The `fields` list contains the specific paths\n * that were detected as changed.\n */\nexport class FieldFrozenError extends NoydbError {\n readonly collection: string\n readonly id: string\n readonly fields: readonly string[]\n\n constructor(collection: string, id: string, fields: readonly string[]) {\n super(\n 'FIELD_FROZEN',\n `Cannot change frozen field(s) on ${collection}/${id}: ${fields.join(', ')}. ` +\n `Use withTransactions({ amendment: true, reason }) with admin/owner role to override.`,\n )\n this.name = 'FieldFrozenError'\n this.collection = collection\n this.id = id\n this.fields = fields\n }\n}\n\n/**\n * Thrown by a `transitionGuard` when a write moves a state field along an\n * arc that the declared transition graph does not allow — either an\n * update `from → to` that is not a listed edge, or an insert whose\n * initial state is not in the allowed `initial` set (reported with\n * `from: '(none)'`). Override via an amendment transaction by an\n * authorized role, like any guard.\n */\nexport class IllegalTransitionError extends NoydbError {\n readonly collection: string\n readonly id: string\n readonly from: string\n readonly to: string\n\n constructor(collection: string, id: string, from: string, to: string) {\n super(\n 'ILLEGAL_TRANSITION',\n `Cannot transition ${collection}/${id} from \"${from}\" to \"${to}\" — not a declared arc. ` +\n `Use withTransactions({ amendment: true, reason }) with admin/owner role to override.`,\n )\n this.name = 'IllegalTransitionError'\n this.collection = collection\n this.id = id\n this.from = from\n this.to = to\n }\n}\n\n/**\n * Thrown by an amendment invariant when the proposed change-set violates\n * the declared business rule (e.g. disbursement total not preserved).\n * Triggers a full transaction rollback via the existing revert pass.\n */\nexport class InvariantError extends NoydbError {\n constructor(message: string) {\n super('INVARIANT_VIOLATED', message)\n this.name = 'InvariantError'\n }\n}\n\n/**\n * Thrown at `withTransactions({ amendment: true })` open if the caller's\n * role is not in the guard's allowed amendment roles. Fail-fast: thrown\n * before any writes are attempted.\n */\nexport class AmendmentForbiddenError extends NoydbError {\n readonly userId: string\n readonly role: string\n\n constructor(userId: string, role: string) {\n super(\n 'AMENDMENT_FORBIDDEN',\n `User \"${userId}\" with role \"${role}\" cannot open an amendment transaction. ` +\n `Amendments require admin or owner role.`,\n )\n this.name = 'AmendmentForbiddenError'\n this.userId = userId\n this.role = role\n }\n}\n\n/**\n * Thrown by `listUsersWithEnvelopes` when the vault's user directory\n * has been disabled (via `db.setDirectoryEnabled(vault, false)`) and\n * the caller's role is neither `owner` nor `admin`. Owner/admin can\n * still enumerate users — the toggle is a UX privacy switch, not a\n * security boundary.\n *\n * Honest caveat: this is a UX flag, not a privacy guarantee. The\n * envelope ciphertext is still in the store, the keyring file is\n * still listed at `_keyring/*`, and anyone with direct store read\n * access can count keyrings without going through the hub. See\n * `https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/user-envelope.md` → \"Directory visibility\".\n */\nexport class DirectoryDisabledError extends NoydbError {\n readonly vault: string\n\n constructor(vault: string) {\n super(\n 'DIRECTORY_DISABLED',\n `Vault \"${vault}\" has its user directory disabled. ` +\n `Only owners and admins can call listUsersWithEnvelopes() here. ` +\n `Use db.setDirectoryEnabled(vault, true) to re-enable.`,\n )\n this.name = 'DirectoryDisabledError'\n this.vault = vault\n }\n}\n\n// ─── Hierarchical Access Errors ─────────────────────\n\n/**\n * Thrown when a user tries to act at a tier they are not cleared for.\n *\n * This is the umbrella error for tier write refusals:\n * - `put({ tier: N })` when the user's keyring lacks tier-N DEK.\n * - `elevate(id, N)` when the caller cannot reach tier N.\n *\n * Distinct from `TierAccessDeniedError` which covers *read* refusals on\n * the invisibility/ghost path.\n */\nexport class TierNotGrantedError extends NoydbError {\n readonly tier: number\n readonly collection: string\n\n constructor(collection: string, tier: number) {\n super(\n 'TIER_NOT_GRANTED',\n `User has no DEK for tier ${tier} in collection \"${collection}\"`,\n )\n this.name = 'TierNotGrantedError'\n this.collection = collection\n this.tier = tier\n }\n}\n\n/**\n * Thrown when a tier-0 `put()` or `delete()` targets a record whose LIVE\n * envelope is elevated (`_tier > 0`) — regardless of whether the caller\n * holds the tier's DEK. `put()`/`delete()` are the tier-0 write APIs;\n * the sanctioned way to write an elevated record is `putAtTier()`,\n * `elevate()`, or `demote()`.\n *\n * Distinct from `TierNotGrantedError`, which means \"no DEK for tier N\"\n * and refuses only non-holders. This error refuses HOLDERS too — the\n * problem isn't clearance, it's that `put()`/`delete()` are the wrong\n * API for an already-elevated record (a tier-0 `put()` over one would\n * otherwise silently demote it; a tier-0 `delete()` would write a\n * marker with no `_tier`, erasing the elevation signal). It is also\n * distinct from the read-path invisibility gate (`liveRecordIsElevated`\n * in `tier-visibility.ts`), which throws nothing — elevated records\n * simply read as absent there. This is a write-path refusal, not a\n * read-path ghost.\n *\n * #708: also raised by the coordinated-cutover pre-check\n * (`assertCutoverTierSafe`) — a bulk-rewrite is a third tier-0 write path,\n * so it gets the same refusal with a `detail` override naming the record.\n */\nexport class TierWriteRefusedError extends NoydbError {\n readonly tier: number\n readonly collection: string\n\n constructor(collection: string, tier: number, detail?: string) {\n super(\n 'TIER_WRITE_REFUSED',\n detail ?? `put()/delete() cannot write to record in collection \"${collection}\" — ` +\n `it is elevated to tier ${tier}. Use putAtTier()/elevate()/demote() instead.`,\n )\n this.name = 'TierWriteRefusedError'\n this.collection = collection\n this.tier = tier\n }\n}\n\n/**\n * Thrown at `vault.collection()` registration when `tiers` is declared\n * together with a derived-artifact feature whose crypto has not yet been\n * made tier-aware (elevate()/demote() do not re-key it), so an elevated\n * record's data would stay readable at tier 0. Mirrors\n * `UnsupportedIndexOptionError` — the refusal happens loudly at\n * registration instead of leaking silently at rest.\n *\n * `feature` names the incompatible feature (e.g. `'blobs'`) so catch\n * blocks can pattern-match without inspecting the error message.\n */\nexport class UnsupportedTierCompositionError extends NoydbError {\n readonly feature: string\n constructor(feature: string, message: string) {\n super('UNSUPPORTED_TIER_COMPOSITION', message)\n this.name = 'UnsupportedTierCompositionError'\n this.feature = feature\n }\n}\n\n/**\n * Thrown by `createIntent` (blob durability journal, #753 spec §7 C8) when a\n * `_blob_intent` marker already exists for `{collection}::{recordId}` — the\n * CAS create-if-absent (`expectedVersion: 0`) lost to a present row. A\n * present marker means a shred or rehome is already in flight for this\n * record and MUST be resumed before any new intent is minted (overwriting it\n * would orphan the prior op's op-stamps — spec C8). Callers catch this and\n * resume the pending marker first, then retry.\n */\nexport class BlobIntentPendingError extends NoydbError {\n readonly collection: string\n readonly recordId: string\n constructor(collection: string, recordId: string) {\n super(\n 'BLOB_INTENT_PENDING',\n `A blob durability marker is already pending for \"${collection}::${recordId}\" — ` +\n `resume it before starting a new shred/rehome.`,\n )\n this.name = 'BlobIntentPendingError'\n this.collection = collection\n this.recordId = recordId\n }\n}\n\n/**\n * Thrown when an elevated-handle operation runs after the elevation's\n * TTL expired. Reads continue at the original tier; only writes\n * through the scoped handle flip to throwing once expired.\n */\nexport class ElevationExpiredError extends NoydbError {\n readonly tier: number\n readonly expiresAt: number\n\n constructor(opts: { tier: number; expiresAt: number }) {\n super(\n 'ELEVATION_EXPIRED',\n `Elevation to tier ${opts.tier} expired at ${new Date(opts.expiresAt).toISOString()}`,\n )\n this.name = 'ElevationExpiredError'\n this.tier = opts.tier\n this.expiresAt = opts.expiresAt\n }\n}\n\n/**\n * Thrown by `vault.elevate(...)` when an elevation is already active\n * on the vault. Adopters must `release()` the existing handle before\n * starting a new elevation.\n */\nexport class AlreadyElevatedError extends NoydbError {\n readonly activeTier: number\n\n constructor(activeTier: number) {\n super(\n 'ALREADY_ELEVATED',\n `Vault is already elevated to tier ${activeTier}; release the existing handle first`,\n )\n this.name = 'AlreadyElevatedError'\n this.activeTier = activeTier\n }\n}\n\n/**\n * Thrown when `demote()` is called by someone who is not the original\n * elevator and not an owner.\n */\nexport class TierDemoteDeniedError extends NoydbError {\n constructor(id: string, tier: number) {\n super(\n 'TIER_DEMOTE_DENIED',\n `Only the original elevator or an owner can demote record \"${id}\" from tier ${tier}`,\n )\n this.name = 'TierDemoteDeniedError'\n }\n}\n\n/**\n * Thrown when `db.delegate()` is called against a user that has no\n * keyring in the target vault — the delegation token cannot be\n * constructed without the target user's KEK wrap.\n */\nexport class DelegationTargetMissingError extends NoydbError {\n readonly toUser: string\n\n constructor(toUser: string) {\n super(\n 'DELEGATION_TARGET_MISSING',\n `Delegation target user \"${toUser}\" has no keyring in this vault`,\n )\n this.name = 'DelegationTargetMissingError'\n this.toUser = toUser\n }\n}\n\n// ─── Sync Errors ───────────────────────────────────────────────────────\n\n/**\n * Thrown when a `put()` detects an optimistic concurrency conflict.\n *\n * NOYDB uses version numbers (`_v`) for optimistic locking. If a `put()`\n * is called with `expectedVersion: N` but the stored record is at version\n * `M ≠ N`, the write is rejected and the caller must re-read, re-apply their\n * change, and retry. The `version` field carries the actual stored version\n * so callers can decide whether to retry or surface the conflict to the user.\n */\nexport class ConflictError extends NoydbError {\n /** The actual stored version at the time of conflict. */\n readonly version: number\n\n constructor(version: number, message = 'Version conflict') {\n super('CONFLICT', message)\n this.name = 'ConflictError'\n this.version = version\n }\n}\n\n/**\n * Thrown by `LedgerStore.append()` after exhausting its CAS retry\n * budget under multi-writer contention. Two browser tabs, a\n * web app + an offline mobile peer, or a server worker pool all\n * producing ledger entries against the same vault can race on the\n * \"read head, write head+1\" cycle; the optimistic-CAS retry loop\n * resolves the race for `casAtomic: true` stores, but pathological\n * contention (or a buggy peer) can still exhaust the budget. When\n * that happens, the chain is intact — the failed writer simply\n * couldn't claim a slot. Caller's choice whether to retry, queue,\n * or surface the failure to the user.\n */\nexport class LedgerContentionError extends NoydbError {\n readonly attempts: number\n\n constructor(attempts: number) {\n super(\n 'LEDGER_CONTENTION',\n `LedgerStore.append: failed to claim a chain slot after ${attempts} optimistic-CAS retries`,\n )\n this.name = 'LedgerContentionError'\n this.attempts = attempts\n }\n}\n\n/**\n * Thrown by `vault.sequence(name).next()` after exhausting its CAS retry\n * budget under contention. The counter is intact; the caller may retry.\n */\nexport class SequenceContentionError extends NoydbError {\n readonly sequence: string\n readonly attempts: number\n\n constructor(sequence: string, attempts: number) {\n super(\n 'SEQUENCE_CONTENTION',\n `vault.sequence(\"${sequence}\").next(): failed to allocate after ${attempts} optimistic-CAS retries`,\n )\n this.name = 'SequenceContentionError'\n this.sequence = sequence\n this.attempts = attempts\n }\n}\n\n/**\n * Thrown by `vault.sequence(name).next()` when the backing store is not\n * CAS-capable (`capabilities.casAtomic !== true`). Gap-free numbering\n * requires single-authority serialization, which an offline / non-CAS\n * store cannot provide — this is a deliberate online-only wall.\n */\nexport class SequenceOfflineError extends NoydbError {\n constructor() {\n super(\n 'SEQUENCE_OFFLINE',\n 'vault.sequence().next() requires an online CAS-capable store ' +\n '(capabilities.casAtomic). Gap-free numbering cannot be serialized offline.',\n )\n this.name = 'SequenceOfflineError'\n }\n}\n\n/**\n * Thrown by `vault.sequence()` when the atomic-sequence capability was not\n * opted into (the default `NO_SEQUENCE` stub). Sequence is an opt-in,\n * tree-shakeable capability: enable it with `sequenceStrategy: withSequence()`\n * from \"@noy-db/hub\" in createNoydb(). Deferred-numbering series\n * (`withDeferredNumbering`) are a separate capability and are unaffected.\n */\nexport class SequenceNotEnabledError extends NoydbError {\n constructor(\n message = 'vault.sequence() requires the sequence capability. Pass ' +\n '`sequenceStrategy: withSequence()` to createNoydb().',\n ) {\n super('SEQUENCE_NOT_ENABLED', message)\n this.name = 'SequenceNotEnabledError'\n }\n}\n\n/** Thrown by a deferred-numbering pass when the store clock is unavailable or its uncertainty cannot be resolved. */\nexport class NumberingUncertaintyError extends NoydbError {\n readonly series: string\n constructor(series: string) {\n super(\n 'NUMBERING_UNCERTAINTY',\n `Deferred numbering for series \"${series}\" cannot run: the store does not expose getStoreTime() ` +\n `(capabilities.serverWriteTime). Use a CAS sequence or a store with serverWriteTime.`,\n )\n this.name = 'NumberingUncertaintyError'\n this.series = series\n }\n}\n\n/**\n * Thrown when a bundle push is rejected because the remote has been updated\n * since the local bundle was last pulled.\n *\n * Unlike `ConflictError` (per-record), this is a whole-bundle conflict —\n * the remote's bundle handle has changed. The caller must pull the new\n * bundle, merge, and re-push. `remoteVersion` is the handle of the newer\n * remote bundle for use in diagnostics.\n */\nexport class PodVersionConflictError extends NoydbError {\n /** The bundle handle of the newer remote version that rejected the push. */\n readonly remoteVersion: string\n\n constructor(remoteVersion: string, message = 'Bundle version conflict — remote has been updated') {\n super('BUNDLE_VERSION_CONFLICT', message)\n this.name = 'PodVersionConflictError'\n this.remoteVersion = remoteVersion\n }\n}\n\n/** @deprecated Use `PodVersionConflictError`. */\nexport const BundleVersionConflictError = PodVersionConflictError\n/** @deprecated Use `PodVersionConflictError`. */\nexport type BundleVersionConflictError = PodVersionConflictError\n\n/**\n * Thrown when a sync operation (push or pull) fails due to a network error.\n *\n * NOYDB's offline-first design means network errors are expected during sync.\n * Callers should catch `NetworkError`, surface connectivity status in the UI,\n * and rely on the `SyncScheduler` to retry when connectivity is restored.\n */\nexport class NetworkError extends NoydbError {\n constructor(message = 'Network error') {\n super('NETWORK_ERROR', message)\n this.name = 'NetworkError'\n }\n}\n\n// ─── Data Errors ───────────────────────────────────────────────────────\n\n/**\n * Thrown when `collection.get(id)` is called with an ID that does not exist.\n *\n * NOYDB collections are memory-first, so this error is synchronous and cheap —\n * it does not make a network round-trip. Callers that expect the record to be\n * absent should use `collection.getOrNull(id)` instead.\n */\nexport class NotFoundError extends NoydbError {\n constructor(message = 'Record not found') {\n super('NOT_FOUND', message)\n this.name = 'NotFoundError'\n }\n}\n\n/**\n * Thrown when application-level validation fails before encryption.\n *\n * Distinct from `SchemaValidationError` (Standard Schema v1 validator)\n * and `MissingTranslationError` (i18nText). `ValidationError` is the\n * general-purpose validation base — use it for custom guards in `put()`\n * hooks or store middleware.\n */\nexport class ValidationError extends NoydbError {\n constructor(message = 'Validation error') {\n super('VALIDATION_ERROR', message)\n this.name = 'ValidationError'\n }\n}\n\n/**\n * Thrown when a Standard Schema v1 validator rejects a record on\n * `put()` (input validation) or on read (output validation). Carries\n * the raw issue list so callers can render field-level errors.\n *\n * `direction` distinguishes the two cases:\n * - `'input'`: the user passed bad data into `put()`. This is a\n * normal error case that application code should handle — typically\n * by showing validation messages in the UI.\n * - `'output'`: stored data does not match the current schema. This\n * indicates a schema drift (the schema was changed without\n * migrating the existing records) and should be treated as a bug\n * — the application should not swallow it silently.\n *\n * The `issues` type is deliberately `readonly unknown[]` on this class\n * so that `errors.ts` doesn't need to import from `schema.ts` (and\n * create a dependency cycle). Callers who know they're holding a\n * `SchemaValidationError` can cast to the more precise\n * `readonly StandardSchemaV1Issue[]` from `schema.ts`.\n */\nexport class SchemaValidationError extends NoydbError {\n readonly issues: readonly unknown[]\n readonly direction: 'input' | 'output'\n\n constructor(\n message: string,\n issues: readonly unknown[],\n direction: 'input' | 'output',\n ) {\n super('SCHEMA_VALIDATION_FAILED', message)\n this.name = 'SchemaValidationError'\n this.issues = issues\n this.direction = direction\n }\n}\n\n/** Base for schema-evolution strategy rejections. */\nexport class SchemaUpdateError extends NoydbError {\n constructor(code: string, message: string) {\n super(code, message)\n this.name = 'SchemaUpdateError'\n }\n}\n\n/** A non-additive schema change was rejected by the `additiveOnly()` strategy. */\nexport class NonAdditiveSchemaChangeError extends SchemaUpdateError {\n constructor(message: string) {\n super('NON_ADDITIVE_SCHEMA_CHANGE', message)\n this.name = 'NonAdditiveSchemaChangeError'\n }\n}\n\n/** A schema change was rejected by the `lockSchema()` strategy. */\nexport class SchemaLockedError extends SchemaUpdateError {\n constructor(message: string) {\n super('SCHEMA_LOCKED', message)\n this.name = 'SchemaLockedError'\n }\n}\n\n/** Write attempted while a schema cutover fence is up (draining/migrating, or this collection has a pending cutover). */\nexport class SchemaFenceError extends SchemaUpdateError {\n constructor(message: string) {\n super('SCHEMA_FENCE', message)\n this.name = 'SchemaFenceError'\n }\n}\n\n/** Write attempted by a client whose generation snapshot is behind the live fence — reload required. */\nexport class MigrationRequiredError extends SchemaUpdateError {\n constructor(message: string) {\n super('MIGRATION_REQUIRED', message)\n this.name = 'MigrationRequiredError'\n }\n}\n\n/** A coordinated cutover timed out waiting for active clients to quiesce. */\nexport class QuiesceTimeoutError extends SchemaUpdateError {\n constructor(message: string) {\n super('QUIESCE_TIMEOUT', message)\n this.name = 'QuiesceTimeoutError'\n }\n}\n\n// ─── Query DSL Errors ─────────────────────────────────────────────────\n\n/**\n * Thrown when `.groupBy().aggregate()` produces more than the hard\n * cardinality cap (default 100_000 groups)..\n *\n * The cap exists because `.groupBy()` materializes one bucket per\n * distinct key value in memory, and runaway cardinality — a groupBy\n * on a high-uniqueness field like `id` or `createdAt` — is almost\n * always a query mistake rather than legitimate use. A hard error is\n * better than silent OOM: the consumer sees an actionable message\n * naming the field and the observed cardinality, with guidance to\n * either narrow the query with `.where()` or accept the ceiling\n * override.\n *\n * A separate one-shot warning fires at 10% of the cap (10_000\n * groups) so consumers get a heads-up before the hard error — same\n * pattern as `JoinTooLargeError` and the `.join()` row ceiling.\n *\n * **Not overridable in.** The 100k cap is a fixed constant so\n * the failure mode is consistent across the codebase; a\n * `{ maxGroups }` override can be added later without a break if a\n * real consumer asks.\n */\nexport class GroupCardinalityError extends NoydbError {\n /** The field being grouped on. */\n readonly field: string\n /** Observed number of distinct groups at the moment the cap tripped. */\n readonly cardinality: number\n /** The cap that was exceeded. */\n readonly maxGroups: number\n\n constructor(field: string, cardinality: number, maxGroups: number) {\n super(\n 'GROUP_CARDINALITY',\n `.groupBy(\"${field}\") produced ${cardinality} distinct groups, ` +\n `exceeding the ${maxGroups}-group ceiling. This is almost always a ` +\n `query mistake — grouping on a high-uniqueness field like \"id\" or ` +\n `\"createdAt\" produces one bucket per record. Narrow the query with ` +\n `.where() before grouping, or group on a lower-cardinality field ` +\n `(status, category, clientId). If you genuinely need high-cardinality ` +\n `grouping, file an issue with your use case.`,\n )\n this.name = 'GroupCardinalityError'\n this.field = field\n this.cardinality = cardinality\n this.maxGroups = maxGroups\n }\n}\n\n/**\n * Thrown in lazy mode when a `.query()` / `.where()` / `.orderBy()` clause\n * references a field that does not have a declared index.\n *\n * Lazy-mode queries only work when every touched field is indexed.\n * This is deliberate — silent scan-fallback would hide the performance\n * cliff that lazy-mode indexes exist to prevent.\n *\n * Payload:\n * - `collection` — name of the collection queried\n * - `touchedFields` — every field referenced by the query (filter + order)\n * - `missingFields` — subset of `touchedFields` that have no declared index\n */\nexport class IndexRequiredError extends NoydbError {\n readonly collection: string\n readonly touchedFields: readonly string[]\n readonly missingFields: readonly string[]\n\n constructor(args: { collection: string; touchedFields: readonly string[]; missingFields: readonly string[] }) {\n super(\n 'INDEX_REQUIRED',\n `Collection \"${args.collection}\": query references unindexed fields in lazy mode ` +\n `(missing: ${args.missingFields.join(', ')}). ` +\n `Declare an index on each field, or use collection.scan() for non-indexed iteration.`,\n )\n this.name = 'IndexRequiredError'\n this.collection = args.collection\n this.touchedFields = [...args.touchedFields]\n this.missingFields = [...args.missingFields]\n }\n}\n\n/**\n * Thrown by `Collection.put()` when writing a record would violate a\n * unique-index constraint — the same field value (or composite field\n * tuple) is already held by a *different* record id in the collection.\n *\n * Properties:\n * - `collection` — name of the collection the write was targeting\n * - `recordId` — the id of the record being written (the would-be violator)\n * - `fields` — the constrained field(s), e.g. `['taxId']` or `['workerId','employerEntityId']`\n * - `conflictingId` — the id of the record already holding the value\n *\n * Null-distinct semantics: if any constrained field is `null`/`undefined`,\n * the row is exempt (the constraint does not fire). This matches standard\n * SQL NULL-distinct behavior.\n */\nexport class UniqueConstraintError extends NoydbError {\n readonly collection: string\n readonly recordId: string\n readonly fields: readonly string[]\n readonly conflictingId: string\n\n constructor(collection: string, recordId: string, fields: readonly string[], conflictingId: string) {\n super(\n 'UNIQUE_CONSTRAINT',\n `Unique constraint on ${collection}.[${fields.join(', ')}] violated: ` +\n `record \"${recordId}\" duplicates a value already held by \"${conflictingId}\".`,\n )\n this.name = 'UniqueConstraintError'\n this.collection = collection\n this.recordId = recordId\n this.fields = fields\n this.conflictingId = conflictingId\n }\n}\n\n/**\n * Thrown at collection registration when an index option is declared that\n * is incompatible with the collection's operating mode.\n *\n * Currently covers two cases:\n * - `unique: true` on a lazy-mode (`prefetch: false`) collection — lazy mode\n * does not pre-load all records, so an in-memory uniqueness map cannot be\n * maintained reliably.\n * - `unique: true` on a CRDT collection (`crdt: 'lww-map' | 'rga' | 'yjs'`) —\n * CRDT put() short-circuits the unique-constraint check, so enforcement would\n * silently not fire.\n *\n * Both cases are caught eagerly at `vault.collection()` time so the developer\n * sees the incompatibility immediately rather than shipping silently-ignored\n * constraints.\n *\n * The `option` field names the incompatible option (`'unique'`) so catch blocks\n * can pattern-match without inspecting the error message.\n */\nexport class UnsupportedIndexOptionError extends NoydbError {\n readonly option: string\n constructor(option: string, message: string) {\n super('UNSUPPORTED_INDEX_OPTION', message)\n this.name = 'UnsupportedIndexOptionError'\n this.option = option\n }\n}\n\n/**\n * Thrown (or surfaced via the `index:write-partial` event) when one or more\n * per-indexed-field side-car writes fail after the main record write has\n * already succeeded.\n *\n * Not thrown out of `.put()` / `.delete()` directly — those succeed when the\n * main record succeeds. Instead, `IndexWriteFailureError` instances are collected\n * into the session-scoped reconcile queue and emitted on the Collection\n * emitter as `index:write-partial`.\n *\n * Payload:\n * - `recordId` — the id of the main record whose side-car writes failed\n * - `field` — the indexed field whose side-car write failed\n * - `op` — `'put'` or `'delete'`, indicating which mutation was in flight\n * - `cause` — the underlying error from the store\n */\nexport class IndexWriteFailureError extends NoydbError {\n readonly recordId: string\n readonly field: string\n readonly op: 'put' | 'delete'\n override readonly cause: unknown\n\n constructor(args: { recordId: string; field: string; op: 'put' | 'delete'; cause: unknown }) {\n super(\n 'INDEX_WRITE_FAILURE',\n `Index side-car ${args.op} failed for field \"${args.field}\" on record \"${args.recordId}\"`,\n )\n this.name = 'IndexWriteFailureError'\n this.recordId = args.recordId\n this.field = args.field\n this.op = args.op\n this.cause = args.cause\n }\n}\n\n/**\n * Thrown when `PersistedIndexStore`'s compensating `remove()` — the undo of a\n * stale debounced `_ftindex` save that raced a purge (#725) — itself fails.\n * That failure is sticky (`pendingCompensation`) and retried-first by every\n * subsequent store entrypoint (`ensureBuilt`/`rebuildAndPersist`/\n * `removePersisted`) rather than silently dropped, but was previously\n * rethrown as the RAW adapter error indefinitely — indistinguishable from\n * any other adapter failure, so a caller could not catch it deliberately the\n * way `forget()` catches `_purgeSearchIndex` into `indexResidue` (#764).\n *\n * `cause` is the underlying adapter error. Callers that want stuck-\n * compensation resilience instead of an abort (e.g. `elevate()`/`demote()`,\n * #764) catch this type specifically and surface it as residue.\n */\nexport class PersistedIndexCompensationError extends NoydbError {\n override readonly cause: unknown\n\n constructor(cause: unknown) {\n super(\n 'PERSISTED_INDEX_COMPENSATION_STUCK',\n 'Persisted search-index compensation is stuck — a compensating remove() of a stale ' +\n '_ftindex blob failed and is being retried by every subsequent store call.',\n )\n this.name = 'PersistedIndexCompensationError'\n this.cause = cause\n }\n}\n\n/**\n * Thrown by `.where()` / `.orderBy()` / `.aggregate()` (via the Via\n * pipeline's `postureFor`/`wrapReducers`) when the field is covered by a Via\n * feature whose declared posture is `queryable: 'none'` — e.g. a `blobFields`\n * slot (blob content is out-of-band; it never reaches the decrypted record,\n * so nothing indexes or compares it). #629 Task 8 — the first posture\n * consumer.\n *\n * Payload:\n * - `field` — the refused field name\n */\nexport class FieldNotQueryableError extends NoydbError {\n readonly field: string\n\n constructor(field: string) {\n super(\n 'FIELD_NOT_QUERYABLE',\n `Field \"${field}\" is not queryable — its Via feature declares queryable: 'none'.`,\n )\n this.name = 'FieldNotQueryableError'\n this.field = field\n }\n}\n\n// ─── Bundle Format Errors ─────────────────────────────────\n\n/**\n * Thrown by `readPod()` when the body bytes don't match\n * the integrity hash declared in the bundle header — i.e. someone\n * modified the bytes between write and read.\n *\n * Distinct from a generic `Error` (which would be thrown for\n * format violations like a missing magic prefix or malformed\n * header JSON) so consumers can pattern-match the corruption case\n * and handle it differently from a producer bug. A\n * `BundleIntegrityError` indicates \"the bytes you got are not\n * what was written\"; a plain `Error` from `parsePrefixAndHeader`\n * indicates \"what was written wasn't a valid bundle in the first\n * place.\"\n *\n * Also thrown when decompression fails after the integrity hash\n * passed — that's a producer bug (the wrong algorithm byte was\n * written) but it surfaces with the same error class because the\n * end result is \"the body cannot be turned back into a dump.\"\n */\nexport class BundleIntegrityError extends NoydbError {\n constructor(message: string) {\n super('BUNDLE_INTEGRITY', `.noydb bundle integrity check failed: ${message}`)\n this.name = 'BundleIntegrityError'\n }\n}\n\n/**\n * Thrown by `readPod` when the bundle carries\n * sealed per-user passphrases but no supplied `SealingKeyProvider`\n * has a `.id` (= `pid`) matching the sealed entry's `pid`.\n *\n * Carries the failing pid + the user id so the recipient can\n * surface an actionable prompt:\n *\n * ```\n * BundleSealMismatchError: bundle carries sealed passphrase for user \"alice\"\n * under provider \"macos-keychain:com.acme.app/alice@acme.example\",\n * but no registered provider matches that pid.\n * ```\n *\n * Three resolution paths the message names (per foundation §11.9.4):\n *\n * 1. Configure a provider matching the pid and retry import.\n * 2. Pass `attemptUnsealAcrossProviders: true` to try each\n * registered provider regardless of pid.\n * 3. Inspect without unsealing — pass no `sealingProviders` to\n * receive the sealed entries unmodified for offline analysis.\n */\nexport class BundleSealMismatchError extends NoydbError {\n readonly userId: string\n readonly pid: string\n constructor(userId: string, pid: string) {\n super(\n 'BUNDLE_SEAL_MISMATCH',\n `bundle carries sealed passphrase for user \"${userId}\" under provider `\n + `\"${pid}\", but no registered provider matches that pid.\\n\\n`\n + 'Resolutions:\\n'\n + ' 1. Configure a provider matching the pid and retry import.\\n'\n + ' 2. Pass `attemptUnsealAcrossProviders: true` to try each registered\\n'\n + ' provider regardless of pid (extra credential prompts may surface).\\n'\n + ' 3. Inspect the bundle without unsealing — pass no `sealingProviders`\\n'\n + ' to receive the sealed entries unmodified for offline analysis.',\n )\n this.name = 'BundleSealMismatchError'\n this.userId = userId\n this.pid = pid\n }\n}\n\n// ─── i18n / Dictionary Errors ──────────────────────────\n\n/**\n * Thrown when `vault.collection()` is called with a name that is\n * reserved for NOYDB internal use (any name starting with `_dict_`).\n *\n * Dictionary collections are accessed exclusively via\n * `vault.dictionary(name)` — attempting to open one as a regular\n * collection would bypass the dictionary invariants (ACL, rename\n * tracking, reserved-name policy).\n */\nexport class ReservedCollectionNameError extends NoydbError {\n /** The rejected collection name. */\n readonly collectionName: string\n\n constructor(collectionName: string) {\n super(\n 'RESERVED_COLLECTION_NAME',\n `\"${collectionName}\" is a reserved collection name. ` +\n `Use vault.dictionary(\"${collectionName.replace(/^_dict_/, '')}\") ` +\n `to access dictionary collections.`,\n )\n this.name = 'ReservedCollectionNameError'\n this.collectionName = collectionName\n }\n}\n\n/**\n * Thrown by `DictionaryHandle.get()` and `DictionaryHandle.delete()` when\n * the requested key does not exist in the dictionary.\n *\n * Distinct from `NotFoundError` (which is for data records) so callers\n * can distinguish \"data record missing\" from \"dictionary key missing\"\n * without inspecting error messages.\n */\nexport class DictKeyMissingError extends NoydbError {\n /** The dictionary name. */\n readonly dictionaryName: string\n /** The key that was not found. */\n readonly key: string\n\n constructor(dictionaryName: string, key: string) {\n super(\n 'DICT_KEY_MISSING',\n `Dictionary \"${dictionaryName}\" has no entry for key \"${key}\".`,\n )\n this.name = 'DictKeyMissingError'\n this.dictionaryName = dictionaryName\n this.key = key\n }\n}\n\n/**\n * Thrown by `DictionaryHandle.delete()` in strict mode when the key to\n * be deleted is still referenced by one or more records.\n *\n * The caller must either rename the key first (the only sanctioned\n * mass-mutation path) or pass `{ mode: 'warn' }` to skip the check\n * (development only).\n */\nexport class DictKeyInUseError extends NoydbError {\n /** The dictionary name. */\n readonly dictionaryName: string\n /** The key that is still referenced. */\n readonly key: string\n /** Name of the first collection found to reference this key. */\n readonly usedBy: string\n /** Number of records in `usedBy` that reference this key. */\n readonly count: number\n\n constructor(\n dictionaryName: string,\n key: string,\n usedBy: string,\n count: number,\n ) {\n super(\n 'DICT_KEY_IN_USE',\n `Cannot delete key \"${key}\" from dictionary \"${dictionaryName}\": ` +\n `${count} record(s) in \"${usedBy}\" still reference it. ` +\n `Use dictionary.rename(\"${key}\", newKey) to rewrite references first.`,\n )\n this.name = 'DictKeyInUseError'\n this.dictionaryName = dictionaryName\n this.key = key\n this.usedBy = usedBy\n this.count = count\n }\n}\n\n/**\n * Thrown by `VaultLinks.checkLookupRefsRestrict()` (#654) when a `restrict`-mode lookup-ref\n * edge's compare-key cannot be resolved from the backing row — a matrix dimension with a\n * non-default `key` whose row is missing that field or holds a non-string/non-number value\n * (corruption-class rarity). Whether the referencer still points at this row can't be proven\n * either way, so the delete/forget is refused rather than silently allowed through — the\n * fail-closed twin of `DictKeyInUseError` (\"cannot prove no references ⇒ do not delete\").\n */\nexport class RestrictRefUnresolvableError extends NoydbError {\n /** The dimension (backing collection/dictionary) whose row's compare-key was unresolvable. */\n readonly dimension: string\n /** The backing row's key (its PUT-id) that was being deleted/forgotten. */\n readonly key: string\n /** The unresolvable restrict edge, formatted `\"collection.field\"`. */\n readonly referencing: string\n\n constructor(dimension: string, key: string, referencing: string) {\n super(\n 'RESTRICT_REF_UNRESOLVABLE',\n `Cannot delete \"${dimension}\" key \"${key}\": the restrict-mode reference from \"${referencing}\" ` +\n `could not be resolved (its compare-key is unreadable on the backing row). Refusing to ` +\n `delete — cannot prove no references exist.`,\n )\n this.name = 'RestrictRefUnresolvableError'\n this.dimension = dimension\n this.key = key\n this.referencing = referencing\n }\n}\n\n/**\n * Thrown by `Collection.put()` when an `i18nText` field is missing one\n * or more required translations.\n *\n * The `missing` array names each locale code that was absent from the\n * field value. The `field` property names the field so callers can\n * render a field-level error message without parsing the string.\n */\nexport class MissingTranslationError extends NoydbError {\n /** The field name whose translation(s) are missing. */\n readonly field: string\n /** Locale codes that were required but absent. */\n readonly missing: readonly string[]\n\n constructor(field: string, missing: readonly string[], message?: string) {\n super(\n 'MISSING_TRANSLATION',\n message ??\n `Field \"${field}\": missing required translation(s): ${missing.join(', ')}.`,\n )\n this.name = 'MissingTranslationError'\n this.field = field\n this.missing = missing\n }\n}\n\n/**\n * Thrown when reading an `i18nText` field without specifying a locale —\n * either at the call site (`get(id, { locale })`) or on the vault\n * (`openVault(name, { locale })`).\n *\n * Also thrown when `resolveI18nText()` exhausts the fallback chain and\n * no translation is available for the requested locale.\n *\n * The `field` property names the field that triggered the error so the\n * caller can surface it in the UI.\n */\nexport class LocaleNotSpecifiedError extends NoydbError {\n /** The field name that required a locale. */\n readonly field: string\n\n constructor(field: string, message?: string) {\n super(\n 'LOCALE_NOT_SPECIFIED',\n message ??\n `Cannot read i18nText field \"${field}\" without a locale. ` +\n `Pass { locale } to get()/list()/query() or set a default via ` +\n `openVault(name, { locale }).`,\n )\n this.name = 'LocaleNotSpecifiedError'\n this.field = field\n }\n}\n\n/**\n * Thrown at write time when an `i18nText` slot's value contains\n * characters outside the script set allowed for that locale, and the\n * field's `onScriptViolation` policy is `'reject'` (the default).\n *\n * Distinct from {@link MissingTranslationError} (write-shape) and\n * {@link LocaleNotSpecifiedError} (read-hole) so callers can tell a\n * wrong-script value from a missing one.\n */\nexport class ScriptViolationError extends NoydbError {\n /** The field whose value violated its script constraint. */\n readonly field: string\n /** The locale slot (e.g. `'en'`) that was checked. */\n readonly locale: string\n /** The Unicode scripts allowed for this slot. */\n readonly expected: readonly string[]\n /** A short sample of the offending characters, for diagnostics. */\n readonly sample: string\n\n constructor(\n field: string,\n locale: string,\n expected: readonly string[],\n sample: string,\n message?: string,\n ) {\n super(\n 'SCRIPT_VIOLATION',\n message ??\n `Field \"${field}\" slot \"${locale}\" expects script(s) [${expected.join(', ')}] ` +\n `but contains disallowed character(s): \"${sample}\".`,\n )\n this.name = 'ScriptViolationError'\n this.field = field\n this.locale = locale\n this.expected = expected\n this.sample = sample\n }\n}\n\n/**\n * Thrown when a mutation (`put`/`putAll`/`rename`/`delete`) is attempted\n * against a dictionary name that is backed by a `staticDict()` descriptor.\n *\n * A static dict's labels are code constants with no per-vault storage and no\n * mutation surface — a label change is a code deploy, not a runtime write.\n * Distinct from the other dictionary errors so callers can tell a\n * \"this dict is read-only by construction\" refusal from a missing-key or\n * key-in-use failure.\n */\nexport class StaticDictReadonlyError extends NoydbError {\n /** The static dictionary name that was the target of the mutation. */\n readonly dictionaryName: string\n\n constructor(dictionaryName: string) {\n super(\n 'STATIC_DICT_READONLY',\n `Dictionary \"${dictionaryName}\" is a staticDict — its labels are code ` +\n `constants with no mutation surface. put/putAll/rename/delete are not ` +\n `supported; change the label in the staticDict() table and redeploy.`,\n )\n this.name = 'StaticDictReadonlyError'\n this.dictionaryName = dictionaryName\n }\n}\n\n/**\n * Thrown at put-time when a record stores a code for a `staticDict()` field\n * that is not in the descriptor's declared `keys` (a typo or a stale code).\n *\n * Codes are closed by construction, so an unknown code is treated as a bug by\n * default. Opt out per descriptor with `{ validateCodes: false }`.\n *\n * Distinct from {@link LocaleNotSpecifiedError} (a read-hole) — this is a\n * write-shape error.\n */\nexport class UnknownDictCodeError extends NoydbError {\n /** The static dictionary name. */\n readonly dictionaryName: string\n /** The field that carried the unknown code. */\n readonly field: string\n /** The offending code value. */\n readonly code: string\n\n constructor(dictionaryName: string, field: string, code: string) {\n super(\n 'UNKNOWN_DICT_CODE',\n `Field \"${field}\": code \"${code}\" is not a known key of staticDict ` +\n `\"${dictionaryName}\". Use a declared code, or pass ` +\n `{ validateCodes: false } on the descriptor to allow open codes.`,\n )\n this.name = 'UnknownDictCodeError'\n this.dictionaryName = dictionaryName\n this.field = field\n this.code = code\n }\n}\n\n/**\n * Thrown at put-time when a `vocabulary: 'closed'` lookup field (#650 Task\n * 3 — `lookup()`/`enumOf()`/`dict()`, the via-lookup binding) carries a key\n * that is not a member of its dimension — the enum tier's declared key set,\n * the dict tier's declared keys / live reserved-collection entries, or the\n * matrix tier's backing collection.\n *\n * Distinct from {@link UnknownDictCodeError} (the `staticDict()` alias's own\n * error, unchanged by this task) — this is the native `lookup`/`dict`/`enum`\n * descriptors' equivalent. `'open'` vocabulary (the default) never throws\n * this; declare `{ vocabulary: 'closed' }` to opt in.\n */\nexport class UnknownLookupKeyError extends NoydbError {\n /** The dimension (dictionary/target collection) name. */\n readonly dimension: string\n /** The field that carried the unknown key. */\n readonly field: string\n /** The offending key value. */\n readonly key: string\n\n constructor(dimension: string, field: string, key: string) {\n super(\n 'UNKNOWN_LOOKUP_KEY',\n `Field \"${field}\": key \"${key}\" is not a known member of the \"${dimension}\" ` +\n `lookup vocabulary (closed). Use a declared/existing key, or declare ` +\n `{ vocabulary: 'open' } to allow unknown keys.`,\n )\n this.name = 'UnknownLookupKeyError'\n this.dimension = dimension\n this.field = field\n this.key = key\n }\n}\n\n// ─── Translator Errors ─────────────────────────────────────\n\n/**\n * Thrown when a collection has an `i18nText` field with\n * `autoTranslate: true` but no `plaintextTranslator` was configured\n * on `createNoydb()`.\n *\n * The error is raised at `put()` time (not at schema construction) so\n * the mis-configuration is surfaced by the first write rather than\n * silently at startup.\n */\nexport class TranslatorNotConfiguredError extends NoydbError {\n /** The field that requested auto-translation. */\n readonly field: string\n /** The collection the put was targeting. */\n readonly collection: string\n\n constructor(field: string, collection: string) {\n super(\n 'TRANSLATOR_NOT_CONFIGURED',\n `Field \"${field}\" in collection \"${collection}\" has autoTranslate: true, ` +\n `but no plaintextTranslator was configured on createNoydb(). ` +\n `Either configure a plaintextTranslator or remove autoTranslate from the schema.`,\n )\n this.name = 'TranslatorNotConfiguredError'\n this.field = field\n this.collection = collection\n }\n}\n\n// ─── Backup Errors ─────────────────────────────────────────\n\n/**\n * Thrown when `Vault.load()` finds that a backup's hash chain\n * doesn't verify, or that its embedded `ledgerHead.hash` doesn't\n * match the chain head reconstructed from the loaded entries.\n *\n * Distinct from `BackupCorruptedError` so callers can choose to\n * recover from one but not the other (e.g., a corrupted JSON file is\n * unrecoverable; a chain mismatch might mean the backup is from an\n * incompatible noy-db version).\n */\nexport class BackupLedgerError extends NoydbError {\n /** First-broken-entry index, if known. */\n readonly divergedAt?: number\n\n constructor(message: string, divergedAt?: number) {\n super('BACKUP_LEDGER', message)\n this.name = 'BackupLedgerError'\n if (divergedAt !== undefined) this.divergedAt = divergedAt\n }\n}\n\n/**\n * Thrown when `Vault.load()` finds that the backup's data\n * collection content doesn't match the ledger's recorded\n * `payloadHash`es. This is the \"envelope was tampered with after\n * dump\" detection — the chain itself can be intact, but if any\n * encrypted record bytes were swapped, this check catches it.\n */\nexport class BackupCorruptedError extends NoydbError {\n /** The (collection, id) pair whose envelope failed the hash check. */\n readonly collection: string\n readonly id: string\n\n constructor(collection: string, id: string, message: string) {\n super('BACKUP_CORRUPTED', message)\n this.name = 'BackupCorruptedError'\n this.collection = collection\n this.id = id\n }\n}\n\n/**\n * Thrown by partition-extraction primitives when the\n * transitive-closure walk fails — e.g. the FK graph is deeper than\n * `maxDepth`, signalling a runaway or unexpectedly cyclic graph.\n */\nexport class PartitionExtractionError extends NoydbError {\n constructor(message: string) {\n super('PARTITION_EXTRACTION', message)\n this.name = 'PartitionExtractionError'\n }\n}\n\n/**\n * Thrown by `adoptPartition` when the transfer seal can't be\n * opened — a wrong/short transfer key (AES-GCM auth-tag failure) or a\n * malformed sealed payload.\n */\nexport class TransferSealError extends NoydbError {\n constructor(message: string) {\n super('TRANSFER_SEAL', message)\n this.name = 'TransferSealError'\n }\n}\n\n/**\n * Thrown when an adoption-lifecycle precondition fails — re-adopting a\n * partition already consumed in this store, or owner-creation on a\n * vault that isn't in the adopted-unowned state.\n */\nexport class AdoptionStateError extends NoydbError {\n constructor(message: string) {\n super('ADOPTION_STATE', message)\n this.name = 'AdoptionStateError'\n }\n}\n\n// ─── Attestation Errors ────────────────────────────────────\n\n/** Document-attestation failures: undeclared field-schema, non-owner issue, missing field, signer failure. */\nexport class AttestationError extends NoydbError {\n constructor(message: string) {\n super('ATTESTATION', message)\n this.name = 'AttestationError'\n }\n}\n\n/**\n * Thrown when an attestation capability method (`issueAttestation`,\n * `getDocumentSigningPublicKey`, `revokeAttestation`, `unrevokeAttestation`,\n * `getRevokedDocIds`, `publishRevocationList`) is called without opting into\n * the attestation capability (the default `NO_ATTESTATION` stub). Attestation\n * is an opt-in, tree-shakeable capability: enable it with\n * `attestationStrategy: withAttestation()` from \"@noy-db/hub/attestation\" in\n * createNoydb().\n */\nexport class AttestationNotEnabledError extends NoydbError {\n constructor(\n message = 'Attestation requires the attestation capability. Pass ' +\n '`attestationStrategy: withAttestation()` from \"@noy-db/hub/attestation\" ' +\n 'to createNoydb().',\n ) {\n super('ATTESTATION_NOT_ENABLED', message)\n this.name = 'AttestationNotEnabledError'\n }\n}\n\n/**\n * Thrown when `collection.reveal()` is called without opting into the\n * classified capability (the default `NO_CLASSIFIED` stub). Classified reveal\n * is an opt-in, tree-shakeable capability: enable it with\n * `classifiedStrategy: withClassified()` from \"@noy-db/hub/classified\" in\n * createNoydb().\n */\nexport class ClassifiedNotEnabledError extends NoydbError {\n constructor(\n message = 'reveal() requires the classified capability. Pass ' +\n '`classifiedStrategy: withClassified()` from \"@noy-db/hub/classified\" ' +\n 'to createNoydb().',\n ) {\n super('CLASSIFIED_NOT_ENABLED', message)\n this.name = 'ClassifiedNotEnabledError'\n }\n}\n\n/**\n * Thrown when a hierarchical-tiers capability method (`putAtTier`, `getAtTier`,\n * `listAtTier`, `elevate`, `demote`) is called without opting into the tiers\n * capability (the default `NO_TIERS` stub). Tiers is an opt-in, tree-shakeable\n * capability: enable it with `tiersStrategy: withTiers()` from\n * \"@noy-db/hub/tiers\" in createNoydb().\n */\nexport class TiersNotEnabledError extends NoydbError {\n constructor(\n message = 'Hierarchical tiers require the tiers capability. Pass ' +\n '`tiersStrategy: withTiers()` from \"@noy-db/hub/tiers\" to createNoydb().',\n ) {\n super('TIERS_NOT_ENABLED', message)\n this.name = 'TiersNotEnabledError'\n }\n}\n\n/**\n * Thrown when a sealed-record grantor method (`sealRecordToHost`,\n * `revokeSealedRecord`, `rotateRecordCek`) is called without opting into the\n * sealed-record capability (the default `NO_SEALED_RECORD` stub). Sealed-record\n * is an opt-in, tree-shakeable capability: enable it with\n * `sealedRecordStrategy: withSealedRecord()` from \"@noy-db/hub/sealed-record\" in\n * createNoydb(). (The host-side `openSealedRecord` opener is ungated.)\n */\nexport class SealedRecordNotEnabledError extends NoydbError {\n constructor(\n message = 'Record-scoped CEK sealing requires the sealed-record capability. Pass ' +\n '`sealedRecordStrategy: withSealedRecord()` from \"@noy-db/hub/sealed-record\" ' +\n 'to createNoydb().',\n ) {\n super('SEALED_RECORD_NOT_ENABLED', message)\n this.name = 'SealedRecordNotEnabledError'\n }\n}\n\n/**\n * Thrown when a portability capability method (`exportMyAccessibleData`,\n * `unilateralWithdrawal`, `requestWithdrawal`, `listWithdrawalRequests`,\n * `approveWithdrawal`, `rejectWithdrawal`) is called without opting into the\n * portability capability (the default `NO_PORTABILITY` stub). Portability is an\n * opt-in, tree-shakeable capability: enable it with\n * `portabilityStrategy: withPortability()` from \"@noy-db/hub/portability\" in\n * createNoydb().\n */\nexport class PortabilityNotEnabledError extends NoydbError {\n constructor(\n message = 'Data portability (export/withdrawal) requires the portability capability. Pass ' +\n '`portabilityStrategy: withPortability()` from \"@noy-db/hub/portability\" to createNoydb().',\n ) {\n super('PORTABILITY_NOT_ENABLED', message)\n this.name = 'PortabilityNotEnabledError'\n }\n}\n\n/**\n * Thrown when a sovereign-custody (FR-6) operation — `db.grantCustodian`,\n * `db.revokeCustodian`, or `vault.custody.liberate()` (and the `vault.custody.*`\n * facade that composes them) — is called without opting into the custody\n * capability (the default `NO_CUSTODY` stub). Custody is an opt-in,\n * tree-shakeable capability: enable it with `custodyStrategy: withCustody()`\n * from \"@noy-db/hub\" in createNoydb(). (The lower-level `liberateVault` free\n * function stays ungated.)\n */\nexport class CustodyNotEnabledError extends NoydbError {\n constructor(\n message = 'Sovereign custody (grant/revoke custodian, liberate) requires the custody ' +\n 'capability. Pass `custodyStrategy: withCustody()` to createNoydb().',\n ) {\n super('CUSTODY_NOT_ENABLED', message)\n this.name = 'CustodyNotEnabledError'\n }\n}\n\n/**\n * Thrown when a multi-user team operation — `db.grant`, `db.revoke`, or\n * `db.rotate` — is called without opting into the team capability (the\n * default `NO_TEAM` stub). The always-on floor is single-user by design\n * (#267 keyring-grant → team split): enable multi-user grant/revoke/rotate\n * with `teamStrategy: withTeam()` from \"@noy-db/hub/team\" in createNoydb().\n * Single-user primitives (owner keyring, unlock, `listUsers`, `updateUser`,\n * passphrase rotate/recover) stay ungated.\n */\nexport class TeamNotEnabledError extends NoydbError {\n constructor(\n message = 'Multi-user grant/revoke/rotate requires the team capability. ' +\n 'Pass `teamStrategy: withTeam()` (from \"@noy-db/hub/team\") to createNoydb().',\n ) {\n super('TEAM_NOT_ENABLED', message)\n this.name = 'TeamNotEnabledError'\n }\n}\n\n/**\n * Thrown when a search / retrieval capability method — `collection.search`,\n * `collection.retrieve`, `collection.similarTo`, `collection.warmIndex`,\n * `collection.flushIndex`, or the put()-time embedding-vector compute for a\n * collection declaring `embeddings` — is called without opting into the search\n * capability (the default `NO_SEARCH` stub). Search is an opt-in,\n * tree-shakeable capability: enable it with `searchStrategy: withSearch()` from\n * \"@noy-db/hub\" in createNoydb(). Opting in also enables embedding compute (a\n * vector no gated retrieval could read would be dead weight).\n */\nexport class SearchNotEnabledError extends NoydbError {\n constructor(\n message = 'Search / retrieval (search, retrieve, similarTo, warmIndex, flushIndex, and ' +\n 'embedding compute) requires the search capability. Pass ' +\n '`searchStrategy: withSearch()` to createNoydb().',\n ) {\n super('SEARCH_NOT_ENABLED', message)\n this.name = 'SearchNotEnabledError'\n }\n}\n\n/**\n * Thrown when the source-side cargo operation — `extractPartition(vault, …)` —\n * is called without opting into the cargo capability (the default `NO_CARGO`\n * stub). Cargo is an opt-in, tree-shakeable capability: enable it with\n * `cargoStrategy: withCargo()` from \"@noy-db/hub/cargo\" in createNoydb(). The\n * recipient-side `adoptPartition` / `decryptExtractedPartition` free functions —\n * and `diffVault` (shared import/merge infra) — operate without a gated source\n * instance and stay ungated.\n */\nexport class CargoNotEnabledError extends NoydbError {\n constructor(\n message = 'Partition extraction (extractPartition) requires the ' +\n 'cargo capability. Pass `cargoStrategy: withCargo()` from \"@noy-db/hub/cargo\" to createNoydb().',\n ) {\n super('CARGO_NOT_ENABLED', message)\n this.name = 'CargoNotEnabledError'\n }\n}\n\n// ─── Broker Errors (#479 credential broker) ───────────────\n\n/**\n * Thrown when `vault.broker()` is called without opting into the broker\n * capability (the default `NO_BROKER` stub). Opt in with\n * `brokerStrategy: withBroker(config)` from \"@noy-db/hub/broker\" in\n * createNoydb().\n */\nexport class BrokerNotEnabledError extends NoydbError {\n constructor(\n message = 'Credential-broker operations require the broker capability. ' +\n 'Pass `brokerStrategy: withBroker(config)` (from \"@noy-db/hub/broker\") to createNoydb().',\n ) {\n super('BROKER_NOT_ENABLED', message)\n this.name = 'BrokerNotEnabledError'\n }\n}\n\n/**\n * Thrown when the `_broker` seed cannot be enrolled with the broker host:\n * `enroll()`/`rotate()` called on a DEK-only keyring (KEK required to\n * provision the `_broker` DEK on first use — R-B8/I3), a `/enroll` POST\n * refused for lacking a valid dev-backend attestation (R-B3), or\n * `credentialSource()` called on a seed whose `/enroll` never completed\n * successfully (`registered !== true` — a partial enrol, I9).\n */\nexport class BrokerEnrolmentError extends NoydbError {\n constructor(message = 'Credential-broker enrolment failed') {\n super('BROKER_ENROLMENT_ERROR', message)\n this.name = 'BrokerEnrolmentError'\n }\n}\n\n/**\n * Thrown when the broker host rejects a submitted challenge proof (MAC\n * mismatch, expired `expiresAt`, or a reused/burned challenge — R-B5).\n */\nexport class BrokerProofError extends NoydbError {\n constructor(message = 'Broker rejected the challenge proof') {\n super('BROKER_PROOF_ERROR', message)\n this.name = 'BrokerProofError'\n }\n}\n\n// ─── Session Errors ───────────────────────────────────────\n\n/**\n * Thrown by `resolveSession()` when the session token's `expiresAt`\n * timestamp is in the past. The session key is also removed from the\n * in-memory store when this is thrown, so retrying with the same sessionId\n * will produce `SessionNotFoundError`.\n *\n * Separate from `SessionNotFoundError` so callers can distinguish between\n * \"session is gone\" (key store cleared, tab reloaded) and \"session is\n * still in the store but has exceeded its lifetime\" (idle timeout, absolute\n * timeout, policy-driven expiry). The remediation differs: expired sessions\n * should prompt a fresh unlock; not-found sessions may indicate a bug or a\n * cross-tab scenario where the session was never established.\n */\nexport class SessionExpiredError extends NoydbError {\n readonly sessionId: string\n\n constructor(sessionId: string) {\n super('SESSION_EXPIRED', `Session \"${sessionId}\" has expired. Re-unlock to continue.`)\n this.name = 'SessionExpiredError'\n this.sessionId = sessionId\n }\n}\n\n/**\n * Thrown by `resolveSession()` when the session key cannot be found in\n * the module-level store. This happens when:\n * - The session was explicitly revoked via `revokeSession()`.\n * - The JS context was reloaded (tab navigation, page refresh, worker restart).\n * - `Noydb.close()` was called (which calls `revokeAllSessions()`).\n * - The sessionId is wrong or was generated by a different JS context.\n *\n * The session token (if the caller holds it) is permanently useless after\n * this error — the key is gone and cannot be recovered.\n */\nexport class SessionNotFoundError extends NoydbError {\n readonly sessionId: string\n\n constructor(sessionId: string) {\n super('SESSION_NOT_FOUND', `Session key for \"${sessionId}\" not found. The session may have been revoked or the page reloaded.`)\n this.name = 'SessionNotFoundError'\n this.sessionId = sessionId\n }\n}\n\n/**\n * Thrown when a session policy blocks an operation — for example,\n * `requireReAuthFor: ['export']` is set and the caller attempts to\n * call `exportStream()` without re-authenticating for this session.\n *\n * The `operation` field names the specific operation that was blocked\n * (e.g. `'export'`, `'grant'`, `'rotate'`) so the caller can surface\n * a targeted prompt (\"Please re-enter your passphrase to export data\").\n */\nexport class SessionPolicyError extends NoydbError {\n readonly operation: string\n\n constructor(operation: string, message?: string) {\n super(\n 'SESSION_POLICY',\n message ?? `Operation \"${operation}\" requires re-authentication per the active session policy.`,\n )\n this.name = 'SessionPolicyError'\n this.operation = operation\n }\n}\n\n// ─── Query / Join Errors ────────────────────────────────────\n\n/**\n * Thrown when a `.join()` would exceed its configured row ceiling on\n * either side. The ceiling defaults to 50,000 per side and can be\n * overridden via the `{ maxRows }` option on `.join()`.\n *\n * Carries both row counts so the error message can show which side\n * tripped the limit (e.g. \"left had 60,000 rows, right had 1,200,\n * max was 50,000\"). The `side` field is machine-readable so test\n * code and devtools can match on it without regex-parsing the\n * message.\n *\n * The row ceiling exists because joins are bounded in-memory\n * operations over materialized record sets. Consumers whose\n * collections genuinely exceed the ceiling should track \n * (streaming joins over `scan()`) or filter the left side further\n * with `where()` / `limit()` before joining.\n */\nexport class JoinTooLargeError extends NoydbError {\n readonly leftRows: number\n readonly rightRows: number\n readonly maxRows: number\n readonly side: 'left' | 'right'\n\n constructor(opts: {\n leftRows: number\n rightRows: number\n maxRows: number\n side: 'left' | 'right'\n message: string\n }) {\n super('JOIN_TOO_LARGE', opts.message)\n this.name = 'JoinTooLargeError'\n this.leftRows = opts.leftRows\n this.rightRows = opts.rightRows\n this.maxRows = opts.maxRows\n this.side = opts.side\n }\n}\n\n/**\n * Thrown by `.crossJoin()` when the cumulative cartesian product (or lateral\n * filtered count) exceeds the configured ceiling. Check before allocating.\n * Mirrors the pattern of `JoinTooLargeError` and the `.join()` row ceiling.\n *\n * @see CrossJoinClause.maxRows — per-clause override\n * @see DEFAULT_CROSS_JOIN_MAX_ROWS — package default (50_000)\n */\nexport class CrossJoinTooLargeError extends NoydbError {\n readonly target: string\n readonly expected: number\n readonly limit: number\n\n constructor(opts: { target: string; expected: number; limit: number }) {\n super(\n 'CROSS_JOIN_TOO_LARGE',\n `crossJoin(\"${opts.target}\"): would produce ${opts.expected} rows, ` +\n `exceeding the limit of ${opts.limit}. ` +\n `Narrow the left side with .where() first, or raise the ceiling ` +\n `with crossJoin(\"${opts.target}\", { ..., maxRows: ${opts.expected} }).`,\n )\n this.name = 'CrossJoinTooLargeError'\n this.target = opts.target\n this.expected = opts.expected\n this.limit = opts.limit\n }\n}\n\n/**\n * Thrown at cross-join execution time when the target collection is not\n * reachable from the current vault. The left collection is included in the\n * message for context.\n */\nexport class CrossJoinSourceUnknownError extends NoydbError {\n readonly target: string\n readonly leftCollection: string\n\n constructor(target: string, leftCollection: string) {\n super(\n 'CROSS_JOIN_SOURCE_UNKNOWN',\n `crossJoin(\"${target}\"): collection \"${target}\" is not known in the vault ` +\n `(cross-joining from \"${leftCollection}\"). ` +\n `Make sure \"${target}\" is open in the same vault before executing this query.`,\n )\n this.name = 'CrossJoinSourceUnknownError'\n this.target = target\n this.leftCollection = leftCollection\n }\n}\n\n/**\n * Thrown by `.join()` in strict `ref()` mode when a left-side record\n * points at a right-side id that does not exist in the target\n * collection.\n *\n * Distinct from `RefIntegrityError` so test code can pattern-match\n * on the *read-time* dangling case without catching *write-time*\n * integrity violations. Both indicate \"ref points at nothing\" but\n * happen at different lifecycle phases and deserve different\n * remediation in documentation: a RefIntegrityError on `put()`\n * means the input is invalid; a DanglingReferenceError on `.join()`\n * means stored data has drifted and `vault.checkIntegrity()`\n * is the right tool to find the full set of orphans.\n */\nexport class DanglingReferenceError extends NoydbError {\n readonly field: string\n readonly target: string\n readonly refId: string\n\n constructor(opts: {\n field: string\n target: string\n refId: string\n message: string\n }) {\n super('DANGLING_REFERENCE', opts.message)\n this.name = 'DanglingReferenceError'\n this.field = opts.field\n this.target = opts.target\n this.refId = opts.refId\n }\n}\n\n/**\n * Thrown by {@link sanitizeFilename} when an input filename cannot be\n * made safe — NUL byte, empty after normalization, missing\n * `opaqueId` for the opaque profile, `..` segment, or a `maxBytes`\n * cap too small to hold a single code point.\n */\nexport class FilenameSanitizationError extends NoydbError {\n constructor(message: string) {\n super('FILENAME_SANITIZATION', message)\n this.name = 'FilenameSanitizationError'\n }\n}\n\n/**\n * Thrown when a write target resolves OUTSIDE the requested\n * directory after sanitization — the canonical Zip-Slip class. The\n * sanitizer's job is to strip path-traversal segments; this error\n * is the defense-in-depth fallback at the FS write site.\n */\nexport class PathEscapeError extends NoydbError {\n readonly attempted: string\n readonly targetDir: string\n\n constructor(opts: { attempted: string; targetDir: string }) {\n super(\n 'PATH_ESCAPE',\n `Sanitized filename \"${opts.attempted}\" resolves outside target dir \"${opts.targetDir}\"`,\n )\n this.name = 'PathEscapeError'\n this.attempted = opts.attempted\n this.targetDir = opts.targetDir\n }\n}\n\n// ─── Derivation Errors ──────────────────────────────\n\n/**\n * Thrown at vault open if the derivation graph contains a cycle.\n * `path` is the offending chain (e.g. `['a', 'b', 'c', 'a']`).\n */\nexport class DerivationCycleError extends NoydbError {\n readonly path: readonly string[]\n\n constructor(path: readonly string[]) {\n super(\n 'DERIVATION_CYCLE',\n `Derivation graph contains a cycle: ${path.join(' → ')}. ` +\n `Refusing to open vault — break the cycle before retrying.`,\n )\n this.name = 'DerivationCycleError'\n this.path = path\n }\n}\n\n/**\n * Thrown when a cascade of source → output → source → … exceeds the\n * configured `maxDepth` (default 5).\n */\nexport class DerivationDepthError extends NoydbError {\n readonly limit: number\n readonly attempted: number\n\n constructor(limit: number, attempted: number) {\n super(\n 'DERIVATION_DEPTH',\n `Derivation cascade exceeded max depth ${limit} (attempted ${attempted}). ` +\n `Pass lifecycle: { maxDepth: N } to raise the limit if intentional.`,\n )\n this.name = 'DerivationDepthError'\n this.limit = limit\n this.attempted = attempted\n }\n}\n\n/**\n * Thrown at registration if a `withDerivation` strategy references an\n * output `collection` that isn't otherwise declared (no schema, no use\n * elsewhere). Surfacing this early catches typos in collection names.\n */\nexport class DerivationOutputUnknownError extends NoydbError {\n readonly collection: string\n\n constructor(collection: string) {\n super(\n 'DERIVATION_OUTPUT_UNKNOWN',\n `Derivation output collection \"${collection}\" is not declared on the vault. ` +\n `Register the collection (e.g. via schema) before registering a derivation that writes to it.`,\n )\n this.name = 'DerivationOutputUnknownError'\n this.collection = collection\n }\n}\n\n/**\n * Thrown when the user's `derive` function returns a value that doesn't\n * match the declared output spec (e.g. wrong shape, wrong key set).\n */\nexport class DerivationOutputShapeError extends NoydbError {\n readonly outputKey: string\n\n constructor(outputKey: string, detail: string) {\n super(\n 'DERIVATION_OUTPUT_SHAPE',\n `Derivation output \"${outputKey}\" has invalid shape: ${detail}.`,\n )\n this.name = 'DerivationOutputShapeError'\n this.outputKey = outputKey\n }\n}\n\n/**\n * Thrown by array-shape derivations when the `derive` function\n * returns more rows than the output's `maxFanout` cap. The cap exists\n * to keep dispatch cost bounded — without it a single source-row\n * update could fan out to thousands of derived rows, dominating the\n * write path.\n *\n * Defaults to `maxFanout: 64`. Raise on the output spec for\n * carry-forward expansion cases (e.g. monthly rows across multi-year\n * contracts).\n */\nexport class DerivationCapExceededError extends NoydbError {\n readonly outputKey: string\n readonly returned: number\n readonly maxFanout: number\n\n constructor(outputKey: string, returned: number, maxFanout: number) {\n super(\n 'DERIVATION_CAP_EXCEEDED',\n `Derivation array output \"${outputKey}\" returned ${returned} rows, exceeding `\n + `maxFanout=${maxFanout}. Raise \\`maxFanout\\` on the OutputSpec if this fanout `\n + 'is intended (the cap exists to keep dispatch cost bounded).',\n )\n this.name = 'DerivationCapExceededError'\n this.outputKey = outputKey\n this.returned = returned\n this.maxFanout = maxFanout\n }\n}\n\n/**\n * Thrown at vault open if the materialized-view graph contains a\n * cycle. `path` is the offending chain (e.g. `['a-mv', 'b-mv', 'a-mv']`).\n * Detected by the same shared DFS that catches `DerivationCycleError`;\n * surfaces with a distinct error type so consumers can disambiguate.\n */\nexport class MaterializedViewCycleError extends NoydbError {\n readonly path: readonly string[]\n\n constructor(path: readonly string[]) {\n super(\n 'MATERIALIZED_VIEW_CYCLE',\n `Materialized-view graph contains a cycle: ${path.join(' → ')}. ` +\n `Refusing to open vault — break the cycle before retrying.`,\n )\n this.name = 'MaterializedViewCycleError'\n this.path = path\n }\n}\n\n/**\n * Thrown at MV registration if the query references a source\n * collection that isn't declared on the vault. Surfacing this early\n * catches typos in collection names.\n */\nexport class MaterializedViewSourceUnknownError extends NoydbError {\n readonly mvName: string\n readonly collection: string\n\n constructor(mvName: string, collection: string) {\n super(\n 'MATERIALIZED_VIEW_SOURCE_UNKNOWN',\n `Materialized view \"${mvName}\" references unknown source collection \"${collection}\". ` +\n `Declare the collection (e.g. via schema or by writing to it once) before registering the MV.`,\n )\n this.name = 'MaterializedViewSourceUnknownError'\n this.mvName = mvName\n this.collection = collection\n }\n}\n\n/**\n * Thrown by the MV executor when a refresh produces more rows than\n * the configured ceiling. Default ceiling is 100k rows; override\n * per-MV via `maxRows`. Mirrors `JoinTooLargeError` /\n * `GroupCardinalityError` from the query DSL — the explosion is\n * detected BEFORE writes hit the store, so the source-write\n * transaction can roll back cleanly via strict-mode.\n */\nexport class MaterializedViewTooLargeError extends NoydbError {\n readonly mvName: string\n readonly expected: number\n readonly limit: number\n\n constructor(mvName: string, expected: number, limit: number) {\n super(\n 'MATERIALIZED_VIEW_TOO_LARGE',\n `Materialized view \"${mvName}\" would emit ${expected} rows, exceeding the configured limit of ${limit}. ` +\n `Override via { maxRows: N } on the MV strategy if intentional, or tighten the query's filter/groupBy.`,\n )\n this.name = 'MaterializedViewTooLargeError'\n this.mvName = mvName\n this.expected = expected\n this.limit = limit\n }\n}\n\n/**\n * Thrown by `withMaterializedView()` at registration time when the\n * strategy is structurally malformed. Distinct from\n * `MaterializedViewSourceUnknownError` (the source list is well-formed\n * but names a collection the vault doesn't know) and\n * `MaterializedViewCycleError` (the source graph has a cycle): this\n * error fires before either check, at the moment the spec is being\n * normalized.\n *\n * Today the trigger cases are all about the `query` / `unionSources` /\n * `projection` trichotomy:\n * - more than one of `query` / `unionSources` / `projection` was set\n * (mutually exclusive),\n * - none of the three was set,\n * - `unionSources` has fewer than 2 arms,\n * - two arms in `unionSources` reference the same `collection`,\n * - a malformed `projection` leg (dup / empty `as`, neither `field`\n * nor `collect`, empty leg list) — plus, at first materialization,\n * a collect leg whose `on` field lacks a `ref()` targeting the\n * projection source.\n *\n * The error message is prefixed with `[noy-db] withMaterializedView:`\n * so it's grep-friendly in logs and looks consistent with the existing\n * `ValidationError` messages from the same factory.\n */\nexport class MaterializedViewConfigError extends NoydbError {\n constructor(message: string) {\n super(\n 'MATERIALIZED_VIEW_CONFIG',\n `[noy-db] withMaterializedView: ${message}`,\n )\n this.name = 'MaterializedViewConfigError'\n }\n}\n\n/**\n * Thrown at vault open when a `withOverlayedView` declaration uses\n * another virtual-overlay name as its `base`. Multi-overlay stacking\n * is a v2 non-goal — the shallow expansion in\n * `QueryDependencyAnalyzer` would truncate at the inner overlay\n * name, leaving downstream MVs silently stale.\n */\nexport class OverlayBaseIsVirtualError extends NoydbError {\n readonly overlayName: string\n readonly base: string\n\n constructor(overlayName: string, base: string) {\n super(\n 'OVERLAY_BASE_IS_VIRTUAL',\n `withOverlayedView \"${overlayName}\": base \"${base}\" is another overlay's virtual name. ` +\n `Multi-overlay stacking is a v3 feature; base must reference a concrete collection (a real source or an MV output).`,\n )\n this.name = 'OverlayBaseIsVirtualError'\n this.overlayName = overlayName\n this.base = base\n }\n}\n\n/**\n * Thrown at vault open when a `withOverlayedView`'s `overlay`\n * references an unknown collection or an MV-owned collection. The\n * overlay collection is user-writable; MV-owned collections aren't.\n */\nexport class OverlayCollectionUnavailableError extends NoydbError {\n readonly overlayName: string\n readonly overlay: string\n\n constructor(overlayName: string, overlay: string) {\n super(\n 'OVERLAY_COLLECTION_UNAVAILABLE',\n `withOverlayedView \"${overlayName}\": overlay collection \"${overlay}\" is unavailable. ` +\n `It must be a real vault-known collection that is NOT itself an MV output collection.`,\n )\n this.name = 'OverlayCollectionUnavailableError'\n this.overlayName = overlayName\n this.overlay = overlay\n }\n}\n\n/**\n * Thrown at vault open when a `withOverlayedView`'s virtual `name`\n * collides with an MV output or a concrete source collection.\n */\nexport class OverlayNameCollisionError extends NoydbError {\n readonly overlayName: string\n\n constructor(overlayName: string) {\n super(\n 'OVERLAY_NAME_COLLISION',\n `withOverlayedView \"${overlayName}\": virtual name collides with an MV output or a concrete source collection. ` +\n `Pick a unique name for the virtual collection.`,\n )\n this.name = 'OverlayNameCollisionError'\n this.overlayName = overlayName\n }\n}\n\n/**\n * Thrown by the virtual overlay's `put(id, record)` when the\n * consumer-supplied `id` doesn't match `rowKey(record)`. Catches\n * fat-finger separator typos that would otherwise silently produce\n * orphaned overlay rows. Direct writes to the underlying overlay\n * collection (bypass the virtual layer) skip this validation.\n */\nexport class OverlayIdMismatchError extends NoydbError {\n readonly actual: string\n readonly expected: string\n\n constructor(actual: string, expected: string) {\n super(\n 'OVERLAY_ID_MISMATCH',\n `Overlay put(id, record): id \"${actual}\" does not match the base MV's rowKey(record) → \"${expected}\". ` +\n `Pass the row directly via .put(record) to derive the id, or fix the id to match the base MV's rowKey output.`,\n )\n this.name = 'OverlayIdMismatchError'\n this.actual = actual\n this.expected = expected\n }\n}\n\n// ─── Snapshot Errors ──────────────────────────────────────\n\n/**\n * Thrown when a requested snapshot version does not exist in the\n * snapshot store — either it was never created, was pruned by the\n * retention policy, or was deleted manually.\n *\n * The `version` field carries the key that was looked up so callers\n * can surface an actionable \"snapshot X not found\" message without\n * parsing the error string.\n */\nexport class SnapshotNotFoundError extends NoydbError {\n readonly version: string\n\n constructor(version: string) {\n super(\n 'SNAPSHOT_NOT_FOUND',\n `Snapshot not found: \"${version}\" does not exist in the snapshot store. ` +\n `It may have been pruned by the retention policy or deleted manually.`,\n )\n this.name = 'SnapshotNotFoundError'\n this.version = version\n }\n}\n\n// ─── Federation (multi-vault partition) Errors ──────────────────────────\n\n/**\n * Thrown when a write targets a partition key that has no shard and\n * `sharding.autoCreate` is disabled.\n */\nexport class UnknownShardError extends NoydbError {\n readonly partitionKey: string\n\n constructor(partitionKey: string, groupName: string) {\n super(\n 'SHARD_UNKNOWN',\n `No shard for partition key \"${partitionKey}\" in vault group \"${groupName}\" ` +\n `and autoCreate is disabled. Call group.createShard(${JSON.stringify(partitionKey)}) ` +\n `first, or enable sharding.autoCreate.`,\n )\n this.name = 'UnknownShardError'\n this.partitionKey = partitionKey\n }\n}\n\n/**\n * Thrown by `createShard` when the registry has a row for a partition\n * but the corresponding vault is not provisioned in the store —\n * a registry/store divergence. Refusing to recreate avoids masking\n * data loss.\n */\nexport class ShardProvisioningError extends NoydbError {\n readonly vaultId: string\n\n constructor(vaultId: string, partitionKey: string) {\n super(\n 'SHARD_PROVISIONING',\n `Registry has a row for partition \"${partitionKey}\" (vault \"${vaultId}\") but that ` +\n `vault is not provisioned in the store. Refusing to recreate it — the registry and ` +\n `store have diverged. Investigate before retrying.`,\n )\n this.name = 'ShardProvisioningError'\n this.vaultId = vaultId\n }\n}\n\n/**\n * Thrown by `VaultGroup.createShard` when `sharding.regionOf` resolves a\n * required region that doesn't match the placement backend's\n * `capabilities.region` — the shard would land on a non-compliant backend\n * (a data-residency violation). Raised BEFORE provisioning, so no\n * vault is created.\n */\nexport class DataResidencyError extends NoydbError {\n readonly vaultId: string\n readonly requiredRegion: string\n readonly backendRegion: string | undefined\n\n constructor(vaultId: string, requiredRegion: string, backendRegion: string | undefined) {\n super(\n 'DATA_RESIDENCY',\n `Shard \"${vaultId}\" requires region \"${requiredRegion}\" but its placement backend ` +\n `declares region ${backendRegion === undefined ? '(none)' : `\"${backendRegion}\"`}. ` +\n `Refusing to provision — route this shard to a region-correct backend via ` +\n `routeStore({ vaultRoutes }) (e.g. a region-encoded partition key) before retrying.`,\n )\n this.name = 'DataResidencyError'\n this.vaultId = vaultId\n this.requiredRegion = requiredRegion\n this.backendRegion = backendRegion\n }\n}\n\n/**\n * Thrown by `ShardedQuery.crossShardJoin` / `broadcastJoin` for\n * deterministic, query-shaping errors: an undeclared join ref (which\n * would fail identically on every shard), or calling a deferred\n * reactive/aggregate surface on a query that already carries join legs.\n */\nexport class CrossShardJoinError extends NoydbError {\n constructor(message: string) {\n super('CROSS_SHARD_JOIN', message)\n this.name = 'CrossShardJoinError'\n }\n}\n\n/** Thrown when a VaultGroup references a template name that was never registered. */\nexport class VaultTemplateNotFoundError extends NoydbError {\n readonly templateName: string\n\n constructor(templateName: string) {\n super(\n 'VAULT_TEMPLATE_NOT_FOUND',\n `No vault template registered under \"${templateName}\". ` +\n `Register the template before opening the vault group.`,\n )\n this.name = 'VaultTemplateNotFoundError'\n this.templateName = templateName\n }\n}\n\n// ─── Erasure Errors ────────────────────────────────────────────────────\n\n/**\n * Thrown when `vault.forget(subjectId)` is called on a vault whose\n * `createNoydb({ forgetStrategy })` declared no subject fields (the\n * default `NO_FORGET`). GDPR crypto-shred needs a declared subject →\n * record index to know which records belong to a data subject; without\n * one there is nothing to erase and a silent no-op would be a dangerous\n * false \"erased\" signal. Configure with\n * `forgetStrategy: withForgetCascade({ subjects: { invoices: 'buyerId' } })`.\n */\nexport class ForgetStrategyNotConfiguredError extends NoydbError {\n constructor(\n message = 'vault.forget() requires a forget strategy. Pass ' +\n '`forgetStrategy: withForgetCascade({ subjects: { <collection>: <subjectField> } })` ' +\n 'from \"@noy-db/hub/forget\" to createNoydb().',\n ) {\n super('FORGET_NOT_CONFIGURED', message)\n this.name = 'ForgetStrategyNotConfiguredError'\n }\n}\n\n// ─── Sealed-record (record-scoped CEK sealing) Errors ───────────────────\n\n/**\n * Thrown by `openSealedRecord()` when the sealed CEK's binding has passed its\n * `expiresAt`. Surfaced on two checks: a cheap fast-path check on the delivery\n * envelope's clear-text `expiresAt`, and the AUTHORITATIVE check on the\n * `expiresAt` inside the sealed binding (the latter cannot be forged by editing\n * the delivery envelope). Distinct from {@link KeyringExpiredError} (bundle-slot\n * expiry) so a host can tell \"this single-record grant lapsed\" from a keyring-\n * level expiry.\n */\nexport class SealedRecordExpiredError extends NoydbError {\n readonly expiresAt: string\n constructor(expiresAt: string) {\n super(\n 'SEALED_RECORD_EXPIRED',\n `Sealed record CEK expired at ${expiresAt}. The grantor must re-seal the ` +\n `record with a later expiresAt.`,\n )\n this.name = 'SealedRecordExpiredError'\n this.expiresAt = expiresAt\n }\n}\n\n/**\n * Thrown by `openSealedRecord()` when the sealed binding's\n * `{collection, id}` does not match the record envelope the host is trying to\n * decrypt. This is the host-denial boundary: a CEK sealed for record A cannot\n * be replayed against record B's envelope. (A CEK sealed for a PRE-rotation\n * version of a record, applied to the POST-rotation live envelope, is a\n * different failure — the binding still matches `{collection, id}` so it gets\n * past this check, and the AES-GCM auth-tag failure surfaces as\n * {@link TamperedError} instead.)\n */\nexport class SealedRecordMismatchError extends NoydbError {\n readonly expected: { collection: string; id: string }\n readonly actual: { collection: string; id: string }\n constructor(\n expected: { collection: string; id: string },\n actual: { collection: string; id: string },\n ) {\n super(\n 'SEALED_RECORD_MISMATCH',\n `Sealed CEK binding is for ${actual.collection}/${actual.id} but was ` +\n `presented against ${expected.collection}/${expected.id}. A CEK sealed ` +\n `for one record cannot decrypt another.`,\n )\n this.name = 'SealedRecordMismatchError'\n this.expected = expected\n this.actual = actual\n }\n}\n\n/**\n * Thrown by `vault.sealRecordToHost()` / `vault.rotateRecordCek()` when the\n * target record has no live envelope, or its live envelope carries no `_cek`\n * (a legacy / non-`perRecordKeys` collection has nothing record-scoped to\n * seal — its body is keyed off the shared collection DEK, which sealing\n * deliberately never exposes).\n */\nexport class RecordCekNotFoundError extends NoydbError {\n readonly collection: string\n readonly id: string\n constructor(collection: string, id: string) {\n super(\n 'RECORD_CEK_NOT_FOUND',\n `No per-record CEK for ${collection}/${id}. The record is missing, or its ` +\n `collection was not opened with { perRecordKeys: true } — only per-record-key ` +\n `records carry a sealable CEK.`,\n )\n this.name = 'RecordCekNotFoundError'\n this.collection = collection\n this.id = id\n }\n}\n\n// ─── Embedding Errors ────────────────────────────────────────────────────\n\n/**\n * Thrown when a stored vector's dimension does not match the dimension declared\n * by the active `EmbeddingDescriptor`. The `field`, `expected`, and `actual`\n * properties are machine-readable for switch blocks and logging.\n */\nexport class EmbeddingDimMismatchError extends NoydbError {\n readonly field: string\n readonly expected: number\n readonly actual: number\n\n constructor(field: string, expected: number, actual: number) {\n super(\n 'EMBEDDING_DIM_MISMATCH',\n `Embedding for \"${field}\" has dim ${actual}, expected ${expected}.`,\n )\n this.name = 'EmbeddingDimMismatchError'\n this.field = field\n this.expected = expected\n this.actual = actual\n }\n}\n\n/**\n * Thrown when the model tag on a stored vector does not match the `model`\n * declared by the active `EmbeddingDescriptor`. Signals that the encoder was\n * swapped without re-indexing; call `vault.embeddings.reindex()` to rebuild.\n */\nexport class EmbeddingModelMismatchError extends NoydbError {\n readonly expected: string\n readonly found: string\n\n constructor(expected: string, found: string) {\n super(\n 'EMBEDDING_MODEL_MISMATCH',\n `Embedding model mismatch: collection uses \"${expected}\" but a stored vector is \"${found}\". ` +\n `Run vault.embeddings.reindex() after changing the encoder.`,\n )\n this.name = 'EmbeddingModelMismatchError'\n this.expected = expected\n this.found = found\n }\n}\n\n// ─── User Envelope Errors ────────────────────────────────────────────────\n\n/**\n * Thrown when a user-envelope payload exceeds {@link USER_ENVELOPE_MAX_BYTES}\n * after JSON-serialization. The error carries the actual size so callers\n * can decide whether to trim or split.\n */\nexport class UserEnvelopeOversizedError extends NoydbError {\n readonly bytes: number\n readonly limit: number\n constructor(bytes: number, limit: number = USER_ENVELOPE_MAX_BYTES) {\n super(\n 'USER_ENVELOPE_OVERSIZED',\n `User envelope payload is ${bytes} bytes; soft cap is ${limit} bytes. ` +\n `Move large data into the vault's regular collections.`,\n )\n this.name = 'UserEnvelopeOversizedError'\n this.bytes = bytes\n this.limit = limit\n }\n}\n\n// ─── Policy Gate Errors ──────────────────────────────────────────────────\n//\n// The engine (`checkGate`/`describeGate`), presets, and storage helpers for\n// these errors live at `with-party/policy/`; the error classes themselves\n// (like every NOYDB error) live in this always-on spine file.\n\n/**\n * Why a gate denied a request. Stable across hub versions so consumers\n * can switch on the value in error UIs.\n */\nexport type PolicyDenyReason =\n | 'insufficient-tier'\n | 'missing-factor'\n | 'stale-proof'\n | 'disabled'\n | 'shared-device-blocked'\n\n/**\n * Thrown by {@link checkGate} when the active session does not meet\n * the gate's requirements. Carries the gate name, the reason, and the\n * full required {@link GatePolicy} so error UIs can prompt the user\n * for the missing factor without re-reading the policy document.\n */\nexport class PolicyDeniedError extends NoydbError {\n readonly gate: GateName\n readonly reason: PolicyDenyReason\n readonly required: GatePolicy\n constructor(gate: GateName, reason: PolicyDenyReason, required: GatePolicy, message?: string) {\n super(\n 'POLICY_DENIED',\n message ?? `Gate \"${gate}\" denied: ${reason}.`,\n )\n this.name = 'PolicyDeniedError'\n this.gate = gate\n this.reason = reason\n this.required = required\n }\n}\n\n/**\n * Raised by `createNoydb({ ... })` when the developer omits a recovery\n * profile and `recover-passphrase` is not explicitly disabled. Vaults\n * MUST have at least one recovery path enrolled before being\n * production-ready (paper, shamir, multi-channel, or admin-mediated).\n *\n * The error message carries a pointer to the recovery design docs.\n */\nexport class RecoveryNotEnrolledError extends NoydbError {\n constructor(\n message =\n 'Recovery profile not enrolled. Pass `recovery: [{ profile: \"paper\", codes: 10 }]` ' +\n 'to `createNoydb()`, or set `policy.gates[\"recover-passphrase\"].enabled = false` to ' +\n 'opt out of recovery (passphrase loss = data loss). See https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/session-tiers.md.',\n ) {\n super('RECOVERY_NOT_ENROLLED', message)\n this.name = 'RecoveryNotEnrolledError'\n }\n}\n\n/**\n * Raised by `openVault` when a managed-passphrase-mode vault has no\n * STRONG recovery profile enrolled.\n *\n * Managed mode means the user never types a passphrase — the unlock\n * material lives in a `SealingKeyProvider` (`at-*` package). If that\n * provider's key is lost AND no strong recovery is enrolled, the\n * vault is irrecoverable. To prevent that footgun, managed-mode vaults\n * require at least one strong recovery profile (Shamir today;\n * multi-channel / admin-mediated when those ship).\n *\n * Paper recovery alone is NOT strong under managed mode: the user has\n * no memorized passphrase to fall back on, so losing the paper sheet =\n * losing every record permanently.\n *\n * Bootstrap with `db.openVaultAndEnrollRecovery(vault, { recovery: [{ profile: \"shamir\", k, n }] })`\n * to atomically create-and-enroll, or call `db.enrollRecovery(vault, { profile: \"shamir\", ... })`\n * separately before re-attempting `openVault`.\n */\nexport class ManagedRecoveryNotEnrolledError extends NoydbError {\n readonly vault: string\n constructor(vault: string) {\n super(\n 'MANAGED_RECOVERY_NOT_ENROLLED',\n `Managed-mode vault \"${vault}\" requires at least one strong recovery profile `\n + '(Shamir today; multi-channel / admin-mediated when they ship). Paper alone is '\n + 'NOT strong under managed mode — losing the paper sheet would mean losing every '\n + 'record permanently. '\n + `Bootstrap with \\`db.openVaultAndEnrollRecovery(\"${vault}\", { recovery: [{ profile: \"shamir\", k: 2, n: 3 }] })\\`, `\n + 'or call `db.enrollRecovery(vault, { profile: \"shamir\", k, n })` separately, '\n + 'then re-attempt `openVault`.',\n )\n this.name = 'ManagedRecoveryNotEnrolledError'\n this.vault = vault\n }\n}\n\n/**\n * Raised by `db.recoverPassphrase` / `db.enrollRecovery` /\n * `db.rotateRecovery` when the developer requests a recovery profile\n * not yet wired in this hub release.\n *\n * Implemented: `paper` and `shamir`.\n * Pending: `multi-channel` and `admin-mediated` (follow-up slices).\n *\n * The carried `profile` and `tracking` fields let consumers steer the\n * UI (\"multi-channel recovery is not yet wired up — open issue #N to follow\").\n */\nexport class RecoveryProfileNotImplementedError extends NoydbError {\n readonly profile: string\n readonly tracking: string\n constructor(profile: string, tracking: string) {\n super(\n 'RECOVERY_PROFILE_NOT_IMPLEMENTED',\n `Recovery profile \"${profile}\" is not yet implemented in this hub release. ` +\n `Tracking: ${tracking}. Use the \"paper\" profile via @noy-db/on-recovery in the meantime.`,\n )\n this.name = 'RecoveryProfileNotImplementedError'\n this.profile = profile\n this.tracking = tracking\n }\n}\n\n// ─── Enclave Errors ────────────────────────────────────────────────────\n\n/**\n * Thrown by a `kernel/enclave` fork's **optional groups** — sealing,\n * deterministic, per-record-key lifecycle — when that fork's crypto\n * engine does not implement the requested behavior.\n *\n * The enclave barrel is a frozen fork-swap contract: every symbol must\n * exist, but the optional groups may refuse to work rather than\n * implement the full reference semantics. The unconditional core\n * (crypto ops, `RecordCodec`, tombstone) must never throw this — those\n * groups are load-bearing for every consumer regardless of which\n * enclave is wired in. noy-db's own reference enclave supports every\n * group, so this error is never thrown by `@noy-db/hub` itself; it\n * exists for fork authors to signal \"my enclave doesn't do X\" with a\n * stable, catchable code instead of an ad hoc throw.\n */\nexport class EnclaveNotSupportedError extends NoydbError {\n /** The optional group that is not supported by this enclave. */\n readonly group: 'sealing' | 'deterministic' | 'per-record-keys'\n\n constructor(group: 'sealing' | 'deterministic' | 'per-record-keys', detail?: string) {\n super(\n 'ENCLAVE_NOT_SUPPORTED',\n `enclave: ${group} is not supported by this enclave${detail ? ` — ${detail}` : ''}`,\n )\n this.name = 'EnclaveNotSupportedError'\n this.group = group\n }\n}\n\n// ─── Classified Errors ─────────────────────────────────────────────────\n\n/**\n * Raised when a collection's `classifiedFields` configuration is invalid\n * (e.g. a claimed field name collides with a rider companion or another\n * classified field). Homed in `kernel/errors.ts` (rather than the classified\n * feature module) so `kernel/enclave/classify/*` can throw it without\n * importing with-*; the classified feature module re-exports it under the\n * same name for backward-compatible import paths.\n */\nexport class ClassifiedConfigError extends Error {\n constructor(public readonly collection: string, message: string) {\n super(`classifiedFields for collection \"${collection}\": ${message}`)\n this.name = 'ClassifiedConfigError'\n }\n}\n\n/**\n * Raised by `collection.reveal()` when a field cannot be revealed — unknown\n * field, `storage:'never'`, or missing record. See {@link ClassifiedConfigError}\n * for why this lives in `kernel/errors.ts`.\n */\nexport class ClassifiedRevealError extends Error {\n constructor(public readonly collection: string, public readonly field: string, detail: string) {\n super(`Cannot reveal field \"${field}\" in collection \"${collection}\": ${detail}`)\n this.name = 'ClassifiedRevealError'\n }\n}\n\n/**\n * Raised by the classified verify path (`storage:'digest-only'`, stage 2)\n * when a field cannot be verified — unknown field, not digest-only, or no\n * digest stored yet.\n */\nexport class ClassifiedVerifyError extends Error {\n constructor(public readonly collection: string, public readonly field: string, detail: string) {\n super(`Cannot verify field \"${field}\" in collection \"${collection}\": ${detail}`)\n this.name = 'ClassifiedVerifyError'\n }\n}\n\n/**\n * Raised by the classified rotation path (stage 2) when a new value is\n * refused — e.g. reuse of one of the last N values (`notLastN`).\n */\nexport class ClassifiedRotationError extends Error {\n constructor(public readonly collection: string, public readonly field: string, detail: string) {\n super(`Rotation refused for field \"${field}\" in collection \"${collection}\": ${detail}`)\n this.name = 'ClassifiedRotationError'\n }\n}\n\n// ─── Satellite Errors ──────────────────────────────────────────────────\n\n/**\n * A satellite-collection declaration or operation violated the refusal\n * matrix (R-S1…R-S10) of the satellite-collections design. The message\n * always names the R-S id.\n */\nexport class SatelliteConfigError extends NoydbError {\n constructor(message: string, options?: { readonly cause?: unknown }) {\n super('SATELLITE_CONFIG_ERROR', message)\n this.name = 'SatelliteConfigError'\n if (options?.cause !== undefined) this.cause = options.cause\n }\n}\n"],"mappings":"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","names":[]}