@noy-db/hub 0.2.0-pre.4 → 0.2.0-pre.5

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 (280) hide show
  1. package/dist/aggregate/index.cjs.map +1 -1
  2. package/dist/aggregate/index.js +4 -4
  3. package/dist/attestation/index.cjs.map +1 -1
  4. package/dist/attestation/index.d.cts +4 -4
  5. package/dist/attestation/index.d.ts +4 -4
  6. package/dist/attestation/index.js +6 -6
  7. package/dist/blobs/index.cjs.map +1 -1
  8. package/dist/blobs/index.d.cts +5 -5
  9. package/dist/blobs/index.d.ts +5 -5
  10. package/dist/blobs/index.js +5 -5
  11. package/dist/bundle/index.cjs +443 -338
  12. package/dist/bundle/index.cjs.map +1 -1
  13. package/dist/bundle/index.d.cts +17 -17
  14. package/dist/bundle/index.d.ts +17 -17
  15. package/dist/bundle/index.js +10 -10
  16. package/dist/bundle/index.js.map +1 -1
  17. package/dist/{chunk-YL2DR3HY.js → chunk-25WFLKOH.js} +2 -2
  18. package/dist/chunk-25WFLKOH.js.map +1 -0
  19. package/dist/{chunk-EMEX37ZN.js → chunk-2GMRNNI3.js} +3 -3
  20. package/dist/chunk-2GMRNNI3.js.map +1 -0
  21. package/dist/{chunk-NGSPBLLE.js → chunk-34XGYMQT.js} +3 -3
  22. package/dist/chunk-34XGYMQT.js.map +1 -0
  23. package/dist/{chunk-FXQYZNOW.js → chunk-5OVIFUQE.js} +1 -1
  24. package/dist/chunk-5OVIFUQE.js.map +1 -0
  25. package/dist/{chunk-P6256WTJ.js → chunk-5QPF2MJ5.js} +3 -3
  26. package/dist/chunk-5QPF2MJ5.js.map +1 -0
  27. package/dist/{chunk-5ZGZ6HIZ.js → chunk-5VMTAX4Y.js} +2 -2
  28. package/dist/{chunk-74JEQFMT.js → chunk-6A4AMQ2H.js} +5 -5
  29. package/dist/chunk-6A4AMQ2H.js.map +1 -0
  30. package/dist/{chunk-YDLAFP36.js → chunk-6HJ2ZALB.js} +1 -1
  31. package/dist/chunk-6HJ2ZALB.js.map +1 -0
  32. package/dist/{chunk-GDTCGIPX.js → chunk-7TX7HN42.js} +2 -2
  33. package/dist/chunk-7TX7HN42.js.map +1 -0
  34. package/dist/{chunk-EPK6A3WJ.js → chunk-A3JMGXPG.js} +2 -2
  35. package/dist/chunk-A3JMGXPG.js.map +1 -0
  36. package/dist/{chunk-75QDHSE4.js → chunk-A4JNVBPF.js} +5 -5
  37. package/dist/{chunk-IS5HWQO7.js → chunk-ARZAHCCF.js} +3 -3
  38. package/dist/{chunk-T6HQMVML.js → chunk-BT7544RM.js} +399 -301
  39. package/dist/chunk-BT7544RM.js.map +1 -0
  40. package/dist/{chunk-4OQWR46B.js → chunk-CCC25PA7.js} +5 -5
  41. package/dist/{chunk-NSLTPGEN.js → chunk-CGJFCT3X.js} +2 -2
  42. package/dist/{chunk-YK72A4IT.js → chunk-CKH247ZR.js} +4 -4
  43. package/dist/{chunk-HGZ7DC5H.js → chunk-DFCINPB5.js} +2 -2
  44. package/dist/chunk-DFCINPB5.js.map +1 -0
  45. package/dist/{chunk-4X2S7PBF.js → chunk-E225X5CQ.js} +3 -3
  46. package/dist/chunk-E225X5CQ.js.map +1 -0
  47. package/dist/{chunk-5YHWBPOT.js → chunk-ED3E3OLO.js} +2 -2
  48. package/dist/{chunk-UOF74WQY.js → chunk-EKTOYEZ3.js} +2 -2
  49. package/dist/{chunk-SAVQ6E2O.js → chunk-G26QAQNI.js} +2 -2
  50. package/dist/{chunk-YMYK7US4.js → chunk-HIELMTUK.js} +2 -2
  51. package/dist/{chunk-MRIBLZL3.js → chunk-ICH4AIGL.js} +1 -1
  52. package/dist/chunk-ICH4AIGL.js.map +1 -0
  53. package/dist/{chunk-LOL725S4.js → chunk-JSYTGEX4.js} +3 -3
  54. package/dist/{chunk-FBMXWVGP.js → chunk-KGFV72WK.js} +5 -5
  55. package/dist/{chunk-GVXBHCZ2.js → chunk-LJO6Q3X6.js} +5 -5
  56. package/dist/chunk-LJO6Q3X6.js.map +1 -0
  57. package/dist/{chunk-ZC2AAE6J.js → chunk-LWFQYT4N.js} +2 -2
  58. package/dist/chunk-LWFQYT4N.js.map +1 -0
  59. package/dist/{chunk-K5PVGKE4.js → chunk-MDIC4FAU.js} +2 -2
  60. package/dist/{chunk-A6SWRXUQ.js → chunk-NONMIU6C.js} +2 -2
  61. package/dist/{chunk-ZUMGGHRB.js → chunk-OPD3PZOG.js} +4 -4
  62. package/dist/{chunk-LS3JLEIB.js → chunk-PS5G6A3Y.js} +4 -4
  63. package/dist/{chunk-KYKMKLJ6.js → chunk-PX3MJ6RB.js} +3 -3
  64. package/dist/{chunk-FCDO7UAO.js → chunk-R4LTCI6O.js} +2 -2
  65. package/dist/{chunk-BFI3RS42.js → chunk-R7JTYCRX.js} +2 -2
  66. package/dist/chunk-R7JTYCRX.js.map +1 -0
  67. package/dist/{chunk-WRLHNG6H.js → chunk-RIHZBSWJ.js} +4 -4
  68. package/dist/chunk-RIHZBSWJ.js.map +1 -0
  69. package/dist/{chunk-UVPGJXVO.js → chunk-SGSHQ4PH.js} +5 -5
  70. package/dist/{chunk-TLFUDXVV.js → chunk-T6MTNGBM.js} +5 -5
  71. package/dist/chunk-T6MTNGBM.js.map +1 -0
  72. package/dist/{chunk-6S3LLAQ5.js → chunk-TNBIWSQ7.js} +2 -2
  73. package/dist/{chunk-GD3BGKAR.js → chunk-UGVDIOY7.js} +2 -2
  74. package/dist/{chunk-FS7A4XNF.js → chunk-WEA4TDTJ.js} +3 -3
  75. package/dist/{chunk-4UBOTYP5.js → chunk-XDW37COG.js} +5 -5
  76. package/dist/chunk-XDW37COG.js.map +1 -0
  77. package/dist/{chunk-QAU5HM6Q.js → chunk-XVJFFGTG.js} +3 -3
  78. package/dist/{chunk-2EYC3WDT.js → chunk-Y3P5DEMZ.js} +6 -6
  79. package/dist/chunk-Y3P5DEMZ.js.map +1 -0
  80. package/dist/{chunk-G7PAZ3TD.js → chunk-YEHUEUNP.js} +4 -4
  81. package/dist/chunk-YEHUEUNP.js.map +1 -0
  82. package/dist/{chunk-2XLVPKXG.js → chunk-YJ46RFCD.js} +2 -2
  83. package/dist/{chunk-KMI2NBBF.js → chunk-YZ6JETII.js} +6 -6
  84. package/dist/{chunk-NCO2JGKK.js → chunk-Z6FNBOTC.js} +1 -1
  85. package/dist/chunk-Z6FNBOTC.js.map +1 -0
  86. package/dist/{chunk-GAUBWHAF.js → chunk-ZQMYB56Z.js} +4 -4
  87. package/dist/consent/index.cjs.map +1 -1
  88. package/dist/consent/index.d.cts +5 -5
  89. package/dist/consent/index.d.ts +5 -5
  90. package/dist/consent/index.js +3 -3
  91. package/dist/{crypto-H2Y3DDFW.js → crypto-5UDZZL26.js} +3 -3
  92. package/dist/{delegation-QSC7G5QC.js → delegation-42LO4WFO.js} +5 -5
  93. package/dist/derivations/index.cjs +1 -1
  94. package/dist/derivations/index.cjs.map +1 -1
  95. package/dist/derivations/index.d.cts +8 -8
  96. package/dist/derivations/index.d.ts +8 -8
  97. package/dist/derivations/index.js +4 -4
  98. package/dist/{dev-unlock-Cf2B7Kih.d.ts → dev-unlock--ahUTrhc.d.ts} +1 -1
  99. package/dist/{dev-unlock-De3mjQWv.d.cts → dev-unlock-BIwt2V3p.d.cts} +1 -1
  100. package/dist/executor-AWCHQ2KN.js +8 -0
  101. package/dist/executor-RWICJI7J.js +11 -0
  102. package/dist/executor-SOLEQVUB.js +8 -0
  103. package/dist/{fanout-sidecar-NRBWSLRK.js → fanout-sidecar-EVICRM46.js} +2 -2
  104. package/dist/fanout-sidecar-EVICRM46.js.map +1 -0
  105. package/dist/guards/index.cjs +1 -1
  106. package/dist/guards/index.cjs.map +1 -1
  107. package/dist/guards/index.d.cts +6 -6
  108. package/dist/guards/index.d.ts +6 -6
  109. package/dist/guards/index.js +4 -4
  110. package/dist/{hash-vBCB0-Ps.d.cts → hash-BQVrGV-t.d.cts} +1 -1
  111. package/dist/{hash-gVn_uKhp.d.ts → hash-CJEFQxSD.d.ts} +1 -1
  112. package/dist/history/index.cjs.map +1 -1
  113. package/dist/history/index.d.cts +6 -6
  114. package/dist/history/index.d.ts +6 -6
  115. package/dist/history/index.js +6 -6
  116. package/dist/i18n/index.cjs.map +1 -1
  117. package/dist/i18n/index.d.cts +5 -5
  118. package/dist/i18n/index.d.ts +5 -5
  119. package/dist/i18n/index.js +7 -7
  120. package/dist/{index-DVkvrgpm.d.cts → index-5I0MZ0jQ.d.cts} +12 -12
  121. package/dist/{index-BF1B2HB9.d.ts → index-fIPPh5dg.d.ts} +12 -12
  122. package/dist/index.cjs +362 -264
  123. package/dist/index.cjs.map +1 -1
  124. package/dist/index.d.cts +20 -22
  125. package/dist/index.d.ts +20 -22
  126. package/dist/index.js +45 -45
  127. package/dist/index.js.map +1 -1
  128. package/dist/indexing/index.cjs +1 -1
  129. package/dist/indexing/index.cjs.map +1 -1
  130. package/dist/indexing/index.d.cts +3 -3
  131. package/dist/indexing/index.d.ts +3 -3
  132. package/dist/indexing/index.js +4 -4
  133. package/dist/issue-IODMTPME.js +12 -0
  134. package/dist/{lazy-builder-Rpd-V3jP.d.ts → lazy-builder-D1MyR1qH.d.ts} +2 -2
  135. package/dist/{lazy-builder-C-rPfWG0.d.cts → lazy-builder-DXlSCNCJ.d.cts} +2 -2
  136. package/dist/{ledger-WOEJUYTP.js → ledger-UX4QIHWI.js} +6 -6
  137. package/dist/materialized-views/index.cjs.map +1 -1
  138. package/dist/materialized-views/index.d.cts +18 -18
  139. package/dist/materialized-views/index.d.ts +18 -18
  140. package/dist/materialized-views/index.js +7 -7
  141. package/dist/noydb-6TADQIYH.js +34 -0
  142. package/dist/overlay-views/index.cjs +1 -1
  143. package/dist/overlay-views/index.cjs.map +1 -1
  144. package/dist/overlay-views/index.d.cts +8 -8
  145. package/dist/overlay-views/index.d.ts +8 -8
  146. package/dist/overlay-views/index.js +4 -4
  147. package/dist/periods/index.cjs.map +1 -1
  148. package/dist/periods/index.d.cts +5 -5
  149. package/dist/periods/index.d.ts +5 -5
  150. package/dist/periods/index.js +6 -6
  151. package/dist/{predicate-Dnu81tsS.d.cts → predicate-B0IKeBXx.d.cts} +1 -1
  152. package/dist/{predicate-Dnu81tsS.d.ts → predicate-B0IKeBXx.d.ts} +1 -1
  153. package/dist/{public-envelope-OHQ5UZFM.js → public-envelope-YKHKP74C.js} +4 -4
  154. package/dist/query/index.cjs +2 -2
  155. package/dist/query/index.cjs.map +1 -1
  156. package/dist/query/index.d.cts +2 -2
  157. package/dist/query/index.d.ts +2 -2
  158. package/dist/query/index.js +6 -6
  159. package/dist/registry-446I2NMN.js +8 -0
  160. package/dist/{registry-CDHASH73.js → registry-4NEW7LQY.js} +3 -3
  161. package/dist/registry-524KJZG4.js +8 -0
  162. package/dist/registry-DKEXOJVO.js +7 -0
  163. package/dist/{revoke-7JOVLZFD.js → revoke-R5NIQ74J.js} +6 -6
  164. package/dist/session/index.cjs.map +1 -1
  165. package/dist/session/index.d.cts +6 -6
  166. package/dist/session/index.d.ts +6 -6
  167. package/dist/session/index.js +3 -3
  168. package/dist/shadow/index.cjs.map +1 -1
  169. package/dist/shadow/index.d.cts +5 -5
  170. package/dist/shadow/index.d.ts +5 -5
  171. package/dist/shadow/index.js +2 -2
  172. package/dist/{signer-M4K5HBLD.js → signer-WGDJNWSU.js} +5 -5
  173. package/dist/{stale-PAGCS4K5.js → stale-74WGLVZ2.js} +2 -2
  174. package/dist/store/index.cjs.map +1 -1
  175. package/dist/store/index.d.cts +5 -5
  176. package/dist/store/index.d.ts +5 -5
  177. package/dist/store/index.js +2 -2
  178. package/dist/sync/index.cjs.map +1 -1
  179. package/dist/sync/index.d.cts +4 -4
  180. package/dist/sync/index.d.ts +4 -4
  181. package/dist/sync/index.js +4 -4
  182. package/dist/team/index.cjs +1 -1
  183. package/dist/team/index.cjs.map +1 -1
  184. package/dist/team/index.d.cts +5 -5
  185. package/dist/team/index.d.ts +5 -5
  186. package/dist/team/index.js +8 -8
  187. package/dist/tx/index.cjs +2 -2
  188. package/dist/tx/index.cjs.map +1 -1
  189. package/dist/tx/index.d.cts +5 -5
  190. package/dist/tx/index.d.ts +5 -5
  191. package/dist/tx/index.js +3 -3
  192. package/dist/tx/index.js.map +1 -1
  193. package/dist/{types-D9eB0Rvh.d.ts → types-BV4AZKmx.d.ts} +340 -302
  194. package/dist/{types-CSLcfytP.d.cts → types-BeKi0hCx.d.cts} +340 -302
  195. package/dist/{ulid-CiM2OAeM.d.ts → ulid-CQc0eBxE.d.ts} +19 -19
  196. package/dist/{ulid-CG2YvAbg.d.cts → ulid-Cvljl7ZZ.d.cts} +19 -19
  197. package/dist/util/index.cjs.map +1 -1
  198. package/dist/util/index.js +1 -1
  199. package/dist/{with-derivation-Bzpj6UTv.d.ts → with-derivation-BWcwmevt.d.ts} +1 -1
  200. package/dist/{with-derivation-DWajFh4K.d.cts → with-derivation-BkOBDhsu.d.cts} +1 -1
  201. package/dist/{with-guard-DF_Ul3DT.d.cts → with-guard-BD4Hyu8s.d.cts} +1 -1
  202. package/dist/{with-guard-DR7U-l4v.d.ts → with-guard-Du54s3Ti.d.ts} +1 -1
  203. package/dist/{with-materialized-view-qtoJ3xKJ.d.ts → with-materialized-view-B5W4wFAC.d.ts} +2 -2
  204. package/dist/{with-materialized-view-_piodoIz.d.cts → with-materialized-view-BCPPZdjC.d.cts} +2 -2
  205. package/dist/{with-overlayed-view-DFaRfgMr.d.ts → with-overlayed-view-B8RrlLsG.d.cts} +2 -2
  206. package/dist/{with-overlayed-view-DwzCKxn2.d.cts → with-overlayed-view-Cw-h9p9N.d.ts} +2 -2
  207. package/package.json +3 -3
  208. package/dist/chunk-2EYC3WDT.js.map +0 -1
  209. package/dist/chunk-4UBOTYP5.js.map +0 -1
  210. package/dist/chunk-4X2S7PBF.js.map +0 -1
  211. package/dist/chunk-74JEQFMT.js.map +0 -1
  212. package/dist/chunk-BFI3RS42.js.map +0 -1
  213. package/dist/chunk-EMEX37ZN.js.map +0 -1
  214. package/dist/chunk-EPK6A3WJ.js.map +0 -1
  215. package/dist/chunk-FXQYZNOW.js.map +0 -1
  216. package/dist/chunk-G7PAZ3TD.js.map +0 -1
  217. package/dist/chunk-GDTCGIPX.js.map +0 -1
  218. package/dist/chunk-GVXBHCZ2.js.map +0 -1
  219. package/dist/chunk-HGZ7DC5H.js.map +0 -1
  220. package/dist/chunk-MRIBLZL3.js.map +0 -1
  221. package/dist/chunk-NCO2JGKK.js.map +0 -1
  222. package/dist/chunk-NGSPBLLE.js.map +0 -1
  223. package/dist/chunk-P6256WTJ.js.map +0 -1
  224. package/dist/chunk-T6HQMVML.js.map +0 -1
  225. package/dist/chunk-TLFUDXVV.js.map +0 -1
  226. package/dist/chunk-WRLHNG6H.js.map +0 -1
  227. package/dist/chunk-YDLAFP36.js.map +0 -1
  228. package/dist/chunk-YL2DR3HY.js.map +0 -1
  229. package/dist/chunk-ZC2AAE6J.js.map +0 -1
  230. package/dist/executor-BZKFZVRC.js +0 -8
  231. package/dist/executor-GFZFDQXV.js +0 -8
  232. package/dist/executor-KT2IOZVP.js +0 -11
  233. package/dist/fanout-sidecar-NRBWSLRK.js.map +0 -1
  234. package/dist/issue-BAJ7ZB4S.js +0 -12
  235. package/dist/noydb-XNQSKXGO.js +0 -34
  236. package/dist/registry-2IEARCGT.js +0 -7
  237. package/dist/registry-EMGLZGR6.js +0 -8
  238. package/dist/registry-NQALYR77.js +0 -8
  239. /package/dist/{chunk-5ZGZ6HIZ.js.map → chunk-5VMTAX4Y.js.map} +0 -0
  240. /package/dist/{chunk-75QDHSE4.js.map → chunk-A4JNVBPF.js.map} +0 -0
  241. /package/dist/{chunk-IS5HWQO7.js.map → chunk-ARZAHCCF.js.map} +0 -0
  242. /package/dist/{chunk-4OQWR46B.js.map → chunk-CCC25PA7.js.map} +0 -0
  243. /package/dist/{chunk-NSLTPGEN.js.map → chunk-CGJFCT3X.js.map} +0 -0
  244. /package/dist/{chunk-YK72A4IT.js.map → chunk-CKH247ZR.js.map} +0 -0
  245. /package/dist/{chunk-5YHWBPOT.js.map → chunk-ED3E3OLO.js.map} +0 -0
  246. /package/dist/{chunk-UOF74WQY.js.map → chunk-EKTOYEZ3.js.map} +0 -0
  247. /package/dist/{chunk-SAVQ6E2O.js.map → chunk-G26QAQNI.js.map} +0 -0
  248. /package/dist/{chunk-YMYK7US4.js.map → chunk-HIELMTUK.js.map} +0 -0
  249. /package/dist/{chunk-LOL725S4.js.map → chunk-JSYTGEX4.js.map} +0 -0
  250. /package/dist/{chunk-FBMXWVGP.js.map → chunk-KGFV72WK.js.map} +0 -0
  251. /package/dist/{chunk-K5PVGKE4.js.map → chunk-MDIC4FAU.js.map} +0 -0
  252. /package/dist/{chunk-A6SWRXUQ.js.map → chunk-NONMIU6C.js.map} +0 -0
  253. /package/dist/{chunk-ZUMGGHRB.js.map → chunk-OPD3PZOG.js.map} +0 -0
  254. /package/dist/{chunk-LS3JLEIB.js.map → chunk-PS5G6A3Y.js.map} +0 -0
  255. /package/dist/{chunk-KYKMKLJ6.js.map → chunk-PX3MJ6RB.js.map} +0 -0
  256. /package/dist/{chunk-FCDO7UAO.js.map → chunk-R4LTCI6O.js.map} +0 -0
  257. /package/dist/{chunk-UVPGJXVO.js.map → chunk-SGSHQ4PH.js.map} +0 -0
  258. /package/dist/{chunk-6S3LLAQ5.js.map → chunk-TNBIWSQ7.js.map} +0 -0
  259. /package/dist/{chunk-GD3BGKAR.js.map → chunk-UGVDIOY7.js.map} +0 -0
  260. /package/dist/{chunk-FS7A4XNF.js.map → chunk-WEA4TDTJ.js.map} +0 -0
  261. /package/dist/{chunk-QAU5HM6Q.js.map → chunk-XVJFFGTG.js.map} +0 -0
  262. /package/dist/{chunk-2XLVPKXG.js.map → chunk-YJ46RFCD.js.map} +0 -0
  263. /package/dist/{chunk-KMI2NBBF.js.map → chunk-YZ6JETII.js.map} +0 -0
  264. /package/dist/{chunk-GAUBWHAF.js.map → chunk-ZQMYB56Z.js.map} +0 -0
  265. /package/dist/{crypto-H2Y3DDFW.js.map → crypto-5UDZZL26.js.map} +0 -0
  266. /package/dist/{delegation-QSC7G5QC.js.map → delegation-42LO4WFO.js.map} +0 -0
  267. /package/dist/{executor-BZKFZVRC.js.map → executor-AWCHQ2KN.js.map} +0 -0
  268. /package/dist/{executor-GFZFDQXV.js.map → executor-RWICJI7J.js.map} +0 -0
  269. /package/dist/{executor-KT2IOZVP.js.map → executor-SOLEQVUB.js.map} +0 -0
  270. /package/dist/{issue-BAJ7ZB4S.js.map → issue-IODMTPME.js.map} +0 -0
  271. /package/dist/{ledger-WOEJUYTP.js.map → ledger-UX4QIHWI.js.map} +0 -0
  272. /package/dist/{noydb-XNQSKXGO.js.map → noydb-6TADQIYH.js.map} +0 -0
  273. /package/dist/{public-envelope-OHQ5UZFM.js.map → public-envelope-YKHKP74C.js.map} +0 -0
  274. /package/dist/{registry-2IEARCGT.js.map → registry-446I2NMN.js.map} +0 -0
  275. /package/dist/{registry-CDHASH73.js.map → registry-4NEW7LQY.js.map} +0 -0
  276. /package/dist/{registry-EMGLZGR6.js.map → registry-524KJZG4.js.map} +0 -0
  277. /package/dist/{registry-NQALYR77.js.map → registry-DKEXOJVO.js.map} +0 -0
  278. /package/dist/{revoke-7JOVLZFD.js.map → revoke-R5NIQ74J.js.map} +0 -0
  279. /package/dist/{signer-M4K5HBLD.js.map → signer-WGDJNWSU.js.map} +0 -0
  280. /package/dist/{stale-PAGCS4K5.js.map → stale-74WGLVZ2.js.map} +0 -0
@@ -1724,7 +1724,7 @@ async function changeSecret(adapter, vault, keyring, newPassphrase, passphraseOp
1724
1724
  // Tier-2 slots are NOT preserved through `changeSecret` —
1725
1725
  // each slot wraps the OLD KEK, so the new keyring has no
1726
1726
  // authenticator slots until the user re-enrolls. The higher-level
1727
- // `db.rotatePassphrase()` (#10) preserves slots by rewrapping the
1727
+ // `db.rotatePassphrase()` preserves slots by rewrapping the
1728
1728
  // KEK reference, not the KEK itself.
1729
1729
  authenticators: [],
1730
1730
  ...keyring.policy !== void 0 && { policy: keyring.policy }
@@ -4173,7 +4173,7 @@ var init_builder = __esm({
4173
4173
  /**
4174
4174
  * @internal — clone this Query with a declared-predicate map
4175
4175
  * attached. Used by the materialized-view registry to enable
4176
- * `.wherePredicate(name, ctx?)` for the MV's query callback (#153).
4176
+ * `.wherePredicate(name, ctx?)` for the MV's query callback.
4177
4177
  * Consumers don't call this directly.
4178
4178
  */
4179
4179
  _withPredicates(predicates) {
@@ -4186,7 +4186,7 @@ var init_builder = __esm({
4186
4186
  );
4187
4187
  }
4188
4188
  /**
4189
- * Filter by a registered deterministic predicate (#153). Requires
4189
+ * Filter by a registered deterministic predicate. Requires
4190
4190
  * the Query to have been augmented with a predicates map (typically
4191
4191
  * via the materialized-view registry — bare Queries constructed
4192
4192
  * outside an MV throw on `.wherePredicate()`).
@@ -5604,7 +5604,7 @@ var init_transaction = __esm({
5604
5604
  init_errors();
5605
5605
  init_ulid();
5606
5606
  TxContext = class {
5607
- /** Stable id for this transaction; shared by all writes it performs (#230). */
5607
+ /** Stable id for this transaction; shared by all writes it performs. */
5608
5608
  txId = generateULID();
5609
5609
  /** @internal */
5610
5610
  _ops = [];
@@ -5614,7 +5614,7 @@ var init_transaction = __esm({
5614
5614
  * restore prior state via `revertExecuted`. Side-effect writes (e.g.
5615
5615
  * recursive derivation outputs fired inside `Collection.put`) are
5616
5616
  * appended here in execution order so they roll back alongside the
5617
- * main staged ops (#133).
5617
+ * main staged ops.
5618
5618
  */
5619
5619
  _executed = [];
5620
5620
  /** @internal */
@@ -5914,7 +5914,7 @@ async function resolveStaleOnRead(accessor, outputCollection, id) {
5914
5914
  }
5915
5915
  if (out.kind === "array") {
5916
5916
  console.warn(
5917
- `[derivation] unexpected array-shape output "${key}" in lazy resolve path; array-shape derivations require lifecycle: "eager" (#200 slice 1).`
5917
+ `[derivation] unexpected array-shape output "${key}" in lazy resolve path; array-shape derivations require lifecycle: "eager".`
5918
5918
  );
5919
5919
  continue;
5920
5920
  }
@@ -6436,81 +6436,6 @@ var init_stale2 = __esm({
6436
6436
  }
6437
6437
  });
6438
6438
 
6439
- // src/guards/executor.ts
6440
- var executor_exports3 = {};
6441
- __export(executor_exports3, {
6442
- GuardExecutor: () => GuardExecutor
6443
- });
6444
- function deepEqual(a, b) {
6445
- if (a === b) return true;
6446
- if (a === null || b === null) return a === b;
6447
- if (typeof a !== typeof b) return false;
6448
- if (typeof a !== "object") return a === b;
6449
- if (Array.isArray(a) !== Array.isArray(b)) return false;
6450
- if (Array.isArray(a)) {
6451
- const aa = a;
6452
- const bb = b;
6453
- if (aa.length !== bb.length) return false;
6454
- for (let i = 0; i < aa.length; i++) if (!deepEqual(aa[i], bb[i])) return false;
6455
- return true;
6456
- }
6457
- const ao = a;
6458
- const bo = b;
6459
- const ak = Object.keys(ao);
6460
- const bk = Object.keys(bo);
6461
- if (ak.length !== bk.length) return false;
6462
- for (const k of ak) {
6463
- if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
6464
- if (!deepEqual(ao[k], bo[k])) return false;
6465
- }
6466
- return true;
6467
- }
6468
- var GuardExecutor;
6469
- var init_executor3 = __esm({
6470
- "src/guards/executor.ts"() {
6471
- "use strict";
6472
- init_errors();
6473
- GuardExecutor = {
6474
- /**
6475
- * Compare existing vs incoming for each `frozenFields.fields` entry
6476
- * when `frozenFields.when(existing)` is true. Throws
6477
- * `FieldFrozenError` listing every changed frozen field.
6478
- */
6479
- async checkFrozenFields(guard, id, existing, incoming) {
6480
- const ff = guard.frozenFields;
6481
- if (!ff) return;
6482
- if (existing === null) return;
6483
- if (!ff.when(existing)) return;
6484
- const changed = [];
6485
- for (const f of ff.fields) {
6486
- if (existing[f] !== incoming[f]) {
6487
- if (!deepEqual(existing[f], incoming[f])) changed.push(String(f));
6488
- }
6489
- }
6490
- if (changed.length > 0) {
6491
- throw new FieldFrozenError(guard.collection, id, changed);
6492
- }
6493
- },
6494
- /**
6495
- * Run a single guard's invariant over its slice of the change-set.
6496
- * Any throw is converted to `InvariantError` unless it already is one.
6497
- */
6498
- async runInvariant(guard, changes, ctx) {
6499
- const amendment = guard.amendment;
6500
- if (!amendment) return;
6501
- try {
6502
- await amendment.invariant(changes, ctx);
6503
- } catch (err) {
6504
- if (err instanceof InvariantError) throw err;
6505
- throw new InvariantError(
6506
- err instanceof Error ? err.message : `invariant violated: ${String(err)}`
6507
- );
6508
- }
6509
- }
6510
- };
6511
- }
6512
- });
6513
-
6514
6439
  // src/derivations/fanout-sidecar.ts
6515
6440
  var fanout_sidecar_exports = {};
6516
6441
  __export(fanout_sidecar_exports, {
@@ -6648,6 +6573,7 @@ var init_collection = __esm({
6648
6573
  schemaUpdateGate;
6649
6574
  schemaFence;
6650
6575
  writeHooks;
6576
+ subsystemBus;
6651
6577
  activeTxId;
6652
6578
  getDEK;
6653
6579
  onDirty;
@@ -6836,42 +6762,14 @@ var init_collection = __esm({
6836
6762
  syncAdapter;
6837
6763
  /** — consent-audit hook, no-op when no scope is active. */
6838
6764
  onAccess;
6839
- /**
6840
- * accounting-period write guard. Called BEFORE any
6841
- * adapter write with:
6842
- * - `existing` — the prior envelope's `_ts` and decrypted record
6843
- * (or `null` if no prior envelope exists)
6844
- * - `incoming` — the record being written (or `null` for delete)
6845
- *
6846
- * Throws `PeriodClosedError` if either side falls inside a closed
6847
- * period. Installed by Vault; no-op when no period has been closed.
6848
- * Async so the Vault can lazy-load the period list from the
6849
- * adapter on first use.
6850
- */
6851
- periodGuard;
6852
- /**
6853
- * Optional back-reference to the owning vault's guard registry + a
6854
- * read-only vault facade. When present, `Collection.put` and
6855
- * `Collection.delete` consult the registry for guards declared
6856
- * against this collection and run their `check` + `frozenFields`
6857
- * before the adapter write. Absent in unit tests that construct
6858
- * a Collection directly; production code always sets it via
6859
- * `Vault.collection()`.
6860
- *
6861
- * Typed structurally rather than as `Vault` to avoid a circular
6862
- * import (mirrors the `refEnforcer` / `joinResolver` pattern).
6863
- */
6864
- guardSource;
6865
6765
  /**
6866
6766
  * Vault-internal hook for derivation dispatch. When set,
6867
6767
  * `Collection.put` consults the registry after the source-write
6868
6768
  * commits and writes derived outputs through `getCollection(name).put`.
6869
- * Same structural-interface pattern as `guardSource` to avoid a
6870
- * circular Vault import.
6871
6769
  */
6872
6770
  derivationSource;
6873
6771
  /**
6874
- * Vault-internal hook for materialized-view dispatch (#143/#150).
6772
+ * Vault-internal hook for materialized-view dispatch.
6875
6773
  * Parallel to `derivationSource` — when set, `Collection.put` fires
6876
6774
  * `MaterializedViewRegistry.onSourceWrite` after the source-write
6877
6775
  * commits + after `dispatchDerivations` has run.
@@ -6927,6 +6825,7 @@ var init_collection = __esm({
6927
6825
  this.schemaUpdateGate = opts.schemaUpdateGate;
6928
6826
  this.schemaFence = opts.schemaFence;
6929
6827
  this.writeHooks = opts.writeHooks;
6828
+ this.subsystemBus = opts.subsystemBus;
6930
6829
  this.activeTxId = opts.activeTxId;
6931
6830
  this.blobStrategy = opts.blobStrategy ?? NO_BLOBS;
6932
6831
  this.aggregateStrategy = opts.aggregateStrategy ?? NO_AGGREGATE;
@@ -6951,8 +6850,6 @@ var init_collection = __esm({
6951
6850
  this.crdtMode = opts.crdt;
6952
6851
  this.syncAdapter = opts.syncAdapter;
6953
6852
  this.onAccess = opts.onAccess;
6954
- this.periodGuard = opts.periodGuard;
6955
- this.guardSource = opts.guardSource;
6956
6853
  this.derivationSource = opts.derivationSource;
6957
6854
  this.materializedViewSource = opts.materializedViewSource;
6958
6855
  this.tiers = opts.tiers && opts.tiers.length > 0 ? new Set(opts.tiers) : null;
@@ -7154,21 +7051,23 @@ var init_collection = __esm({
7154
7051
  }
7155
7052
  /**
7156
7053
  * Create or update a record. Runs inside the hub's write-queue tracker
7157
- * (#227) so `hub.writeQueue.pending` reflects this write.
7054
+ * so `hub.writeQueue.pending` reflects this write.
7158
7055
  *
7159
7056
  * @param id Record identifier.
7160
7057
  * @param record The record body (validated by the collection's schema
7161
7058
  * if one was attached at `vault.collection(...)` time).
7162
7059
  * @param options Optional metadata for audit + import workflows.
7163
7060
  * `reason` is stamped onto the resulting ledger entry
7164
- * (see #1) so audit consumers can filter via
7061
+ * so audit consumers can filter via
7165
7062
  * `entries.filter(e => e.reason?.startsWith('import:'))`.
7166
7063
  */
7167
7064
  async put(id, record, options) {
7168
7065
  await this.schemaUpdateGate?.assertWritable();
7169
7066
  await this.schemaFence?.assertWritable(this.name);
7067
+ const hooksActive = this.#hooksActive();
7068
+ const busAfterPut = (this.subsystemBus?.hasHandlers("afterPut") ?? false) && !(this.subsystemBus?.dispatching ?? false);
7170
7069
  let event;
7171
- if (this.#hooksActive()) {
7070
+ if (hooksActive || busAfterPut) {
7172
7071
  const prior = await this.#priorForHook(id);
7173
7072
  event = {
7174
7073
  op: prior.record === null ? "create" : "update",
@@ -7183,23 +7082,26 @@ var init_collection = __esm({
7183
7082
  baseVersion: prior.version,
7184
7083
  version: prior.version + 1
7185
7084
  };
7186
- await this.writeHooks.runBefore(event);
7085
+ if (hooksActive) await this.writeHooks.runBefore(event);
7187
7086
  }
7188
7087
  if (this.writeQueue) await this.writeQueue.track(() => this.putInternal(id, record, options));
7189
7088
  else await this.putInternal(id, record, options);
7190
- if (event) await this.writeHooks.runAfter(event);
7089
+ if (event) {
7090
+ if (hooksActive) await this.writeHooks.runAfter(event);
7091
+ if (busAfterPut) await this.subsystemBus.dispatch("afterPut", event);
7092
+ }
7191
7093
  }
7192
- /** @internal #230 — true when hooks should fire for this write (handlers exist, not re-entrant). */
7094
+ /** @internal — true when hooks should fire for this write (handlers exist, not re-entrant). */
7193
7095
  #hooksActive() {
7194
7096
  return this.writeHooks !== void 0 && this.writeHooks.hasHandlers && !this.writeHooks.suppressed;
7195
7097
  }
7196
7098
  /**
7197
- * @internal #230/#228c — resolve the prior record for a hook's `before` and
7099
+ * @internal — resolve the prior record for a hook's `before` and
7198
7100
  * its version. Critically, this uses the SAME basis `putInternal` writes from
7199
7101
  * (the in-memory cache in eager mode; lru-then-adapter in lazy) — NOT a fresh
7200
7102
  * store read — so `baseVersion`/`version` match the version actually written.
7201
7103
  * A separate store read would diverge once another tab has advanced the shared
7202
- * store past this tab's cache, breaking #228c conflict detection.
7104
+ * store past this tab's cache, breaking cross-tab conflict detection.
7203
7105
  */
7204
7106
  async #priorForHook(id) {
7205
7107
  if (this.lazy && this.lru) {
@@ -7221,52 +7123,28 @@ var init_collection = __esm({
7221
7123
  if (!hasWritePermission(this.keyring, this.name)) {
7222
7124
  throw new ReadOnlyError();
7223
7125
  }
7224
- if (this.guardSource) {
7225
- const registry = this.guardSource.registry();
7226
- const guards = registry.guardsFor(this.name);
7227
- if (guards.length > 0) {
7228
- const existingEnv = await this.adapter.get(this.vault, this.name, id);
7229
- let existingRecord = null;
7230
- if (existingEnv) {
7231
- try {
7232
- existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
7233
- } catch {
7234
- existingRecord = null;
7235
- }
7236
- }
7237
- const incomingRecord = record;
7238
- const ctx = {
7239
- existing: existingRecord,
7240
- vault: this.guardSource.readOnlyVault(),
7241
- userId: this.keyring.userId,
7242
- role: this.keyring.role
7243
- };
7244
- if (registry.isAmendmentActive()) {
7245
- const vBefore = existingEnv?._v ?? 0;
7246
- registry.collectChange(this.name, id, existingRecord, incomingRecord, vBefore, vBefore + 1);
7247
- } else {
7248
- await registry.runChecks(this.name, incomingRecord, ctx);
7249
- const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor3(), executor_exports3));
7250
- for (const g of guards) {
7251
- await GuardExecutor2.checkFrozenFields(g, id, existingRecord, incomingRecord);
7252
- }
7253
- }
7254
- }
7255
- }
7256
- if (this.periodGuard !== void 0) {
7126
+ if (this.subsystemBus?.hasGateHandlers("beforePut")) {
7257
7127
  const existingEnv = await this.adapter.get(this.vault, this.name, id);
7258
- let priorRecord = null;
7128
+ let existingRecord = null;
7259
7129
  if (existingEnv) {
7260
7130
  try {
7261
- priorRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
7131
+ existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
7262
7132
  } catch {
7263
- priorRecord = null;
7133
+ existingRecord = null;
7264
7134
  }
7265
7135
  }
7266
- await this.periodGuard(
7267
- existingEnv ? { ts: existingEnv._ts, record: priorRecord } : null,
7268
- record
7269
- );
7136
+ await this.subsystemBus.dispatchGate("beforePut", {
7137
+ op: existingEnv ? "update" : "create",
7138
+ vault: this.vault,
7139
+ collection: this.name,
7140
+ docId: id,
7141
+ incoming: record,
7142
+ existing: existingRecord,
7143
+ existingVersion: existingEnv?._v ?? 0,
7144
+ existingTs: existingEnv?._ts,
7145
+ userId: this.keyring.userId,
7146
+ role: this.keyring.role
7147
+ });
7270
7148
  }
7271
7149
  if (this.schema !== void 0) {
7272
7150
  record = await validateSchemaInput(this.schema, record, `put(${id})`);
@@ -7452,7 +7330,7 @@ var init_collection = __esm({
7452
7330
  * Fire registered MV strategies whose dependency set includes this
7453
7331
  * collection. Eager-mode MVs re-materialize inline via
7454
7332
  * `MaterializedViewExecutor.refresh`; lazy / manual modes are
7455
- * no-ops in the foundation (subtask #150) wired in #151.
7333
+ * no-ops in the foundation; wired in the lazy-mode implementation.
7456
7334
  *
7457
7335
  * Skips entirely when the record being written is itself an
7458
7336
  * MV-emitted row (carries `_materializedFrom`) — defensive guard
@@ -7596,13 +7474,15 @@ var init_collection = __esm({
7596
7474
  }
7597
7475
  /**
7598
7476
  * Delete a record by ID. Runs inside the hub's write-queue tracker
7599
- * (#227) so `hub.writeQueue.pending` reflects this write.
7477
+ * so `hub.writeQueue.pending` reflects this write.
7600
7478
  */
7601
7479
  async delete(id) {
7602
7480
  await this.schemaUpdateGate?.assertWritable();
7603
7481
  await this.schemaFence?.assertWritable(this.name);
7482
+ const hooksActive = this.#hooksActive();
7483
+ const busAfterDelete = (this.subsystemBus?.hasHandlers("afterDelete") ?? false) && !(this.subsystemBus?.dispatching ?? false);
7604
7484
  let event;
7605
- if (this.#hooksActive()) {
7485
+ if (hooksActive || busAfterDelete) {
7606
7486
  const prior = await this.#priorForHook(id);
7607
7487
  event = {
7608
7488
  op: "delete",
@@ -7617,14 +7497,17 @@ var init_collection = __esm({
7617
7497
  baseVersion: prior.version,
7618
7498
  version: prior.version + 1
7619
7499
  };
7620
- await this.writeHooks.runBefore(event);
7500
+ if (hooksActive) await this.writeHooks.runBefore(event);
7621
7501
  }
7622
7502
  if (this.writeQueue) await this.writeQueue.track(() => this.deleteInternal(id));
7623
7503
  else await this.deleteInternal(id);
7624
- if (event) await this.writeHooks.runAfter(event);
7504
+ if (event) {
7505
+ if (hooksActive) await this.writeHooks.runAfter(event);
7506
+ if (busAfterDelete) await this.subsystemBus.dispatch("afterDelete", event);
7507
+ }
7625
7508
  }
7626
7509
  /**
7627
- * @internal #232 — bulk-rewrite every record through a cutover transform.
7510
+ * @internal — bulk-rewrite every record through a cutover transform.
7628
7511
  * Raw adapter path (bypasses the write gate + guards — the transform is
7629
7512
  * trusted and runs only during the `migrating` phase). Bumps each
7630
7513
  * record's `_v` and appends a ledger `op:'migration'` entry.
@@ -7663,8 +7546,7 @@ var init_collection = __esm({
7663
7546
  }
7664
7547
  /**
7665
7548
  * @internal — system-internal delete that bypasses user-facing
7666
- * delete hooks (`onDelete`, accounting-period guard, FK ref
7667
- * enforcer). Used by derivation tombstones (#144) and MV refresh
7549
+ * delete hooks (`onDelete`, FK ref enforcer). Used by derivation tombstones and MV refresh
7668
7550
  * (Dim 14 v2) — system housekeeping shouldn't trip user invariants
7669
7551
  * registered against the output collection. The ledger entry and
7670
7552
  * history snapshot still fire so backup integrity and time-travel
@@ -7676,7 +7558,7 @@ var init_collection = __esm({
7676
7558
  *
7677
7559
  * When a `txCtx` is supplied, the prior envelope is captured and
7678
7560
  * pushed onto `txCtx._executed` BEFORE the delete fires — mirrors
7679
- * the #133 rollback hardening for puts. Callers outside a
7561
+ * the rollback hardening for puts. Callers outside a
7680
7562
  * multi-record transaction pass `null` and skip the tracking.
7681
7563
  *
7682
7564
  * Amendment composition: if `_internalDelete` runs while a vault's
@@ -7719,58 +7601,27 @@ var init_collection = __esm({
7719
7601
  if (!hasWritePermission(this.keyring, this.name)) {
7720
7602
  throw new ReadOnlyError();
7721
7603
  }
7722
- if (this.guardSource) {
7723
- const registry = this.guardSource.registry();
7724
- const guards = registry.guardsFor(this.name);
7725
- if (guards.length > 0) {
7726
- const existingEnv = await this.adapter.get(this.vault, this.name, id);
7727
- if (existingEnv) {
7728
- let existingRecord = null;
7729
- try {
7730
- existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
7731
- } catch {
7732
- existingRecord = null;
7733
- }
7734
- if (registry.isAmendmentActive()) {
7735
- const vBefore = existingEnv._v;
7736
- registry.collectChange(
7737
- this.name,
7738
- id,
7739
- existingRecord,
7740
- null,
7741
- vBefore,
7742
- vBefore
7743
- );
7744
- } else if (!internal) {
7745
- const ctx = {
7746
- existing: existingRecord,
7747
- vault: this.guardSource.readOnlyVault(),
7748
- userId: this.keyring.userId,
7749
- role: this.keyring.role
7750
- };
7751
- await registry.runOnDelete(
7752
- this.name,
7753
- existingRecord ?? {},
7754
- ctx
7755
- );
7756
- }
7757
- }
7758
- }
7759
- }
7760
- if (!internal && this.periodGuard !== void 0) {
7604
+ if (this.subsystemBus?.hasGateHandlers("beforeDelete")) {
7761
7605
  const existingEnv = await this.adapter.get(this.vault, this.name, id);
7762
- let priorRecord = null;
7763
7606
  if (existingEnv) {
7607
+ let existingRecord = null;
7764
7608
  try {
7765
- priorRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
7609
+ existingRecord = await this.decryptRecord(existingEnv, { skipValidation: true });
7766
7610
  } catch {
7767
- priorRecord = null;
7611
+ existingRecord = null;
7768
7612
  }
7613
+ await this.subsystemBus.dispatchGate("beforeDelete", {
7614
+ vault: this.vault,
7615
+ collection: this.name,
7616
+ docId: id,
7617
+ existing: existingRecord,
7618
+ existingVersion: existingEnv._v,
7619
+ existingTs: existingEnv._ts,
7620
+ internal,
7621
+ userId: this.keyring.userId,
7622
+ role: this.keyring.role
7623
+ });
7769
7624
  }
7770
- await this.periodGuard(
7771
- existingEnv ? { ts: existingEnv._ts, record: priorRecord } : null,
7772
- null
7773
- );
7774
7625
  }
7775
7626
  if (!internal && this.refEnforcer !== void 0) {
7776
7627
  await this.refEnforcer.enforceRefsOnDelete(this.name, id);
@@ -7831,7 +7682,7 @@ var init_collection = __esm({
7831
7682
  }
7832
7683
  /**
7833
7684
  * Cascade deletes of array-shape derived rows when a source row is
7834
- * deleted (#200). Reads each registered strategy's fanout sidecar
7685
+ * deleted. Reads each registered strategy's fanout sidecar
7835
7686
  * for this source id, deletes every listed derived row, then
7836
7687
  * deletes the sidecar itself.
7837
7688
  *
@@ -7870,8 +7721,8 @@ var init_collection = __esm({
7870
7721
  }
7871
7722
  }
7872
7723
  /**
7873
- * Mirror of {@link dispatchMaterializedViews} for the delete path
7874
- * (#181). No record content is available (it's gone), so the
7724
+ * Mirror of {@link dispatchMaterializedViews} for the delete path.
7725
+ * No record content is available (it's gone), so the
7875
7726
  * `_materializedFrom` skip used by the put-side dispatch doesn't
7876
7727
  * apply here — instead, the recursion guard is the `internal` gate
7877
7728
  * at the `_doDelete` call site above.
@@ -8401,7 +8252,7 @@ var init_collection = __esm({
8401
8252
  * .aggregate({ total: sum('amount'), n: count() })
8402
8253
  * ```
8403
8254
  *
8404
- * **Lazy-MV gap (#157):** `scan()` is synchronous-build and does
8255
+ * **Lazy-MV gap:** `scan()` is synchronous-build and does
8405
8256
  * NOT trigger lazy materialized-view resolve-on-read. For lazy
8406
8257
  * MVs, call `list()` (which DOES resolve) or `vault.refreshView(name)`
8407
8258
  * before scanning. Same shape as the `query()` limitation.
@@ -8482,7 +8333,7 @@ var init_collection = __esm({
8482
8333
  this.indexes?.upsert(id, record, previous ? previous.record : null);
8483
8334
  }
8484
8335
  /**
8485
- * #228b — apply a peer tab's committed write to THIS tab's in-memory view:
8336
+ * Apply a peer tab's committed write to THIS tab's in-memory view:
8486
8337
  * re-read the (already-persisted) envelope from the shared store + refresh
8487
8338
  * cache/indexes, then emit a `change` event so reactive consumers re-render.
8488
8339
  * Never writes to the store and never fires write hooks, so it cannot loop.
@@ -8491,7 +8342,7 @@ var init_collection = __esm({
8491
8342
  await this._invalidateCacheEntry(id);
8492
8343
  this.emitter.emit("change", { vault: this.vault, collection: this.name, id, action });
8493
8344
  }
8494
- /** @internal #228c — the current in-memory record without a store read (for conflict capture). */
8345
+ /** @internal — the current in-memory record without a store read (for conflict capture). */
8495
8346
  _peekCached(id) {
8496
8347
  const entry = this.lazy && this.lru ? this.lru.get(id) : this.cache.get(id);
8497
8348
  return entry ? entry.record : null;
@@ -9511,7 +9362,7 @@ var init_virtual_collection = __esm({
9511
9362
  // error pointing at the relevant issue — so consumers don't hit a
9512
9363
  // cryptic `undefined is not a function` runtime crash.
9513
9364
  //
9514
- // Closes niwat-review of PR #160.
9365
+ // Throw-stubs so consumers get actionable errors rather than cryptic crashes.
9515
9366
  /** @throws — chainable Query<T> over a virtual collection is deferred. */
9516
9367
  query() {
9517
9368
  throw new Error(
@@ -10087,7 +9938,7 @@ var init_api = __esm({
10087
9938
  * the envelope on first call. Optimistic-concurrency safe — a stale
10088
9939
  * `_v` (parallel writer on another device) throws `ConflictError`.
10089
9940
  *
10090
- * Patch semantics (#57):
9941
+ * Patch semantics:
10091
9942
  * - `undefined` (or omitted key) — skip; existing value preserved
10092
9943
  * - `null` — delete the field from the merged result
10093
9944
  * - any other value — overwrite (deep-merge for plain objects,
@@ -10143,7 +9994,7 @@ var init_api = __esm({
10143
9994
  this.fireChange(this.writerKeyringId, written);
10144
9995
  return written;
10145
9996
  }
10146
- // ─── Visibility (#122) ───────────────────────────────────────────────
9997
+ // ─── Visibility ──────────────────────────────────────────────────────
10147
9998
  /**
10148
9999
  * Read the current user's visibility flag from
10149
10000
  * `_meta/visibility/<keyringId>`. Returns `{ hidden: false }` when no
@@ -11204,7 +11055,7 @@ var init_registry2 = __esm({
11204
11055
  guardsFor(collection) {
11205
11056
  return this._byCollection.get(collection) ?? [];
11206
11057
  }
11207
- /** Per-collection guard counts, for introspection (#229). */
11058
+ /** Per-collection guard counts, for introspection. */
11208
11059
  summary() {
11209
11060
  return [...this._byCollection.entries()].map(([collection, guards]) => ({
11210
11061
  collection,
@@ -11662,23 +11513,23 @@ var init_vault = __esm({
11662
11513
  * `null` for vaults that never register any guard strategy. The
11663
11514
  * runtime class is dynamic-imported on demand so consumers that
11664
11515
  * never use guards don't pull `GuardRegistry`/`GuardExecutor` into
11665
- * their bundle (#130).
11516
+ * their bundle.
11666
11517
  */
11667
11518
  guardRegistry = null;
11668
11519
  /**
11669
11520
  * Per-vault derivation registry. Same lazy-load contract as
11670
11521
  * `guardRegistry` — `null` until `_initDerivations()` runs with at
11671
- * least one strategy handle. See #130 for the bundle motivation.
11522
+ * least one strategy handle.
11672
11523
  */
11673
11524
  derivationRegistry = null;
11674
11525
  /**
11675
- * Per-vault materialized-view registry (#143/#150). Same lazy-load
11526
+ * Per-vault materialized-view registry. Same lazy-load
11676
11527
  * contract as `derivationRegistry` — `null` until
11677
11528
  * `_initMaterializedViews()` runs with at least one MV handle.
11678
11529
  */
11679
11530
  materializedViewRegistry = null;
11680
11531
  /**
11681
- * Per-vault overlay registry (#154). Same lazy-load contract as
11532
+ * Per-vault overlay registry. Same lazy-load contract as
11682
11533
  * `materializedViewRegistry` — `null` until `_initOverlayedViews()`
11683
11534
  * runs with at least one handle.
11684
11535
  */
@@ -11699,7 +11550,7 @@ var init_vault = __esm({
11699
11550
  * target this vault session's keyringId. There is no method to write
11700
11551
  * another principal's envelope (own-only write rule, structural).
11701
11552
  * - Read-anyone: `get(keyringId)`, `list()` — read other principals'
11702
- * envelopes, subject to the `view-team-profiles` policy gate (#22).
11553
+ * envelopes, subject to the `view-team-profiles` policy gate.
11703
11554
  * - Reactive: `subscribe(id, cb)`, `live(id)` — fire on local writes.
11704
11555
  *
11705
11556
  * @see docs/superpowers/specs/2026-05-05-user-envelope-design.md
@@ -11719,12 +11570,12 @@ var init_vault = __esm({
11719
11570
  */
11720
11571
  reloadKeyring;
11721
11572
  collectionCache = /* @__PURE__ */ new Map();
11722
- /** #232 — vault-level schema cutover fence/controller. */
11573
+ /** Vault-level schema cutover fence/controller. */
11723
11574
  schemaFence;
11724
- /** #232 — per-client heartbeat/watcher; started lazily on cutover registration. */
11575
+ /** Per-client heartbeat/watcher; started lazily on cutover registration. */
11725
11576
  #fenceWatcher;
11726
11577
  #fenceCoordinationStarted = false;
11727
- /** #229 — per-collection registered schema-update strategy names. */
11578
+ /** Per-collection registered schema-update strategy names. */
11728
11579
  #schemaUpdateNames = /* @__PURE__ */ new Map();
11729
11580
  /**
11730
11581
  * per-collection `blobFields` retention/TTL config.
@@ -11798,8 +11649,7 @@ var init_vault = __esm({
11798
11649
  * Cache of closed/opened accounting periods.
11799
11650
  * Populated on first `closePeriod` / `openPeriod` / `listPeriods` /
11800
11651
  * per-collection write call. Kept in memory as an ordered list (by
11801
- * `closedAt`) so the `periodGuard` hook runs synchronously against
11802
- * each collection's put/delete path.
11652
+ * `closedAt`) so period checks run fast when the gate bus fires.
11803
11653
  *
11804
11654
  * Sentinel `null` means "not yet loaded" — the first consumer
11805
11655
  * triggers a one-time `loadPeriods()` pass. Every subsequent
@@ -11994,6 +11844,7 @@ var init_vault = __esm({
11994
11844
  emitter: this.emitter,
11995
11845
  writeQueue: this.noydb._writeQueueTracker,
11996
11846
  writeHooks: this.noydb._writeHooks,
11847
+ subsystemBus: this.noydb._subsystemBus,
11997
11848
  activeTxId: () => this.noydb._activeTxContextOrNull?.txId ?? null,
11998
11849
  schemaUpdateGate,
11999
11850
  schemaFence: this.schemaFence,
@@ -12016,18 +11867,12 @@ var init_vault = __esm({
12016
11867
  defaultLocale: this.locale,
12017
11868
  onRegisterConflictResolver: this.onRegisterConflictResolver,
12018
11869
  onAccess: (op, id) => this._logConsent(op, collectionName, id),
12019
- periodGuard: (existing, incoming) => this._assertTsWritable(existing, incoming),
12020
- // Guard / derivation sources are only wired when the
12021
- // corresponding registry has been initialised. Vaults without
12022
- // guards/derivations skip this entirely so `Collection.put`'s
12023
- // `if (this.guardSource)` / `if (this.derivationSource)`
12024
- // branches no-op without ever touching the subsystem code.
12025
- ...this.guardRegistry !== null ? {
12026
- guardSource: {
12027
- registry: () => this.guardRegistry,
12028
- readOnlyVault: () => this._ensureReadOnlyFacade()
12029
- }
12030
- } : {},
11870
+ // Derivation source is only wired when the corresponding registry
11871
+ // has been initialised. Guard source was removed in Track A slice 3b
11872
+ // guards now run via the gate bus in Noydb.#registerGuardGate.
11873
+ // Vaults without derivations skip this so `Collection.put`'s
11874
+ // `if (this.derivationSource)` branch no-ops without touching the
11875
+ // derivation subsystem code.
12031
11876
  ...this.derivationRegistry !== null ? {
12032
11877
  derivationSource: {
12033
11878
  registry: () => this.derivationRegistry,
@@ -12117,7 +11962,7 @@ var init_vault = __esm({
12117
11962
  await Promise.allSettled(pending);
12118
11963
  }
12119
11964
  /**
12120
- * Run a coordinated schema cutover (#232). Drains pending writes, waits
11965
+ * Run a coordinated schema cutover. Drains pending writes, waits
12121
11966
  * for the active client set to quiesce (the ack-barrier), applies every
12122
11967
  * pending collection transform in bulk, bumps the vault schema generation,
12123
11968
  * and clears the fence. Returns the count of collections migrated.
@@ -12135,9 +11980,9 @@ var init_vault = __esm({
12135
11980
  await coll._applyCutoverTransform(transform);
12136
11981
  }
12137
11982
  /**
12138
- * #228b — refresh a loaded collection's view of one document from a peer
11983
+ * Refresh a loaded collection's view of one document from a peer
12139
11984
  * tab's broadcast. No-op when the collection isn't loaded in this tab
12140
- * (it will read fresh on next open). Mirrors #runCutoverTransform's guard.
11985
+ * (it will read fresh on next open). Mirrors `#runCutoverTransform`'s guard.
12141
11986
  */
12142
11987
  async _applyRemoteWrite(collectionName, docId, action) {
12143
11988
  const coll = this.collectionCache.get(collectionName);
@@ -12145,9 +11990,9 @@ var init_vault = __esm({
12145
11990
  await coll._applyRemoteChange(docId, action);
12146
11991
  }
12147
11992
  /**
12148
- * #228c — for a detected conflict: capture this tab's clobbered record,
11993
+ * For a detected conflict: capture this tab's clobbered record,
12149
11994
  * read the common ancestor from history, converge the cache to the store's
12150
- * authoritative value (the (b) re-read), and return all three for the
11995
+ * authoritative value (the re-read), and return all three for the
12151
11996
  * WriteConflict payload. Returns null when the collection isn't loaded.
12152
11997
  */
12153
11998
  async _captureAndConverge(collectionName, docId, action, baseV) {
@@ -12164,15 +12009,15 @@ var init_vault = __esm({
12164
12009
  const remote = await coll.get(docId);
12165
12010
  return { local, remote, base };
12166
12011
  }
12167
- /** Recover a stuck cutover fence (#232) — reset to normal without bumping. */
12012
+ /** Recover a stuck cutover fence — reset to normal without bumping. */
12168
12013
  async abortSchemaCutover() {
12169
12014
  await this.schemaFence.abort();
12170
12015
  }
12171
- /** Current schema-cutover fence state for this vault (#232/#233). Thin live read. */
12016
+ /** Current schema-cutover fence state for this vault. Thin live read. */
12172
12017
  async schemaFenceState() {
12173
12018
  return loadFence(this.adapter, this.name);
12174
12019
  }
12175
- /** @internal Start the per-client heartbeat + fence watcher once a cutover is registered (#232). */
12020
+ /** @internal Start the per-client heartbeat + fence watcher once a cutover is registered. */
12176
12021
  _ensureFenceCoordination() {
12177
12022
  if (this.#fenceCoordinationStarted) return;
12178
12023
  this.#fenceCoordinationStarted = true;
@@ -12845,7 +12690,7 @@ var init_vault = __esm({
12845
12690
  * Dynamic-imports `GuardRegistry` + `ReadOnlyVaultFacade` and seeds
12846
12691
  * the registry with the supplied strategy handles. No-op when the
12847
12692
  * handles array is empty — keeps the guard subsystem out of the
12848
- * floor bundle for consumers that don't use guards (#130).
12693
+ * floor bundle for consumers that don't use guards.
12849
12694
  *
12850
12695
  * The read-only facade is eagerly instantiated here so the sync
12851
12696
  * accessor `_getReadOnlyFacade()` (called from the tx amendment
@@ -12863,10 +12708,9 @@ var init_vault = __esm({
12863
12708
  this.readOnlyFacade = new ReadOnlyVaultFacade2(this);
12864
12709
  }
12865
12710
  /**
12866
- * @internal — Collection.put calls into this. Returns `null` for
12867
- * vaults that never registered any guard strategy. Callers MUST
12868
- * gate on null (the existing `if (this.guardSource)` branches in
12869
- * `Collection` already do this transitively).
12711
+ * @internal — The gate handler in Noydb.#registerGuardGate calls into
12712
+ * this. Returns `null` for vaults that never registered any guard
12713
+ * strategy. Callers MUST gate on null.
12870
12714
  */
12871
12715
  _getGuardRegistry() {
12872
12716
  return this.guardRegistry;
@@ -12877,7 +12721,7 @@ var init_vault = __esm({
12877
12721
  * derivation strategies (async because `strategyHash` computation
12878
12722
  * goes through `crypto.subtle.digest`). No-op when the handles
12879
12723
  * array is empty — keeps the derivation subsystem out of the floor
12880
- * bundle for consumers that don't use derivations (#130). Throws
12724
+ * bundle for consumers that don't use derivations. Throws
12881
12725
  * `DerivationCycleError` if a cycle is detected after registration.
12882
12726
  */
12883
12727
  async _initDerivations(handles) {
@@ -12909,7 +12753,7 @@ var init_vault = __esm({
12909
12753
  * MV spec (which invokes its `query()` once for dependency
12910
12754
  * analysis), then runs the unified cycle detection across the MV +
12911
12755
  * derivation graphs. No-op when the handles array is empty — keeps
12912
- * the MV subsystem out of the floor bundle (mirrors v1 #130).
12756
+ * the MV subsystem out of the floor bundle (mirrors the derivation lazy-import pattern).
12913
12757
  * Throws `MaterializedViewCycleError` if a cycle is detected.
12914
12758
  */
12915
12759
  async _initMaterializedViews(handles) {
@@ -12966,13 +12810,13 @@ var init_vault = __esm({
12966
12810
  return this.overlayedViewRegistry;
12967
12811
  }
12968
12812
  /**
12969
- * Manual re-materialize for a single registered MV (#151). Useful
12813
+ * Manual re-materialize for a single registered MV. Useful
12970
12814
  * for `refresh: 'manual'` MVs (whose consumer drives refreshes
12971
12815
  * externally), for stale-bit recovery on vault re-open, and as the
12972
12816
  * explicit bulk-recompute escape hatch after a strategy change.
12973
12817
  *
12974
- * Returns `{ written, deleted, failed }`. `deleted` is always 0 in
12975
- * foundation + this sub-issue — tombstoning lands in #152.
12818
+ * Returns `{ written, deleted, failed }`. `deleted` is always 0
12819
+ * when tombstoning is not enabled.
12976
12820
  *
12977
12821
  * Throws if `name` is not a registered MV.
12978
12822
  */
@@ -13068,22 +12912,19 @@ var init_vault = __esm({
13068
12912
  /**
13069
12913
  * @internal — exposed for `runTransaction({ amendment: true })` so
13070
12914
  * the amendment invariant runner can pass the SAME read-only vault
13071
- * facade that the per-record `Collection.put` guard hook uses
13072
- * (`guardSource.readOnlyVault()` above). Eagerly instantiated by
13073
- * `_initGuards()` so this accessor stays synchronous; returns
13074
- * `null` for vaults that never registered any guard (amendments
13075
- * require at least one guard, so the caller should never see null).
12915
+ * facade that the gate handler in Noydb.#registerGuardGate uses.
12916
+ * Eagerly instantiated by `_initGuards()` so this accessor stays
12917
+ * synchronous; returns `null` for vaults that never registered any
12918
+ * guard (amendments require at least one guard, so the caller should
12919
+ * never see null).
13076
12920
  */
13077
12921
  _getReadOnlyFacade() {
13078
12922
  return this.readOnlyFacade;
13079
12923
  }
13080
12924
  /**
13081
- * Internal lazy-allocator for the read-only facade. Used by the
13082
- * per-collection `guardSource.readOnlyVault` callback when guards
13083
- * ARE configured but `_initGuards()` raced with the first guard
13084
- * invocation (theoretically impossible — `Noydb.openVault` awaits
13085
- * `_initGuards` before returning — but we keep the defensive lazy
13086
- * path so the closure's contract stays "always returns a facade").
12925
+ * Internal lazy-allocator for the read-only facade. Used as a
12926
+ * defensive fallback; in practice `_initGuards()` eagerly
12927
+ * instantiates this, so the lazy path is a no-op.
13087
12928
  */
13088
12929
  _ensureReadOnlyFacade() {
13089
12930
  if (this.readOnlyFacade !== null) return this.readOnlyFacade;
@@ -13538,7 +13379,7 @@ var init_vault = __esm({
13538
13379
  const all = await this._loadPeriodsCache();
13539
13380
  return all.find((p) => p.name === name) ?? null;
13540
13381
  }
13541
- /** @internal — periodGuard callback installed on every Collection. */
13382
+ /** @internal — called by the gate bus before put/delete. */
13542
13383
  async _assertTsWritable(existing, incoming) {
13543
13384
  if (existing === null && incoming === null) return;
13544
13385
  if (this.periodCache === null) {
@@ -13626,7 +13467,7 @@ var init_vault = __esm({
13626
13467
  return dumpVaultSchema(this, opts);
13627
13468
  }
13628
13469
  /**
13629
- * Lightweight read of the vault's registered schema (#229): collections
13470
+ * Lightweight read of the vault's registered schema: collections
13630
13471
  * (+ doc counts), guards, materialized views, schema-update strategies,
13631
13472
  * and the unlocked user's grants. Cheap — one `adapter.list` per
13632
13473
  * collection, no decryption. For a full snapshot + stats use dumpSchema().
@@ -14433,6 +14274,116 @@ var init_write_hooks = __esm({
14433
14274
  }
14434
14275
  });
14435
14276
 
14277
+ // src/subsystem-bus.ts
14278
+ var SubsystemBus;
14279
+ var init_subsystem_bus = __esm({
14280
+ "src/subsystem-bus.ts"() {
14281
+ "use strict";
14282
+ SubsystemBus = class {
14283
+ #handlers = /* @__PURE__ */ new Map();
14284
+ #gateHandlers = /* @__PURE__ */ new Map();
14285
+ #depth = 0;
14286
+ /** Register a handler for an observe point. Returns an unsubscribe fn. */
14287
+ register(point, handler) {
14288
+ let arr = this.#handlers.get(point);
14289
+ if (!arr) {
14290
+ arr = [];
14291
+ this.#handlers.set(point, arr);
14292
+ }
14293
+ arr.push(handler);
14294
+ return () => {
14295
+ const a = this.#handlers.get(point);
14296
+ if (!a) return;
14297
+ const i = a.indexOf(handler);
14298
+ if (i >= 0) a.splice(i, 1);
14299
+ };
14300
+ }
14301
+ /** Cheap gate for the write path — true when any handler is registered for the point. */
14302
+ hasHandlers(point) {
14303
+ const a = this.#handlers.get(point);
14304
+ return a !== void 0 && a.length > 0;
14305
+ }
14306
+ /**
14307
+ * True while one or more dispatches are in flight. Backed by a depth counter
14308
+ * so that two concurrent async dispatches (`Promise.all([put('a'), put('b')])`
14309
+ * each captured `busAfterPut=true` at their respective put() tops while depth
14310
+ * was 0) both proceed independently — the counter stays > 0 until BOTH finish,
14311
+ * so any nested write attempted by a handler still sees `dispatching === true`
14312
+ * and is suppressed by the write-path gate in `collection.ts`
14313
+ * (`busAfterPut = hasHandlers('afterPut') && !dispatching`). Re-entrancy
14314
+ * suppression lives exclusively on that write-path gate; concurrent independent
14315
+ * dispatches must not drop each other's events.
14316
+ */
14317
+ get dispatching() {
14318
+ return this.#depth > 0;
14319
+ }
14320
+ /**
14321
+ * Dispatch in registration order, awaited. Per-handler errors are warned, not
14322
+ * thrown — an observe handler must never abort a completed write. A
14323
+ * re-entrancy guard suppresses nested firing so a handler that itself writes
14324
+ * cannot loop (same rationale as WriteHookRegistry.#suppressed).
14325
+ */
14326
+ async dispatch(point, event) {
14327
+ const a = this.#handlers.get(point);
14328
+ if (!a || a.length === 0) return;
14329
+ this.#depth++;
14330
+ try {
14331
+ for (const h of a.slice()) {
14332
+ try {
14333
+ await h(event);
14334
+ } catch (err) {
14335
+ console.warn(
14336
+ `[noy-db] subsystem observe handler failed at ${point}: ` + (err instanceof Error ? err.message : String(err))
14337
+ );
14338
+ }
14339
+ }
14340
+ } finally {
14341
+ this.#depth--;
14342
+ }
14343
+ }
14344
+ /** Register a write-gating handler. A throw from the handler ABORTS the write. Returns an unsubscribe fn. */
14345
+ registerGate(point, handler) {
14346
+ let arr = this.#gateHandlers.get(point);
14347
+ if (!arr) {
14348
+ arr = [];
14349
+ this.#gateHandlers.set(point, arr);
14350
+ }
14351
+ arr.push(handler);
14352
+ return () => {
14353
+ const a = this.#gateHandlers.get(point);
14354
+ if (!a) return;
14355
+ const i = a.indexOf(handler);
14356
+ if (i >= 0) a.splice(i, 1);
14357
+ };
14358
+ }
14359
+ /** Cheap gate for the write path — true when any gate handler is registered for the point. */
14360
+ hasGateHandlers(point) {
14361
+ const a = this.#gateHandlers.get(point);
14362
+ return a !== void 0 && a.length > 0;
14363
+ }
14364
+ /**
14365
+ * Run gate handlers in registration order, awaited. Unlike `dispatch`
14366
+ * (observe), a handler throw is NOT swallowed — it PROPAGATES, aborting the
14367
+ * write before it reaches the store. The first throw stops the remaining
14368
+ * handlers (fail-fast). This is the seam guards/periods migrate onto.
14369
+ *
14370
+ * Note: gate handlers are validators that read, not write. A gate handler
14371
+ * that writes back into the same collection would re-enter the write path
14372
+ * and re-dispatch this point; loop-suppression for that case is deferred to
14373
+ * the migration slice (contract: gate handlers must not perform writes that
14374
+ * re-trigger their own point).
14375
+ */
14376
+ async dispatchGate(point, event) {
14377
+ const a = this.#gateHandlers.get(point);
14378
+ if (!a || a.length === 0) return;
14379
+ for (const h of a.slice()) {
14380
+ await h(event);
14381
+ }
14382
+ }
14383
+ };
14384
+ }
14385
+ });
14386
+
14436
14387
  // src/tab-coordination.ts
14437
14388
  function isPresenceMsg(x) {
14438
14389
  if (x === null || typeof x !== "object") return false;
@@ -14935,9 +14886,9 @@ var init_presets = __esm({
14935
14886
  minTier: 1,
14936
14887
  enabled: true
14937
14888
  },
14938
- // rotate-recovery (#121): deliberate paper-sheet regeneration
14939
- // when the user remembers their passphrase. PERSONAL matches the
14940
- // pre-#121 low-level flow's bar — knowing the passphrase is enough.
14889
+ // rotate-recovery: deliberate paper-sheet regeneration
14890
+ // when the user remembers their passphrase. PERSONAL allows tier-1 —
14891
+ // knowing the passphrase is enough.
14941
14892
  "rotate-recovery": { minTier: 1 },
14942
14893
  "enroll-authenticator": { minTier: 1 },
14943
14894
  "remove-authenticator": { minTier: 1 },
@@ -14971,7 +14922,7 @@ var init_presets = __esm({
14971
14922
  minTier: 1,
14972
14923
  enabled: false
14973
14924
  },
14974
- // ─── User envelope gates (#22) ────────────────────────────────────
14925
+ // ─── User envelope gates ──────────────────────────────────────────
14975
14926
  // edit-own-profile: tier 3 floor — any active session can edit their
14976
14927
  // own profile/preferences. Tightening to require a TOTP for
14977
14928
  // profile changes is a one-line override.
@@ -14998,7 +14949,7 @@ var init_presets = __esm({
14998
14949
  minTier: 1,
14999
14950
  enabled: true
15000
14951
  },
15001
- // rotate-recovery (#121): STRICT requires an off-device factor —
14952
+ // rotate-recovery: STRICT requires an off-device factor —
15002
14953
  // rotating recovery is an off-site-trust event; a stolen unlocked
15003
14954
  // laptop must not be able to silently mint a new sheet for the
15004
14955
  // attacker. Matches the `peer-recover-user` STRICT default.
@@ -15071,7 +15022,7 @@ var init_presets = __esm({
15071
15022
  minTier: 1,
15072
15023
  enabled: false
15073
15024
  },
15074
- // ─── User envelope gates (#22) ────────────────────────────────────
15025
+ // ─── User envelope gates ──────────────────────────────────────────
15075
15026
  // STRICT: profile edits require a TOTP/email-OTP factor (typical
15076
15027
  // shared-workstation hardening — your name/avatar shouldn't change
15077
15028
  // without a fresh second-factor proof).
@@ -15154,6 +15105,81 @@ var init_policy2 = __esm({
15154
15105
  }
15155
15106
  });
15156
15107
 
15108
+ // src/guards/executor.ts
15109
+ var executor_exports3 = {};
15110
+ __export(executor_exports3, {
15111
+ GuardExecutor: () => GuardExecutor
15112
+ });
15113
+ function deepEqual(a, b) {
15114
+ if (a === b) return true;
15115
+ if (a === null || b === null) return a === b;
15116
+ if (typeof a !== typeof b) return false;
15117
+ if (typeof a !== "object") return a === b;
15118
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
15119
+ if (Array.isArray(a)) {
15120
+ const aa = a;
15121
+ const bb = b;
15122
+ if (aa.length !== bb.length) return false;
15123
+ for (let i = 0; i < aa.length; i++) if (!deepEqual(aa[i], bb[i])) return false;
15124
+ return true;
15125
+ }
15126
+ const ao = a;
15127
+ const bo = b;
15128
+ const ak = Object.keys(ao);
15129
+ const bk = Object.keys(bo);
15130
+ if (ak.length !== bk.length) return false;
15131
+ for (const k of ak) {
15132
+ if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
15133
+ if (!deepEqual(ao[k], bo[k])) return false;
15134
+ }
15135
+ return true;
15136
+ }
15137
+ var GuardExecutor;
15138
+ var init_executor3 = __esm({
15139
+ "src/guards/executor.ts"() {
15140
+ "use strict";
15141
+ init_errors();
15142
+ GuardExecutor = {
15143
+ /**
15144
+ * Compare existing vs incoming for each `frozenFields.fields` entry
15145
+ * when `frozenFields.when(existing)` is true. Throws
15146
+ * `FieldFrozenError` listing every changed frozen field.
15147
+ */
15148
+ async checkFrozenFields(guard, id, existing, incoming) {
15149
+ const ff = guard.frozenFields;
15150
+ if (!ff) return;
15151
+ if (existing === null) return;
15152
+ if (!ff.when(existing)) return;
15153
+ const changed = [];
15154
+ for (const f of ff.fields) {
15155
+ if (existing[f] !== incoming[f]) {
15156
+ if (!deepEqual(existing[f], incoming[f])) changed.push(String(f));
15157
+ }
15158
+ }
15159
+ if (changed.length > 0) {
15160
+ throw new FieldFrozenError(guard.collection, id, changed);
15161
+ }
15162
+ },
15163
+ /**
15164
+ * Run a single guard's invariant over its slice of the change-set.
15165
+ * Any throw is converted to `InvariantError` unless it already is one.
15166
+ */
15167
+ async runInvariant(guard, changes, ctx) {
15168
+ const amendment = guard.amendment;
15169
+ if (!amendment) return;
15170
+ try {
15171
+ await amendment.invariant(changes, ctx);
15172
+ } catch (err) {
15173
+ if (err instanceof InvariantError) throw err;
15174
+ throw new InvariantError(
15175
+ err instanceof Error ? err.message : `invariant violated: ${String(err)}`
15176
+ );
15177
+ }
15178
+ }
15179
+ };
15180
+ }
15181
+ });
15182
+
15157
15183
  // src/noydb.ts
15158
15184
  var noydb_exports = {};
15159
15185
  __export(noydb_exports, {
@@ -15226,6 +15252,7 @@ var init_noydb = __esm({
15226
15252
  init_events();
15227
15253
  init_write_queue();
15228
15254
  init_write_hooks();
15255
+ init_subsystem_bus();
15229
15256
  init_tab_coordination();
15230
15257
  init_tab_write_relay();
15231
15258
  init_keyring();
@@ -15249,13 +15276,14 @@ var init_noydb = __esm({
15249
15276
  emitter = new NoydbEventEmitter();
15250
15277
  writeQueueTracker = new WriteQueueTracker();
15251
15278
  writeHooks = new WriteHookRegistry();
15279
+ subsystemBus = new SubsystemBus();
15252
15280
  clientId = generateULID();
15253
15281
  vaultCache = /* @__PURE__ */ new Map();
15254
15282
  keyringCache = /* @__PURE__ */ new Map();
15255
15283
  syncEngines = /* @__PURE__ */ new Map();
15256
15284
  /**
15257
15285
  * Per-vault active session tier — defaults to `1` after a passphrase
15258
- * unlock; tier-2 / tier-3 unlocks (issue #11) downgrade it. Used by
15286
+ * unlock; tier-2 / tier-3 unlocks downgrade it. Used by
15259
15287
  * {@link checkGate} to evaluate `gate.minTier`.
15260
15288
  */
15261
15289
  activeTier = /* @__PURE__ */ new Map();
@@ -15265,14 +15293,14 @@ var init_noydb = __esm({
15265
15293
  */
15266
15294
  policyCache = /* @__PURE__ */ new Map();
15267
15295
  /**
15268
- * One-shot bypass for the managed-mode strong-recovery check (#195).
15296
+ * One-shot bypass for the managed-mode strong-recovery check.
15269
15297
  * Set true by {@link openVaultAndEnrollRecovery} for the duration of
15270
15298
  * the bootstrap window so the keyring can be created before the
15271
15299
  * strong recovery is enrolled. Always cleared (try/finally).
15272
15300
  * @internal
15273
15301
  */
15274
15302
  _skipNextManagedRecoveryCheck = false;
15275
- /** Per-vault tier-3 (PIN / quick-resume) state — issue #11. */
15303
+ /** Per-vault tier-3 (PIN / quick-resume) state. */
15276
15304
  quickUnlock = new QuickUnlockStore();
15277
15305
  /**
15278
15306
  * Resolved public-envelope schema. Lazily computed once from
@@ -15282,9 +15310,9 @@ var init_noydb = __esm({
15282
15310
  publicEnvelopeSchema;
15283
15311
  closed = false;
15284
15312
  sessionTimer = null;
15285
- /** Same-device multi-tab coordinator (#228); created on `enableTabCoordination()`. */
15313
+ /** Same-device multi-tab coordinator; created on `enableTabCoordination()`. */
15286
15314
  tabCoordinator;
15287
- /** Cross-tab write relay (#228b); created on `enableTabCoordination()`. */
15315
+ /** Cross-tab write relay; created on `enableTabCoordination()`. */
15288
15316
  writeRelay;
15289
15317
  /** Per-vault policy enforcers. */
15290
15318
  policyEnforcers = /* @__PURE__ */ new Map();
@@ -15297,8 +15325,8 @@ var init_noydb = __esm({
15297
15325
  * the same function's `finally` block. Side-effect writes triggered
15298
15326
  * during a staged op's `Collection.put` (today: eager derivation
15299
15327
  * outputs) register their pre-write envelope on `_executed` here so
15300
- * a mid-batch failure rolls them back alongside the main staged ops
15301
- * (#133). `null` outside of Phase 2.
15328
+ * a mid-batch failure rolls them back alongside the main staged ops.
15329
+ * `null` outside of Phase 2.
15302
15330
  * @internal
15303
15331
  */
15304
15332
  _activeTxContext = null;
@@ -15319,8 +15347,95 @@ var init_noydb = __esm({
15319
15347
  if (options.sessionPolicy) {
15320
15348
  this.sessionStrategy.validateSessionPolicy(options.sessionPolicy);
15321
15349
  }
15350
+ this.#registerGuardGate();
15351
+ this.#registerPeriodGate();
15322
15352
  this.resetSessionTimer();
15323
15353
  }
15354
+ // Track A — guards migration. Registers record-lock / field-freeze / onDelete
15355
+ // / amendment-collect as gate-bus handlers (only when guards are opted in, so
15356
+ // the write path is zero-cost otherwise). Resolves the live vault's
15357
+ // GuardRegistry per dispatch. Registered BEFORE the period gate so guard
15358
+ // checks run first. The amendment branch is a side-effect (collectChange),
15359
+ // NOT a throw — and runs even for internal deletes (an amendment invariant
15360
+ // must see system housekeeping tombstones); onDelete/checks run only for
15361
+ // user (non-internal) operations.
15362
+ #registerGuardGate() {
15363
+ if (this.options.guardStrategies === void 0) return;
15364
+ this.subsystemBus.registerGate("beforePut", async (e) => {
15365
+ const v = this.vaultCache.get(e.vault);
15366
+ if (!v) return;
15367
+ const registry = v._getGuardRegistry();
15368
+ if (!registry) return;
15369
+ const guards = registry.guardsFor(e.collection);
15370
+ if (guards.length === 0) return;
15371
+ const existing = e.existing ?? null;
15372
+ const incoming = e.incoming;
15373
+ if (registry.isAmendmentActive()) {
15374
+ registry.collectChange(e.collection, e.docId, existing, incoming, e.existingVersion, e.existingVersion + 1);
15375
+ return;
15376
+ }
15377
+ const facade = v._getReadOnlyFacade();
15378
+ if (!facade) return;
15379
+ const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
15380
+ await registry.runChecks(e.collection, incoming, ctx);
15381
+ const { GuardExecutor: GuardExecutor2 } = await Promise.resolve().then(() => (init_executor3(), executor_exports3));
15382
+ for (const g of guards) {
15383
+ await GuardExecutor2.checkFrozenFields(g, e.docId, existing, incoming);
15384
+ }
15385
+ });
15386
+ this.subsystemBus.registerGate("beforeDelete", async (e) => {
15387
+ const v = this.vaultCache.get(e.vault);
15388
+ if (!v) return;
15389
+ const registry = v._getGuardRegistry();
15390
+ if (!registry) return;
15391
+ const guards = registry.guardsFor(e.collection);
15392
+ if (guards.length === 0) return;
15393
+ const existing = e.existing ?? null;
15394
+ if (registry.isAmendmentActive()) {
15395
+ registry.collectChange(e.collection, e.docId, existing, null, e.existingVersion, e.existingVersion);
15396
+ return;
15397
+ }
15398
+ if (e.internal) return;
15399
+ const facade = v._getReadOnlyFacade();
15400
+ if (!facade) return;
15401
+ const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
15402
+ await registry.runOnDelete(e.collection, existing ?? {}, ctx);
15403
+ });
15404
+ }
15405
+ /**
15406
+ * Register closed-period write guards on the subsystem bus when a
15407
+ * periodsStrategy is configured. Handlers resolve the live Vault from
15408
+ * vaultCache so they always use the up-to-date period cache.
15409
+ */
15410
+ // Track A — periods migration. Registers the closed-period write guard as a
15411
+ // gate-bus handler (only when periods is opted in, so the write path is
15412
+ // zero-cost otherwise). Each handler resolves the LIVE vault from the cache
15413
+ // per dispatch and delegates to its `_assertTsWritable`, which owns all
15414
+ // period logic. Resolving the live vault makes eviction/re-creation
15415
+ // transparent. Semantics note: if a write reaches the gate through a retained
15416
+ // collection handle whose vault has been evicted from `vaultCache` (e.g. a
15417
+ // post-revocation write on a stale handle), the period check is skipped — the
15418
+ // guard binds to the live vault, not a captured instance. Periods is a
15419
+ // write-integrity guard, not a security boundary, and a re-open reloads the
15420
+ // period cache; the trade-off is intentional.
15421
+ #registerPeriodGate() {
15422
+ if (this.options.periodsStrategy === void 0) return;
15423
+ this.subsystemBus.registerGate("beforePut", async (e) => {
15424
+ const v = this.vaultCache.get(e.vault);
15425
+ if (!v) return;
15426
+ const existing = e.op === "create" ? null : { ts: e.existingTs ?? null, record: e.existing ?? null };
15427
+ await v._assertTsWritable(existing, e.incoming);
15428
+ });
15429
+ this.subsystemBus.registerGate("beforeDelete", async (e) => {
15430
+ if (e.internal) return;
15431
+ const v = this.vaultCache.get(e.vault);
15432
+ if (!v) return;
15433
+ await v._assertTsWritable(
15434
+ { ts: e.existingTs ?? null, record: e.existing ?? null },
15435
+ null
15436
+ );
15437
+ });
15438
+ }
15324
15439
  resetSessionTimer() {
15325
15440
  if (this.sessionTimer) clearTimeout(this.sessionTimer);
15326
15441
  const idleMs = this.options.sessionPolicy?.idleTimeoutMs ?? this.options.sessionTimeout;
@@ -15610,8 +15725,6 @@ var init_noydb = __esm({
15610
15725
  * @throws `NoAccessError` when no keyring exists for the target.
15611
15726
  * @throws `PermissionDeniedError` when the role hierarchy rejects.
15612
15727
  * @throws `ValidationError` when no field is provided.
15613
- *
15614
- * @see #54
15615
15728
  */
15616
15729
  async updateUser(vault, options, factors) {
15617
15730
  await this.checkGate(vault, "update-user", factors);
@@ -15947,7 +16060,7 @@ var init_noydb = __esm({
15947
16060
  * Phase 2. `Collection.dispatchDerivations` consults this so a
15948
16061
  * recursive derived-output write inside `Collection.put` can register
15949
16062
  * its envelope onto `ctx._executed` and roll back with the main
15950
- * staged ops on mid-batch failure (#133).
16063
+ * staged ops on mid-batch failure.
15951
16064
  *
15952
16065
  * @internal
15953
16066
  */
@@ -15976,7 +16089,7 @@ var init_noydb = __esm({
15976
16089
  * `Collection.putManyAtomic` (via `derivationSource.createTxContext`)
15977
16090
  * to publish an active context for the duration of its bulk-atomic
15978
16091
  * Phase 2 loop, so recursive derivation-output writes register on
15979
- * `ctx._executed` and roll back together with the source ops (#133).
16092
+ * `ctx._executed` and roll back together with the source ops.
15980
16093
  *
15981
16094
  * @internal
15982
16095
  */
@@ -16047,26 +16160,26 @@ var init_noydb = __esm({
16047
16160
  return this.writeQueueTracker;
16048
16161
  }
16049
16162
  /**
16050
- * Register a hook that runs before each write (#230). Awaited; a throw
16163
+ * Register a hook that runs before each write. Awaited; a throw
16051
16164
  * aborts the write. Returns an unsubscribe function.
16052
16165
  */
16053
16166
  onBeforeWrite(handler) {
16054
16167
  return this.writeHooks.onBeforeWrite(handler);
16055
16168
  }
16056
16169
  /**
16057
- * Register a hook that runs after each committed write (#230). Awaited;
16170
+ * Register a hook that runs after each committed write. Awaited;
16058
16171
  * a handler error is warned, never rolled back. Returns an unsubscribe fn.
16059
16172
  */
16060
16173
  onAfterWrite(handler) {
16061
16174
  return this.writeHooks.onAfterWrite(handler);
16062
16175
  }
16063
- /** Subscribe to cross-tab write conflicts (#228c). Returns an unsubscribe. */
16176
+ /** Subscribe to cross-tab write conflicts. Returns an unsubscribe. */
16064
16177
  onWriteConflict(fn) {
16065
16178
  this.on("write:conflict", fn);
16066
16179
  return () => this.off("write:conflict", fn);
16067
16180
  }
16068
16181
  /**
16069
- * Enable same-device multi-tab coordination (#228): primary/secondary
16182
+ * Enable same-device multi-tab coordination: primary/secondary
16070
16183
  * election + presence. Browser-only — a graceful no-op (role 'unknown')
16071
16184
  * when Web Locks / BroadcastChannel are unavailable and nothing is
16072
16185
  * injected. Idempotent; returns a disposer.
@@ -16149,7 +16262,11 @@ var init_noydb = __esm({
16149
16262
  get _writeHooks() {
16150
16263
  return this.writeHooks;
16151
16264
  }
16152
- /** @internal Stable per-instance id for schema-cutover coordination (#232). */
16265
+ /** @internal The observe bus, threaded into every Collection. */
16266
+ get _subsystemBus() {
16267
+ return this.subsystemBus;
16268
+ }
16269
+ /** @internal Stable per-instance id for schema-cutover coordination. */
16153
16270
  get _clientId() {
16154
16271
  return this.clientId;
16155
16272
  }
@@ -16169,10 +16286,6 @@ var init_noydb = __esm({
16169
16286
  * survives lock; nothing about it changes when DEKs are scrubbed).
16170
16287
  *
16171
16288
  * No-op when `vault` is not currently in cache (idempotent).
16172
- *
16173
- * Unblocks vLannaAi/niwat#33.
16174
- *
16175
- * @see #17
16176
16289
  */
16177
16290
  lockVault(vault) {
16178
16291
  this.syncEngines.get(vault)?.stopAutoSync();
@@ -16287,7 +16400,7 @@ var init_noydb = __esm({
16287
16400
  return merged;
16288
16401
  }
16289
16402
  /**
16290
- * Read the current vault-level user-directory toggle (#122). Returns
16403
+ * Read the current vault-level user-directory toggle. Returns
16291
16404
  * the default-on shape (`{ enabled: true }`) when no `_meta/directory`
16292
16405
  * document has been persisted yet.
16293
16406
  *
@@ -16299,7 +16412,7 @@ var init_noydb = __esm({
16299
16412
  return persisted?.enabled ?? true;
16300
16413
  }
16301
16414
  /**
16302
- * Toggle the vault's user-directory listing on or off (#122).
16415
+ * Toggle the vault's user-directory listing on or off.
16303
16416
  * Owner-only. When disabled, `listUsersWithEnvelopes()` throws
16304
16417
  * {@link import('./errors.js').DirectoryDisabledError} for callers
16305
16418
  * whose role is neither `owner` nor `admin`.
@@ -16359,7 +16472,7 @@ var init_noydb = __esm({
16359
16472
  *
16360
16473
  * Two enforcement modes:
16361
16474
  *
16362
- * 1. **Managed-mode mandatory strong-recovery (#195).** When
16475
+ * 1. **Managed-mode mandatory strong-recovery.** When
16363
16476
  * `passphraseMode === 'managed'`, the vault MUST have at least
16364
16477
  * one **strong** recovery profile (Shamir today). Paper alone is
16365
16478
  * rejected because under managed mode the user has no memorized
@@ -16393,14 +16506,14 @@ var init_noydb = __esm({
16393
16506
  throw new RecoveryNotEnrolledError();
16394
16507
  }
16395
16508
  /**
16396
- * Internal accessor used by tier-2/tier-3 unlock paths (issue #11)
16509
+ * Internal accessor used by tier-2/tier-3 unlock paths
16397
16510
  * to mark the active session tier.
16398
16511
  * @internal
16399
16512
  */
16400
16513
  _setActiveTier(vault, tier) {
16401
16514
  this.activeTier.set(vault, tier);
16402
16515
  }
16403
- // ─── Tier-2 enroll / remove (issue #11) ────────────────────────
16516
+ // ─── Tier-2 enroll / remove ─────────────────────────────────────
16404
16517
  /**
16405
16518
  * Add a tier-2 authenticator slot to the calling user's keyring.
16406
16519
  * Each slot independently wraps the SAME KEK under a method-specific
@@ -16430,7 +16543,7 @@ var init_noydb = __esm({
16430
16543
  const next = await removeAuthenticator(this.options.store, vault, keyring, slotId);
16431
16544
  this.keyringCache.set(vault, next);
16432
16545
  }
16433
- /** Read the slot list for a vault. Internal — `describeAuthConfig` (#13) consumes this. */
16546
+ /** Read the slot list for a vault. Internal — `describeAuthConfig` consumes this. */
16434
16547
  async listAuthenticators(vault) {
16435
16548
  const keyring = await this.getKeyringInternal(vault);
16436
16549
  return keyring.authenticators;
@@ -16442,7 +16555,7 @@ var init_noydb = __esm({
16442
16555
  * are immutable through this method. Anti-slot-swap is structural,
16443
16556
  * not gate-driven.
16444
16557
  *
16445
- * `meta` patch semantics (#57-aligned):
16558
+ * `meta` patch semantics (top-level merge):
16446
16559
  * - Top-level merge — absent keys preserved
16447
16560
  * - `null` value — delete that meta key
16448
16561
  * - Other values — replace verbatim
@@ -16460,8 +16573,6 @@ var init_noydb = __esm({
16460
16573
  *
16461
16574
  * @throws `NoAccessError` when no slot with the given id exists.
16462
16575
  * @throws `ValidationError` when no patch field is provided.
16463
- *
16464
- * @see #55
16465
16576
  */
16466
16577
  async updateAuthenticator(vault, slotId, options, factors) {
16467
16578
  await this.checkGate(vault, "update-authenticator", factors);
@@ -16470,7 +16581,7 @@ var init_noydb = __esm({
16470
16581
  this.keyringCache.set(vault, next);
16471
16582
  }
16472
16583
  /**
16473
- * Native WebAuthn enrollment using the **real** internal keyring (#16).
16584
+ * Native WebAuthn enrollment using the **real** internal keyring.
16474
16585
  *
16475
16586
  * Why this exists: when a consumer is using `createNoydb({ secret })`,
16476
16587
  * they cannot reach the live `UnlockedKeyring` to feed it to
@@ -16513,8 +16624,6 @@ var init_noydb = __esm({
16513
16624
  * a server-side allowlist).
16514
16625
  *
16515
16626
  * Gated by `enroll-authenticator` like `enrollAuthenticator()` itself.
16516
- *
16517
- * @see #16
16518
16627
  */
16519
16628
  async enrollWebAuthn(vault, ceremony, factors) {
16520
16629
  await this.checkGate(vault, "enroll-authenticator", factors);
@@ -16541,8 +16650,6 @@ var init_noydb = __esm({
16541
16650
  * deciding when a new device prompt should appear. Identity is
16542
16651
  * `id` + `enrolled_at`; the `meta.credentialId` (base64) is used by
16543
16652
  * `allowCredentials` at unlock time.
16544
- *
16545
- * @see #16
16546
16653
  */
16547
16654
  async listWebAuthnSlots(vault) {
16548
16655
  const keyring = await this.getKeyringInternal(vault);
@@ -16624,7 +16731,7 @@ var init_noydb = __esm({
16624
16731
  async getPublicEnvelope(vault, opts = {}) {
16625
16732
  return readPublicEnvelope(this.options.store, vault, opts);
16626
16733
  }
16627
- // ─── Auth introspection (issue #13) ────────────────────────────
16734
+ // ─── Auth introspection ─────────────────────────────────────────
16628
16735
  /** English summary of the configured auth model. */
16629
16736
  async describeAuthConfig(vault) {
16630
16737
  return describeAuthConfig(this.options.store, vault);
@@ -16647,7 +16754,7 @@ var init_noydb = __esm({
16647
16754
  await this.checkGate(vault, "view-user-auth", factors);
16648
16755
  return describeAllUsersAuth(this.options.store, vault);
16649
16756
  }
16650
- // ─── Tier-1 change flows (issue #10) ───────────────────────────
16757
+ // ─── Tier-1 change flows ────────────────────────────────────────
16651
16758
  /**
16652
16759
  * Rotate the user's passphrase (user remembers old). Validates the
16653
16760
  * new phrase against the configured `passphrase` policy, runs the
@@ -16655,8 +16762,7 @@ var init_noydb = __esm({
16655
16762
  *
16656
16763
  * Tier-2 authenticator slots are dropped — each slot wraps the old
16657
16764
  * KEK and would need its derivation key to be re-presented. Re-enrol
16658
- * via `db.enrollAuthenticator` after rotation. Tracked as a
16659
- * v0.1.0-pre.5 limitation.
16765
+ * via `db.enrollAuthenticator` after rotation.
16660
16766
  *
16661
16767
  * @throws `WeakPassphraseError` on a weak new phrase.
16662
16768
  * @throws `PolicyDeniedError` when the gate denies (missing factor, …).
@@ -16678,8 +16784,8 @@ var init_noydb = __esm({
16678
16784
  }
16679
16785
  /**
16680
16786
  * Reset the passphrase using a recovery proof (user forgot the old).
16681
- * v0.1.0-pre.5 supports the `'paper'` profile end-to-end; the
16682
- * other three profiles throw {@link RecoveryProfileNotImplementedError}.
16787
+ * Currently supports the `'paper'` profile end-to-end; the
16788
+ * other profiles throw {@link RecoveryProfileNotImplementedError}.
16683
16789
  *
16684
16790
  * Burns the used recovery entry on success.
16685
16791
  */
@@ -16708,7 +16814,7 @@ var init_noydb = __esm({
16708
16814
  return { newCodes: codes };
16709
16815
  }
16710
16816
  /**
16711
- * Deliberate paper-recovery-code regeneration (#121). User knows their
16817
+ * Deliberate paper-recovery-code regeneration. User knows their
16712
16818
  * passphrase but wants a fresh sheet — they lost the printout or
16713
16819
  * suspect compromise of the off-site copy.
16714
16820
  *
@@ -16718,7 +16824,7 @@ var init_noydb = __esm({
16718
16824
  *
16719
16825
  * Gated by the `rotate-recovery` policy gate:
16720
16826
  * - PERSONAL_POLICY: `{ minTier: 1 }` — knowing the passphrase
16721
- * suffices, matching the pre-#121 low-level flow's bar.
16827
+ * suffices, matching the lower-level flow's bar.
16722
16828
  * - STRICT_POLICY: `{ minTier: 1, factors: [{ anyOf: ['totp',
16723
16829
  * 'email-otp', 'webauthn-roaming'] }] }` — rotation is an
16724
16830
  * off-site-trust event; require an off-device factor so a
@@ -16823,7 +16929,7 @@ var init_noydb = __esm({
16823
16929
  return { newShares: shareStrings, entryId: targetEntryId };
16824
16930
  }
16825
16931
  /**
16826
- * **Atomic create-and-enroll for managed-mode vaults (#195).**
16932
+ * **Atomic create-and-enroll for managed-mode vaults.**
16827
16933
  *
16828
16934
  * Bootstraps a managed-mode vault and enrolls strong recovery in
16829
16935
  * a single ceremony. Under `passphraseMode: 'managed'`, every
@@ -16894,7 +17000,7 @@ var init_noydb = __esm({
16894
17000
  return { vault: vaultHandle, recoveryEnrollments };
16895
17001
  }
16896
17002
  /**
16897
- * **Recovery flow under managed-passphrase mode (#195).**
17003
+ * **Recovery flow under managed-passphrase mode.**
16898
17004
  *
16899
17005
  * Replaces the sealed passphrase of a managed-mode vault with a
16900
17006
  * fresh 256-bit random, sealed under the configured
@@ -16911,7 +17017,7 @@ var init_noydb = __esm({
16911
17017
  * 5. Drop the keyring cache so the next operation re-derives.
16912
17018
  *
16913
17019
  * The vault's strong-recovery enrollment is preserved across
16914
- * recovery (Shamir entries are not burned on use — see #196).
17020
+ * recovery (Shamir entries are not burned on use).
16915
17021
  *
16916
17022
  * @throws ValidationError if the Noydb instance is not in managed mode.
16917
17023
  */
@@ -16959,7 +17065,7 @@ var init_noydb = __esm({
16959
17065
  }
16960
17066
  /**
16961
17067
  * Atomic peer-recovery — re-wraps an EXISTING user's keyring under
16962
- * a fresh temp passphrase in a single store write. Closes #34's
17068
+ * a fresh temp passphrase in a single store write. Closes the
16963
17069
  * partial-failure window (the previous compose-from-primitives
16964
17070
  * pattern was `db.revoke + db.grant`, two writes — if the issuer
16965
17071
  * cancelled between them the target was locked out entirely).
@@ -16969,7 +17075,7 @@ var init_noydb = __esm({
16969
17075
  * - Same `userId`, role, permissions, capabilities preserved.
16970
17076
  * - DEKs unchanged → every other principal in the vault keeps
16971
17077
  * access. No key rotation.
16972
- * - Allows owner→owner natively (#33). The existing
17078
+ * - Allows owner→owner natively. The existing
16973
17079
  * `db.revoke` retains its block — peer-recovery is a separate,
16974
17080
  * intentionally-named operation.
16975
17081
  * - Tier-2 slots dropped (they wrap the old KEK).
@@ -16998,7 +17104,6 @@ var init_noydb = __esm({
16998
17104
  * @throws `PrivilegeEscalationError` when the caller lacks a DEK
16999
17105
  * the target previously had access to.
17000
17106
  *
17001
- * @see #33 #34 — the issues this method closes.
17002
17107
  */
17003
17108
  async recoverUser(vault, options, factors) {
17004
17109
  await this.checkGate(vault, "peer-recover-user", factors);
@@ -17009,7 +17114,7 @@ var init_noydb = __esm({
17009
17114
  }
17010
17115
  }
17011
17116
  /**
17012
- * Persist a recovery enrollment. v0.1.0-pre.5 accepts the `'paper'`
17117
+ * Persist a recovery enrollment. Accepts the `'paper'`
17013
17118
  * profile.
17014
17119
  *
17015
17120
  * The hub wraps the user's DEK set (not the KEK) under a code-derived
@@ -17029,7 +17134,7 @@ var init_noydb = __esm({
17029
17134
  * showCodesToUser(codes)
17030
17135
  * ```
17031
17136
  *
17032
- * As of pre.8, `@noy-db/on-recovery`'s `generateRecoveryCodeSet`
17137
+ * `@noy-db/on-recovery`'s `generateRecoveryCodeSet`
17033
17138
  * delegates to `mintPaperRecoveryEntry` internally — its output is
17034
17139
  * fed directly to this API. Pick whichever fits your code-gen layer:
17035
17140
  *
@@ -17069,13 +17174,13 @@ var init_noydb = __esm({
17069
17174
  "#196"
17070
17175
  );
17071
17176
  }
17072
- /** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig` (#13). */
17177
+ /** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig`. */
17073
17178
  async listRecoveryEntries(vault) {
17074
17179
  const paper = await loadPaperRecoveryEntries(this.options.store, vault);
17075
17180
  const shamir = await loadShamirRecoveryEntries(this.options.store, vault);
17076
17181
  return { paper, shamir };
17077
17182
  }
17078
- // ─── Tier-3 enroll / unlock (issue #11) ────────────────────────
17183
+ // ─── Tier-3 enroll / unlock ─────────────────────────────────────
17079
17184
  /**
17080
17185
  * Register a tier-3 quick-unlock state for the vault. The state is
17081
17186
  * an opaque blob produced by `@noy-db/on-pin/enrollPin` (or any
@@ -17111,11 +17216,11 @@ var init_noydb = __esm({
17111
17216
  this.quickUnlock.delete(vault);
17112
17217
  }
17113
17218
  /**
17114
- * Public accessor for the unlocked keyring of a vault — issue #28.
17219
+ * Public accessor for the unlocked keyring of a vault.
17115
17220
  *
17116
17221
  * Returns a **defensive shallow copy** so consumers can read the DEK
17117
17222
  * map and authenticator list without the risk of mutating the hub's
17118
- * internal cache (#88). Internal hub code paths use a live reference
17223
+ * internal cache. Internal hub code paths use a live reference
17119
17224
  * via `getKeyringInternal`; ceremonies and external consumers always
17120
17225
  * get a snapshot.
17121
17226
  *