@noy-db/hub 0.2.0-pre.1 → 0.2.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 (306) hide show
  1. package/dist/aggregate/index.cjs +9 -0
  2. package/dist/aggregate/index.cjs.map +1 -1
  3. package/dist/aggregate/index.d.cts +2 -2
  4. package/dist/aggregate/index.d.ts +2 -2
  5. package/dist/aggregate/index.js +4 -4
  6. package/dist/attestation/index.cjs +305 -0
  7. package/dist/attestation/index.cjs.map +1 -0
  8. package/dist/attestation/index.d.cts +52 -0
  9. package/dist/attestation/index.d.ts +52 -0
  10. package/dist/attestation/index.js +36 -0
  11. package/dist/attestation/index.js.map +1 -0
  12. package/dist/blobs/index.cjs.map +1 -1
  13. package/dist/blobs/index.d.cts +7 -6
  14. package/dist/blobs/index.d.ts +7 -6
  15. package/dist/blobs/index.js +10 -8
  16. package/dist/blobs/index.js.map +1 -1
  17. package/dist/bundle/index.cjs +18899 -129
  18. package/dist/bundle/index.cjs.map +1 -1
  19. package/dist/bundle/index.d.cts +175 -6
  20. package/dist/bundle/index.d.ts +175 -6
  21. package/dist/bundle/index.js +533 -5
  22. package/dist/bundle/index.js.map +1 -1
  23. package/dist/{chunk-6HPZY4ON.js → chunk-26NK23DZ.js} +9 -4
  24. package/dist/chunk-26NK23DZ.js.map +1 -0
  25. package/dist/{chunk-XGSOTWYX.js → chunk-2LPPNWF6.js} +3 -3
  26. package/dist/{chunk-5SCJ5UEF.js → chunk-2N62W5YP.js} +3 -3
  27. package/dist/{chunk-537VFZTR.js → chunk-3LPV6BXR.js} +4 -4
  28. package/dist/{chunk-UA4RI7OT.js → chunk-4CLICFEY.js} +5 -5
  29. package/dist/chunk-4CLICFEY.js.map +1 -0
  30. package/dist/chunk-4USCAEDT.js +10529 -0
  31. package/dist/chunk-4USCAEDT.js.map +1 -0
  32. package/dist/chunk-5IXJGFF2.js +83 -0
  33. package/dist/chunk-5IXJGFF2.js.map +1 -0
  34. package/dist/{chunk-HB3Z2GCR.js → chunk-5OEJ6GOT.js} +2 -2
  35. package/dist/chunk-5OEJ6GOT.js.map +1 -0
  36. package/dist/chunk-5OX6XVNS.js +79 -0
  37. package/dist/chunk-5OX6XVNS.js.map +1 -0
  38. package/dist/{chunk-VMIO4IXG.js → chunk-6EOXTJS2.js} +6 -229
  39. package/dist/chunk-6EOXTJS2.js.map +1 -0
  40. package/dist/{chunk-UZXLQCHP.js → chunk-6T2UDBKG.js} +2 -2
  41. package/dist/chunk-6T2UDBKG.js.map +1 -0
  42. package/dist/{chunk-23TTQXVO.js → chunk-6YLPHBKR.js} +214 -9
  43. package/dist/chunk-6YLPHBKR.js.map +1 -0
  44. package/dist/{chunk-Z72JH4KG.js → chunk-7CEGU63S.js} +5 -35
  45. package/dist/chunk-7CEGU63S.js.map +1 -0
  46. package/dist/{chunk-PEULZC6M.js → chunk-A3JMGXPG.js} +8 -1
  47. package/dist/chunk-A3JMGXPG.js.map +1 -0
  48. package/dist/{chunk-7H6DOO3E.js → chunk-BB27JMWB.js} +211 -36
  49. package/dist/chunk-BB27JMWB.js.map +1 -0
  50. package/dist/{chunk-I6MX32UC.js → chunk-BDV7INMP.js} +4 -4
  51. package/dist/{chunk-MKSA2V7A.js → chunk-C3WE6UJY.js} +2 -2
  52. package/dist/{chunk-FCXOFQAJ.js → chunk-CH22FZHT.js} +19 -2
  53. package/dist/chunk-CH22FZHT.js.map +1 -0
  54. package/dist/{chunk-DYBQG5PQ.js → chunk-CXFOITNS.js} +2 -2
  55. package/dist/{chunk-5ZGZ6HIZ.js → chunk-CXJG63MA.js} +11 -2
  56. package/dist/chunk-CXJG63MA.js.map +1 -0
  57. package/dist/{chunk-EGQYGYIU.js → chunk-DAP2XL7Q.js} +3 -3
  58. package/dist/chunk-DAP2XL7Q.js.map +1 -0
  59. package/dist/{chunk-4TFSM22V.js → chunk-DJRWA3Q5.js} +4 -4
  60. package/dist/chunk-DRXIZOFV.js +233 -0
  61. package/dist/chunk-DRXIZOFV.js.map +1 -0
  62. package/dist/{chunk-DPMFBCV6.js → chunk-FO3UEG4S.js} +20 -3
  63. package/dist/chunk-FO3UEG4S.js.map +1 -0
  64. package/dist/{chunk-SIZWEV2Y.js → chunk-GAUEWM7D.js} +7 -5
  65. package/dist/chunk-GAUEWM7D.js.map +1 -0
  66. package/dist/{chunk-NIOHFJPJ.js → chunk-GNHAC43Q.js} +218 -119
  67. package/dist/chunk-GNHAC43Q.js.map +1 -0
  68. package/dist/chunk-HHOO7HGH.js +57 -0
  69. package/dist/chunk-HHOO7HGH.js.map +1 -0
  70. package/dist/{chunk-PA6R5ZCI.js → chunk-HQSQC2XL.js} +5 -5
  71. package/dist/chunk-HQSQC2XL.js.map +1 -0
  72. package/dist/chunk-IMYKDWB4.js +139 -0
  73. package/dist/chunk-IMYKDWB4.js.map +1 -0
  74. package/dist/{chunk-5DWL3JBF.js → chunk-LSTBFLL2.js} +2 -2
  75. package/dist/{chunk-ADQ5MQ54.js → chunk-O6EJ6WTI.js} +163 -2
  76. package/dist/chunk-O6EJ6WTI.js.map +1 -0
  77. package/dist/{chunk-OMLIZL2P.js → chunk-PC6ZEDRL.js} +12 -2
  78. package/dist/{chunk-OMLIZL2P.js.map → chunk-PC6ZEDRL.js.map} +1 -1
  79. package/dist/chunk-PM3QYWUU.js +251 -0
  80. package/dist/chunk-PM3QYWUU.js.map +1 -0
  81. package/dist/{chunk-34YSDCDP.js → chunk-PVUUIWHY.js} +2 -2
  82. package/dist/{chunk-CBAHB2BF.js → chunk-PXTQPZO4.js} +7 -70
  83. package/dist/chunk-PXTQPZO4.js.map +1 -0
  84. package/dist/{chunk-DYECX3IX.js → chunk-QSOYKKMD.js} +4 -4
  85. package/dist/chunk-QSOYKKMD.js.map +1 -0
  86. package/dist/{chunk-WCA2NROQ.js → chunk-R233SLY3.js} +2 -2
  87. package/dist/chunk-RC6SU5NO.js +36 -0
  88. package/dist/chunk-RC6SU5NO.js.map +1 -0
  89. package/dist/{chunk-P7EQ2S5O.js → chunk-RRNA5GKT.js} +2 -2
  90. package/dist/{chunk-ZNOEIM6Y.js → chunk-RYIL3PI2.js} +2 -2
  91. package/dist/chunk-STNPB3UM.js +9 -0
  92. package/dist/chunk-STNPB3UM.js.map +1 -0
  93. package/dist/{chunk-MRIBLZL3.js → chunk-TV3YZ35S.js} +5 -1
  94. package/dist/chunk-TV3YZ35S.js.map +1 -0
  95. package/dist/chunk-TY32C732.js +59 -0
  96. package/dist/chunk-TY32C732.js.map +1 -0
  97. package/dist/{chunk-YMYK7US4.js → chunk-WIBHRONM.js} +2 -2
  98. package/dist/chunk-WIBHRONM.js.map +1 -0
  99. package/dist/{chunk-YS3POABP.js → chunk-WIRRPTFH.js} +1 -1
  100. package/dist/chunk-WIRRPTFH.js.map +1 -0
  101. package/dist/{chunk-KESP7GOK.js → chunk-Y26YV5R3.js} +3 -3
  102. package/dist/{chunk-MIQHZESA.js → chunk-YM7LFCG7.js} +5 -5
  103. package/dist/{chunk-MIQHZESA.js.map → chunk-YM7LFCG7.js.map} +1 -1
  104. package/dist/{chunk-2AXFIYHT.js → chunk-Z6FNBOTC.js} +1 -1
  105. package/dist/chunk-Z6FNBOTC.js.map +1 -0
  106. package/dist/{chunk-RD5LYKD6.js → chunk-ZROPXHJY.js} +2 -2
  107. package/dist/chunk-ZROPXHJY.js.map +1 -0
  108. package/dist/consent/index.cjs.map +1 -1
  109. package/dist/consent/index.d.cts +7 -6
  110. package/dist/consent/index.d.ts +7 -6
  111. package/dist/consent/index.js +3 -3
  112. package/dist/{crypto-A7FRXYHC.js → crypto-2CRLG4F4.js} +3 -3
  113. package/dist/{delegation-YBA4X4JN.js → delegation-ZTRT2PRV.js} +5 -5
  114. package/dist/derivations/index.cjs +18 -1
  115. package/dist/derivations/index.cjs.map +1 -1
  116. package/dist/derivations/index.d.cts +10 -9
  117. package/dist/derivations/index.d.ts +10 -9
  118. package/dist/derivations/index.js +4 -4
  119. package/dist/{dev-unlock-DRwVSy2S.d.cts → dev-unlock-AglVnkPY.d.cts} +1 -1
  120. package/dist/{dev-unlock-D9s-loPr.d.ts → dev-unlock-BOEYl1xl.d.ts} +1 -1
  121. package/dist/discriminant-BN9REW3o.d.cts +60 -0
  122. package/dist/discriminant-BN9REW3o.d.ts +60 -0
  123. package/dist/executor-S76VN45G.js +8 -0
  124. package/dist/executor-UCXLIGLW.js +11 -0
  125. package/dist/executor-ZCNZJMGR.js +8 -0
  126. package/dist/{fanout-sidecar-VJ52RIEY.js → fanout-sidecar-OKPMMPLG.js} +2 -2
  127. package/dist/fanout-sidecar-OKPMMPLG.js.map +1 -0
  128. package/dist/guards/index.cjs +7 -0
  129. package/dist/guards/index.cjs.map +1 -1
  130. package/dist/guards/index.d.cts +8 -7
  131. package/dist/guards/index.d.ts +8 -7
  132. package/dist/guards/index.js +4 -4
  133. package/dist/{hash-DXXXusyk.d.ts → hash-B9m3_fhj.d.ts} +1 -1
  134. package/dist/{hash-DtRih9MQ.d.cts → hash-RVqz2zi8.d.cts} +1 -1
  135. package/dist/history/index.cjs +2 -2
  136. package/dist/history/index.cjs.map +1 -1
  137. package/dist/history/index.d.cts +8 -7
  138. package/dist/history/index.d.ts +8 -7
  139. package/dist/history/index.js +6 -6
  140. package/dist/i18n/index.cjs +287 -27
  141. package/dist/i18n/index.cjs.map +1 -1
  142. package/dist/i18n/index.d.cts +7 -6
  143. package/dist/i18n/index.d.ts +7 -6
  144. package/dist/i18n/index.js +21 -6
  145. package/dist/i18n/index.js.map +1 -1
  146. package/dist/{index-CmVgTkqk.d.cts → index-B8bjExET.d.cts} +214 -18
  147. package/dist/{index-CNwA-B6-.d.ts → index-DfUbNad8.d.ts} +214 -18
  148. package/dist/index.cjs +3329 -446
  149. package/dist/index.cjs.map +1 -1
  150. package/dist/index.d.cts +43 -22
  151. package/dist/index.d.ts +43 -22
  152. package/dist/index.js +179 -8799
  153. package/dist/index.js.map +1 -1
  154. package/dist/indexing/index.cjs +5 -1
  155. package/dist/indexing/index.cjs.map +1 -1
  156. package/dist/indexing/index.d.cts +3 -3
  157. package/dist/indexing/index.d.ts +3 -3
  158. package/dist/indexing/index.js +4 -4
  159. package/dist/issue-3W6IVLKH.js +12 -0
  160. package/dist/{lazy-builder-Rpd-V3jP.d.ts → lazy-builder-Ci5_YG73.d.cts} +2 -2
  161. package/dist/{lazy-builder-C-rPfWG0.d.cts → lazy-builder-D5GU14TS.d.ts} +2 -2
  162. package/dist/{ledger-3TXNP47J.js → ledger-O7FXOG3D.js} +6 -6
  163. package/dist/materialized-views/index.cjs +21 -2
  164. package/dist/materialized-views/index.cjs.map +1 -1
  165. package/dist/materialized-views/index.d.cts +23 -20
  166. package/dist/materialized-views/index.d.ts +23 -20
  167. package/dist/materialized-views/index.js +12 -12
  168. package/dist/noydb-YAZNH5TI.js +34 -0
  169. package/dist/overlay-views/index.cjs +11 -1
  170. package/dist/overlay-views/index.cjs.map +1 -1
  171. package/dist/overlay-views/index.d.cts +10 -9
  172. package/dist/overlay-views/index.d.ts +10 -9
  173. package/dist/overlay-views/index.js +6 -4
  174. package/dist/periods/index.cjs.map +1 -1
  175. package/dist/periods/index.d.cts +7 -6
  176. package/dist/periods/index.d.ts +7 -6
  177. package/dist/periods/index.js +6 -6
  178. package/dist/{predicate-Dnu81tsS.d.cts → predicate-Bt5ft-9c.d.cts} +28 -3
  179. package/dist/{predicate-Dnu81tsS.d.ts → predicate-Bt5ft-9c.d.ts} +28 -3
  180. package/dist/{public-envelope-PY6NKFLI.js → public-envelope-HMYHZIRH.js} +4 -4
  181. package/dist/query/index.cjs +255 -6
  182. package/dist/query/index.cjs.map +1 -1
  183. package/dist/query/index.d.cts +3 -3
  184. package/dist/query/index.d.ts +3 -3
  185. package/dist/query/index.js +12 -6
  186. package/dist/registry-DKEXOJVO.js +7 -0
  187. package/dist/{registry-3L3N3PTG.js → registry-ST2VNFZC.js} +3 -3
  188. package/dist/registry-UFIK7CSR.js +8 -0
  189. package/dist/registry-ZGYYSM5I.js +8 -0
  190. package/dist/registry-ZGYYSM5I.js.map +1 -0
  191. package/dist/revoke-S6JMSLUN.js +17 -0
  192. package/dist/revoke-S6JMSLUN.js.map +1 -0
  193. package/dist/session/index.cjs.map +1 -1
  194. package/dist/session/index.d.cts +8 -7
  195. package/dist/session/index.d.ts +8 -7
  196. package/dist/session/index.js +3 -3
  197. package/dist/shadow/index.cjs.map +1 -1
  198. package/dist/shadow/index.d.cts +7 -6
  199. package/dist/shadow/index.d.ts +7 -6
  200. package/dist/shadow/index.js +2 -2
  201. package/dist/signer-7NPTB3SQ.js +18 -0
  202. package/dist/signer-7NPTB3SQ.js.map +1 -0
  203. package/dist/snapshots/index.cjs +937 -0
  204. package/dist/snapshots/index.cjs.map +1 -0
  205. package/dist/snapshots/index.d.cts +28 -0
  206. package/dist/snapshots/index.d.ts +28 -0
  207. package/dist/snapshots/index.js +152 -0
  208. package/dist/snapshots/index.js.map +1 -0
  209. package/dist/{stale-HSC5YO2O.js → stale-VKXSXJF4.js} +2 -2
  210. package/dist/stale-VKXSXJF4.js.map +1 -0
  211. package/dist/store/index.cjs.map +1 -1
  212. package/dist/store/index.d.cts +7 -6
  213. package/dist/store/index.d.ts +7 -6
  214. package/dist/store/index.js +2 -2
  215. package/dist/{strategy-DSTrsZ8t.d.cts → strategy-CT2LCKAX.d.cts} +12 -0
  216. package/dist/{strategy-DSTrsZ8t.d.ts → strategy-CT2LCKAX.d.ts} +12 -0
  217. package/dist/sync/index.cjs.map +1 -1
  218. package/dist/sync/index.d.cts +6 -5
  219. package/dist/sync/index.d.ts +6 -5
  220. package/dist/sync/index.js +4 -4
  221. package/dist/team/index.cjs +1 -1
  222. package/dist/team/index.cjs.map +1 -1
  223. package/dist/team/index.d.cts +7 -6
  224. package/dist/team/index.d.ts +7 -6
  225. package/dist/team/index.js +13 -11
  226. package/dist/tx/index.cjs +82 -2
  227. package/dist/tx/index.cjs.map +1 -1
  228. package/dist/tx/index.d.cts +8 -7
  229. package/dist/tx/index.d.ts +8 -7
  230. package/dist/tx/index.js +56 -3
  231. package/dist/tx/index.js.map +1 -1
  232. package/dist/{types-DW9RGSSs.d.ts → types-CaNQm4i8.d.ts} +1270 -259
  233. package/dist/{types-C4lwMKKF.d.cts → types-n2_IfwlQ.d.cts} +1270 -259
  234. package/dist/{index-4agOpzqd.d.ts → ulid-B9SMWj5i.d.ts} +64 -46
  235. package/dist/{index-hdFvZkBP.d.cts → ulid-CLMjmyhG.d.cts} +64 -46
  236. package/dist/util/index.cjs +7 -0
  237. package/dist/util/index.cjs.map +1 -1
  238. package/dist/util/index.d.cts +2 -0
  239. package/dist/util/index.d.ts +2 -0
  240. package/dist/util/index.js +5 -1
  241. package/dist/util/index.js.map +1 -1
  242. package/dist/{with-derivation-g-pGoMzL.d.ts → with-derivation-CVIOPTUf.d.ts} +1 -1
  243. package/dist/{with-derivation-C8LDlV7t.d.cts → with-derivation-aKrtS7Jj.d.cts} +1 -1
  244. package/dist/{with-guard-jI1x9Z3k.d.cts → with-guard-DZQbPzoP.d.cts} +1 -1
  245. package/dist/{with-guard-DWOCK4Ca.d.ts → with-guard-DseETUrF.d.ts} +1 -1
  246. package/dist/{with-materialized-view-DaKR-N6J.d.ts → with-materialized-view-C1eA1_T_.d.cts} +2 -2
  247. package/dist/{with-materialized-view-DcTx4H3j.d.cts → with-materialized-view-DaYaE8-Q.d.ts} +2 -2
  248. package/dist/{with-overlayed-view-N7jYuNOS.d.ts → with-overlayed-view-DQsh2p8H.d.ts} +2 -2
  249. package/dist/{with-overlayed-view-D-6oWAgM.d.cts → with-overlayed-view-DleJfKcV.d.cts} +2 -2
  250. package/package.json +27 -4
  251. package/dist/chunk-23TTQXVO.js.map +0 -1
  252. package/dist/chunk-2AXFIYHT.js.map +0 -1
  253. package/dist/chunk-5ZGZ6HIZ.js.map +0 -1
  254. package/dist/chunk-6HPZY4ON.js.map +0 -1
  255. package/dist/chunk-7H6DOO3E.js.map +0 -1
  256. package/dist/chunk-ADQ5MQ54.js.map +0 -1
  257. package/dist/chunk-CBAHB2BF.js.map +0 -1
  258. package/dist/chunk-DPMFBCV6.js.map +0 -1
  259. package/dist/chunk-DYECX3IX.js.map +0 -1
  260. package/dist/chunk-EGQYGYIU.js.map +0 -1
  261. package/dist/chunk-FCXOFQAJ.js.map +0 -1
  262. package/dist/chunk-HB3Z2GCR.js.map +0 -1
  263. package/dist/chunk-MRIBLZL3.js.map +0 -1
  264. package/dist/chunk-NIOHFJPJ.js.map +0 -1
  265. package/dist/chunk-PA6R5ZCI.js.map +0 -1
  266. package/dist/chunk-PEULZC6M.js.map +0 -1
  267. package/dist/chunk-RD5LYKD6.js.map +0 -1
  268. package/dist/chunk-SIZWEV2Y.js.map +0 -1
  269. package/dist/chunk-UA4RI7OT.js.map +0 -1
  270. package/dist/chunk-UZXLQCHP.js.map +0 -1
  271. package/dist/chunk-VMIO4IXG.js.map +0 -1
  272. package/dist/chunk-YMYK7US4.js.map +0 -1
  273. package/dist/chunk-YS3POABP.js.map +0 -1
  274. package/dist/chunk-Z72JH4KG.js.map +0 -1
  275. package/dist/executor-7E3VFGW7.js +0 -11
  276. package/dist/executor-CEWX2FQI.js +0 -8
  277. package/dist/executor-X4SQ3ZLC.js +0 -8
  278. package/dist/fanout-sidecar-VJ52RIEY.js.map +0 -1
  279. package/dist/registry-O47PUPSY.js +0 -8
  280. package/dist/registry-RFGGMVNJ.js +0 -7
  281. package/dist/registry-WLLMODKN.js +0 -8
  282. /package/dist/{chunk-XGSOTWYX.js.map → chunk-2LPPNWF6.js.map} +0 -0
  283. /package/dist/{chunk-5SCJ5UEF.js.map → chunk-2N62W5YP.js.map} +0 -0
  284. /package/dist/{chunk-537VFZTR.js.map → chunk-3LPV6BXR.js.map} +0 -0
  285. /package/dist/{chunk-I6MX32UC.js.map → chunk-BDV7INMP.js.map} +0 -0
  286. /package/dist/{chunk-MKSA2V7A.js.map → chunk-C3WE6UJY.js.map} +0 -0
  287. /package/dist/{chunk-DYBQG5PQ.js.map → chunk-CXFOITNS.js.map} +0 -0
  288. /package/dist/{chunk-4TFSM22V.js.map → chunk-DJRWA3Q5.js.map} +0 -0
  289. /package/dist/{chunk-5DWL3JBF.js.map → chunk-LSTBFLL2.js.map} +0 -0
  290. /package/dist/{chunk-34YSDCDP.js.map → chunk-PVUUIWHY.js.map} +0 -0
  291. /package/dist/{chunk-WCA2NROQ.js.map → chunk-R233SLY3.js.map} +0 -0
  292. /package/dist/{chunk-P7EQ2S5O.js.map → chunk-RRNA5GKT.js.map} +0 -0
  293. /package/dist/{chunk-ZNOEIM6Y.js.map → chunk-RYIL3PI2.js.map} +0 -0
  294. /package/dist/{chunk-KESP7GOK.js.map → chunk-Y26YV5R3.js.map} +0 -0
  295. /package/dist/{crypto-A7FRXYHC.js.map → crypto-2CRLG4F4.js.map} +0 -0
  296. /package/dist/{delegation-YBA4X4JN.js.map → delegation-ZTRT2PRV.js.map} +0 -0
  297. /package/dist/{executor-7E3VFGW7.js.map → executor-S76VN45G.js.map} +0 -0
  298. /package/dist/{executor-CEWX2FQI.js.map → executor-UCXLIGLW.js.map} +0 -0
  299. /package/dist/{executor-X4SQ3ZLC.js.map → executor-ZCNZJMGR.js.map} +0 -0
  300. /package/dist/{ledger-3TXNP47J.js.map → issue-3W6IVLKH.js.map} +0 -0
  301. /package/dist/{public-envelope-PY6NKFLI.js.map → ledger-O7FXOG3D.js.map} +0 -0
  302. /package/dist/{registry-3L3N3PTG.js.map → noydb-YAZNH5TI.js.map} +0 -0
  303. /package/dist/{registry-O47PUPSY.js.map → public-envelope-HMYHZIRH.js.map} +0 -0
  304. /package/dist/{registry-RFGGMVNJ.js.map → registry-DKEXOJVO.js.map} +0 -0
  305. /package/dist/{registry-WLLMODKN.js.map → registry-ST2VNFZC.js.map} +0 -0
  306. /package/dist/{stale-HSC5YO2O.js.map → registry-UFIK7CSR.js.map} +0 -0
@@ -1,8 +1,9 @@
1
- import { I as IndexStrategy, d as LazyQuery } from './lazy-builder-Rpd-V3jP.js';
2
- import { b as AggregateSpec, A as AggregateStrategy } from './strategy-DSTrsZ8t.js';
1
+ import { I as IndexStrategy, d as LazyQuery } from './lazy-builder-D5GU14TS.js';
2
+ import { b as AggregateSpec, A as AggregateStrategy } from './strategy-CT2LCKAX.js';
3
3
  import { C as CrdtStrategy, a as CrdtMode, b as CrdtState } from './strategy-BSxFXGzb.js';
4
- import { N as NoydbError, Q as Query, ak as RefRegistry, ah as RefDescriptor, _ as JoinableSource, am as RefViolation, an as ScanBuilder } from './index-CNwA-B6-.js';
5
- import { F as FieldClause, I as IndexDef, C as CollectionIndexes } from './predicate-Dnu81tsS.js';
4
+ import { N as NoydbError, Q as Query, aw as RefRegistry, at as RefDescriptor, a7 as JoinableSource, ay as RefViolation, az as ScanBuilder } from './index-DfUbNad8.js';
5
+ import { F as FieldClause, I as IndexDef, C as CollectionIndexes } from './predicate-Bt5ft-9c.js';
6
+ import { AttestationFieldSchema, RevocationList } from '@noy-db/attestation';
6
7
 
7
8
  /**
8
9
  * Standard Schema v1 integration.
@@ -794,8 +795,15 @@ interface LedgerEntry {
794
795
  * guards subsystem when an admin/owner uses `withTransactions(...)`
795
796
  * to repair a constraint-violating state. See `amendment` field
796
797
  * below for the structured payload.
798
+ *
799
+ * `'lifecycle'` records a non-data audit event (e.g. partition
800
+ * handover) — `collection`/`id` are empty and the event detail
801
+ * lives in `reason` (e.g. `'partition-handed-over:<sealId>'`). Like
802
+ * `amendment`, it carries no data envelope, so `verifyBackupIntegrity`
803
+ * skips it in the data cross-check (it still participates in the
804
+ * tamper-evident chain).
797
805
  */
798
- readonly op: 'put' | 'delete' | 'amendment';
806
+ readonly op: 'put' | 'delete' | 'amendment' | 'lifecycle' | 'migration';
799
807
  /** The collection the mutation targeted. */
800
808
  readonly collection: string;
801
809
  /** The record id the mutation targeted. */
@@ -820,8 +828,8 @@ interface LedgerEntry {
820
828
  */
821
829
  readonly payloadHash: string;
822
830
  /**
823
- * Optional human-readable tag describing why this mutation happened
824
- * (#1). Threaded through `collection.put(_, _, { reason })`. Common
831
+ * Optional human-readable tag describing why this mutation happened.
832
+ * Threaded through `collection.put(_, _, { reason })`. Common
825
833
  * values include `'import:csv'`, `'import:json'`, `'import:xlsx'` from
826
834
  * `as-*` ImportPlan.apply(), but consumers can use any string for
827
835
  * domain-specific audit filtering. Auto-strip via `canonicalJson` —
@@ -1098,8 +1106,8 @@ interface AppendInput {
1098
1106
  */
1099
1107
  amendment?: LedgerEntry['amendment'];
1100
1108
  /**
1101
- * Optional human-readable tag describing why this mutation happened
1102
- * (#1). Threaded from `collection.put(_, _, { reason })`.
1109
+ * Optional human-readable tag describing why this mutation happened.
1110
+ * Threaded from `collection.put(_, _, { reason })`.
1103
1111
  * Carried verbatim onto the resulting ledger entry's `reason` field;
1104
1112
  * omitted from canonical JSON when undefined.
1105
1113
  */
@@ -1636,6 +1644,51 @@ interface PeriodsStrategy {
1636
1644
  appendPeriodLedgerEntry(ledger: LedgerStore | null, actor: string, envelope: EncryptedEnvelope, periodName: string): Promise<void>;
1637
1645
  }
1638
1646
 
1647
+ /**
1648
+ * Per-layer i18n resolution policy.
1649
+ *
1650
+ * `onMissing` governs what happens when a multilingual field is resolved
1651
+ * to a target locale that is absent. It may be a single scalar policy or
1652
+ * a per-layer map, so a field can be lenient at the app read boundary but
1653
+ * strict inside a materialized view.
1654
+ *
1655
+ * Effective policy for layer `λ`:
1656
+ *
1657
+ * ```
1658
+ * explicit(λ) = typeof onMissing === 'object' ? onMissing[λ] : undefined
1659
+ * scalar = typeof onMissing === 'string' ? onMissing : undefined
1660
+ * policy(λ) = explicit(λ) ?? layerDefault(λ) ?? scalar ?? 'throw'
1661
+ * ```
1662
+ *
1663
+ * - `layerDefault('guard') = 'substitute'` — guards are lenient unless
1664
+ * EXPLICITLY overridden; they never inherit a scalar policy (a guard
1665
+ * reading a display value must not hard-fail on a missing locale).
1666
+ * - every other layer has no default, so it inherits the scalar, else
1667
+ * falls back to `'throw'` (today's behavior — zero breaking change).
1668
+ *
1669
+ * @public
1670
+ */
1671
+ type OnMissing = 'substitute' | 'null' | 'throw';
1672
+ /**
1673
+ * The contexts in which a multilingual field is resolved. Each can carry
1674
+ * its own `onMissing` policy.
1675
+ *
1676
+ * - `read` — ordinary app reads (`get`/`list`/query projection).
1677
+ * - `guard` — a guard callback reading a value.
1678
+ * - `join` — a joined record expanded onto a row.
1679
+ * - `mv` — materialized-view input.
1680
+ * - `derivation` — derivation input.
1681
+ * - `export` — bundle/public-envelope export.
1682
+ */
1683
+ type Layer = 'read' | 'guard' | 'join' | 'mv' | 'derivation' | 'export';
1684
+ /** Field-level policy: a single scalar, or a per-layer map. */
1685
+ type OnMissingPolicy = OnMissing | Partial<Record<Layer, OnMissing>>;
1686
+ /**
1687
+ * Resolve the effective `OnMissing` for a layer from a field's declared
1688
+ * policy. See module docs for the resolution rule.
1689
+ */
1690
+ declare function resolvePolicy(onMissing: OnMissingPolicy | undefined, layer: Layer): OnMissing;
1691
+
1639
1692
  /**
1640
1693
  * i18nText schema type —
1641
1694
  *
@@ -1677,6 +1730,37 @@ interface PeriodsStrategy {
1677
1730
  * Pluralization, RTL rendering, date/number formatting, per-locale CRDT
1678
1731
  * merging.
1679
1732
  */
1733
+
1734
+ /** Flatten an intersection into a single object literal for nicer hovers. */
1735
+ type Prettify<T> = {
1736
+ [K in keyof T]: T[K];
1737
+ } & {};
1738
+ /**
1739
+ * The stored shape of a multilingual field, inferred from its `required`
1740
+ * mode — so the compiler forces you to handle an absent optional locale
1741
+ * (`string | undefined`) instead of silently yielding `undefined`.
1742
+ *
1743
+ * Mirrors `i18nText({ languages, required })`:
1744
+ * - `'all'` (default) — every locale required: `{ th: string; en: string }`
1745
+ * - `'any'` — every locale optional: `{ th?: string; en?: string }`
1746
+ * (the "at least one present" guarantee is runtime-only — not expressible
1747
+ * in TypeScript — so each key is optional)
1748
+ * - `readonly L[]` — listed locales required, the rest optional:
1749
+ * `I18nMap<'th'|'en', ['th']>` → `{ th: string; en?: string }`
1750
+ *
1751
+ * @example
1752
+ * ```ts
1753
+ * type Lang = 'th' | 'en'
1754
+ * interface Contact {
1755
+ * name: I18nMap<Lang, 'any'> // { th?: string; en?: string }
1756
+ * legalName: I18nMap<Lang, ['th']> // { th: string; en?: string }
1757
+ * slug: I18nMap<Lang> // { th: string; en: string }
1758
+ * }
1759
+ * ```
1760
+ *
1761
+ * @public
1762
+ */
1763
+ type I18nMap<Langs extends string, Required extends 'all' | 'any' | readonly Langs[] = 'all'> = Required extends 'all' ? Record<Langs, string> : Required extends 'any' ? Partial<Record<Langs, string>> : Required extends readonly (infer R extends Langs)[] ? Prettify<Record<R, string> & Partial<Record<Exclude<Langs, R>, string>>> : never;
1680
1764
  /**
1681
1765
  * Options for `i18nText()`.
1682
1766
  *
@@ -1705,6 +1789,38 @@ interface I18nTextOptions {
1705
1789
  * before `put()` if a translator is configured. Default: `false`.
1706
1790
  */
1707
1791
  readonly autoTranslate?: boolean;
1792
+ /**
1793
+ * What to do when this field is resolved to a locale that is absent.
1794
+ * A single policy, or a per-layer map (read/guard/join/mv/derivation/
1795
+ * export). Default `'throw'` — today's behavior, zero breaking change.
1796
+ * See {@link OnMissingPolicy}.
1797
+ *
1798
+ * NOTE (current wiring): the `read` layer (`get`/`list`) is enforced.
1799
+ * Guard, derivation, mv, join and export reads currently resolve
1800
+ * through the same read path and so observe the `read`/scalar policy;
1801
+ * dedicated per-layer enforcement for those contexts is a tracked
1802
+ * follow-up (mv/join additionally require resolution to be injected
1803
+ * into the query/aggregate pipeline, which today reads raw maps).
1804
+ */
1805
+ readonly onMissing?: OnMissingPolicy;
1806
+ /**
1807
+ * Ordered preferred-substitute locales used when `onMissing` resolves
1808
+ * to `'substitute'` and the target locale is absent. `'any'` as an
1809
+ * element means "first non-empty value". A caller-supplied `fallback`
1810
+ * at read time takes precedence over this declared list.
1811
+ */
1812
+ readonly substitute?: readonly string[];
1813
+ /**
1814
+ * Per-locale script enforcement (write-time). `'auto'` infers the
1815
+ * allowed Unicode scripts per locale (asymmetric Latin tolerance); an
1816
+ * object overrides per slot. Absent ⇒ no check. See `./script.ts`.
1817
+ */
1818
+ readonly script?: 'auto' | Partial<Record<string, readonly string[]>>;
1819
+ /**
1820
+ * What to do when a slot's value contains characters outside its
1821
+ * allowed script set. Default `'reject'`.
1822
+ */
1823
+ readonly onScriptViolation?: 'reject' | 'filter' | 'warn';
1708
1824
  }
1709
1825
  /**
1710
1826
  * Descriptor returned by `i18nText()`. Attach to the collection's
@@ -1759,23 +1875,52 @@ declare function validateI18nTextValue(value: unknown, field: string, descriptor
1759
1875
  * @param field Field name used in `LocaleNotSpecifiedError` messages.
1760
1876
  * @returns The resolved string, OR the original map when `locale === 'raw'`.
1761
1877
  */
1878
+ /** Options for the policy-aware form of {@link resolveI18nText}. */
1879
+ interface ResolveI18nOptions {
1880
+ /** Effective policy for the resolution layer. Default `'throw'`. */
1881
+ readonly policy?: OnMissing;
1882
+ /** Declared substitute chain; applied only under policy `'substitute'`. */
1883
+ readonly substitute?: readonly string[];
1884
+ }
1762
1885
  declare function resolveI18nText(value: Record<string, string>, locale: string, fallback?: string | readonly string[], field?: string): string | Record<string, string>;
1886
+ declare function resolveI18nText(value: Record<string, string>, locale: string, fallback: string | readonly string[] | undefined, field: string | undefined, opts: ResolveI18nOptions): string | Record<string, string> | null;
1887
+ /**
1888
+ * Return all leaf values at `path`, expanding `[].` array wildcards.
1889
+ *
1890
+ * - `'name'` → `[obj.name]`
1891
+ * - `'address.lineOne'` → `[obj.address.lineOne]`
1892
+ * - `'contacts[].title'` → `[obj.contacts[0].title, obj.contacts[1].title, …]`
1893
+ *
1894
+ * Returns an empty array when the path does not resolve (missing key,
1895
+ * wrong type, etc.). Used by `enforceI18nOnPut` to validate nested fields.
1896
+ */
1897
+ declare function getAtPath(obj: Record<string, unknown>, path: string): unknown[];
1763
1898
  /**
1764
- * Apply locale resolution to a single record, in-place over a copy.
1899
+ * Mutate `obj` in-place, setting `value` at the nested `path`.
1900
+ * Supports dot notation (`'address.lineOne'`) but not array wildcards —
1901
+ * auto-translate on `contacts[].title` style paths is not supported.
1902
+ */
1903
+ declare function setAtPathInPlace(obj: Record<string, unknown>, path: string, value: unknown): void;
1904
+ /**
1905
+ * Apply locale resolution to a single record, returning a new copy.
1765
1906
  *
1766
1907
  * For each field registered as an `i18nText` descriptor:
1767
1908
  * - If `locale === 'raw'`, the field value is left as the stored map.
1768
1909
  * - Otherwise, the field value is replaced with the resolved string.
1769
1910
  *
1770
- * Records that are not plain objects (null, array, primitives) are
1771
- * returned unchanged.
1911
+ * Field paths support dot notation (`'address.lineOne'`) and array
1912
+ * wildcards (`'contacts[].title'`). Top-level fields work as before.
1772
1913
  *
1773
1914
  * @param record The decrypted record.
1774
- * @param i18nFields Map of field name → `I18nTextDescriptor`.
1915
+ * @param i18nFields Map of field path → `I18nTextDescriptor`.
1775
1916
  * @param locale The requested locale (or `'raw'`).
1776
1917
  * @param fallback Fallback chain (optional).
1918
+ * @param layer Resolution layer (default `'read'`). Each field's
1919
+ * `onMissing` policy is resolved for this layer, so the
1920
+ * same record resolves leniently on a get but strictly
1921
+ * inside an mv/derivation.
1777
1922
  */
1778
- declare function applyI18nLocale(record: Record<string, unknown>, i18nFields: Record<string, I18nTextDescriptor>, locale: string, fallback?: string | readonly string[]): Record<string, unknown>;
1923
+ declare function applyI18nLocale(record: Record<string, unknown>, i18nFields: Record<string, I18nTextDescriptor>, locale: string, fallback?: string | readonly string[], layer?: Layer): Record<string, unknown>;
1779
1924
 
1780
1925
  type EventHandler<T> = (data: T) => void;
1781
1926
  /** Typed event emitter for NOYDB events. */
@@ -1840,7 +1985,6 @@ interface PassphrasePolicy {
1840
1985
  * double-space). For non-space-delimited word semantics, use
1841
1986
  * {@link customValidator} instead.
1842
1987
  *
1843
- * Added in pre.8 (#31).
1844
1988
  */
1845
1989
  readonly pattern?: RegExp;
1846
1990
  /**
@@ -1858,7 +2002,6 @@ interface PassphrasePolicy {
1858
2002
  * {@link assertStrongPassphrase} dispatches on — `ok: true` accepts;
1859
2003
  * `ok: false` throws `WeakPassphraseError` with the supplied reason.
1860
2004
  *
1861
- * Added in pre.8 (#31).
1862
2005
  */
1863
2006
  readonly customValidator?: (phrase: string) => PassphraseValidationResult;
1864
2007
  }
@@ -1975,7 +2118,7 @@ interface UnlockedKeyring {
1975
2118
  * - Unencrypted mode (no KEK exists)
1976
2119
  * - Tier-3 PIN quick-resume (`@noy-db/on-pin`)
1977
2120
  * - Wrap-DEKs tier-2 unlock (`@noy-db/on-password`'s
1978
- * `verifyPasswordSlot` after #26 Path C)
2121
+ * `verifyPasswordSlot`)
1979
2122
  * - Session-state restore (`session/session.ts`)
1980
2123
  * - Dev-unlock fixture (`session/dev-unlock.ts`)
1981
2124
  *
@@ -1984,9 +2127,8 @@ interface UnlockedKeyring {
1984
2127
  * null-check and throw a clear error if absent — re-authenticate
1985
2128
  * at tier 1 first to recover the KEK.
1986
2129
  *
1987
- * Tightened from `CryptoKey` to `CryptoKey | null` in pre.8 (#41).
1988
- * The runtime contract has always allowed null; the type now
1989
- * matches reality.
2130
+ * Tightened from `CryptoKey` to `CryptoKey | null`; the runtime
2131
+ * contract has always allowed null, the type now matches reality.
1990
2132
  */
1991
2133
  readonly kek: CryptoKey | null;
1992
2134
  readonly salt: Uint8Array;
@@ -2007,7 +2149,7 @@ interface UnlockedKeyring {
2007
2149
  /**
2008
2150
  * Tier-2 authenticator slots — readonly snapshot loaded from the
2009
2151
  * keyring file. Mutations go through `enrollAuthenticator` /
2010
- * `removeAuthenticator` (issue #11), which write back via
2152
+ * `removeAuthenticator`, which write back via
2011
2153
  * `persistKeyring`. Always defined; loads with an empty array for
2012
2154
  * keyrings written before the multi-slot extension landed.
2013
2155
  */
@@ -2060,7 +2202,6 @@ declare function revoke(adapter: NoydbStore, vault: string, callerKeyring: Unloc
2060
2202
  * @throws `PermissionDeniedError` when the role hierarchy rejects.
2061
2203
  * @throws `ValidationError` when the diff is empty (nothing to update).
2062
2204
  *
2063
- * @see #54
2064
2205
  */
2065
2206
  declare function updateKeyringIdentity(adapter: NoydbStore, vault: string, callerKeyring: UnlockedKeyring, options: UpdateUserOptions): Promise<void>;
2066
2207
  /**
@@ -2155,7 +2296,7 @@ interface ListUsersOptions {
2155
2296
  * `userEnvelopeDek` is the vault's `_users` collection DEK
2156
2297
  * (`vault.getDEK('_users')`); used to decrypt every envelope.
2157
2298
  *
2158
- * `callerRole` (#122) drives the directory-visibility checks:
2299
+ * `callerRole` drives the directory-visibility checks:
2159
2300
  *
2160
2301
  * - When the vault's `_meta/directory` document has `enabled: false`,
2161
2302
  * only `owner` and `admin` callers may enumerate; anyone else gets
@@ -2165,7 +2306,7 @@ interface ListUsersOptions {
2165
2306
  * `{ includeHidden: true }` to see them; lower roles passing that
2166
2307
  * option get `PermissionDeniedError`.
2167
2308
  *
2168
- * Honest caveat (#122): these filters are a UX hint, not a security
2309
+ * Honest caveat: these filters are a UX hint, not a security
2169
2310
  * boundary. The keyring file is still listed at `_keyring/*` and the
2170
2311
  * envelope ciphertext at `_users/*`. A caller with direct store access
2171
2312
  * — or a caller that calls this function with `callerRole: 'owner'`
@@ -2304,6 +2445,16 @@ interface DictKeyDescriptor<Keys extends string = string> {
2304
2445
  readonly name: string;
2305
2446
  /** Declared valid keys. When set, `put()` rejects keys not in this set. */
2306
2447
  readonly keys: readonly Keys[] | undefined;
2448
+ /**
2449
+ * What to do when a label is missing for the resolved locale. Mirrors
2450
+ * `i18nText`'s `onMissing`. Default behavior (when unset) preserves the
2451
+ * legacy contract: a missing label is omitted (scalar) or `null`
2452
+ * (array element) — i.e. `'null'`. Set `'substitute'` to walk the
2453
+ * declared `substitute` chain, or `'throw'` to raise.
2454
+ */
2455
+ readonly onMissing?: OnMissingPolicy;
2456
+ /** Ordered preferred-substitute locales for label resolution. */
2457
+ readonly substitute?: readonly string[];
2307
2458
  }
2308
2459
  /**
2309
2460
  * Create a `DictKeyDescriptor` for a dictionary-backed enum field.
@@ -2322,7 +2473,10 @@ interface DictKeyDescriptor<Keys extends string = string> {
2322
2473
  * })
2323
2474
  * ```
2324
2475
  */
2325
- declare function dictKey<Keys extends string>(name: string, keys?: readonly Keys[]): DictKeyDescriptor<Keys>;
2476
+ declare function dictKey<Keys extends string>(name: string, keys?: readonly Keys[], opts?: {
2477
+ onMissing?: OnMissingPolicy;
2478
+ substitute?: readonly string[];
2479
+ }): DictKeyDescriptor<Keys>;
2326
2480
  /** Runtime predicate for detecting a DictKeyDescriptor. */
2327
2481
  declare function isDictKeyDescriptor(x: unknown): x is DictKeyDescriptor;
2328
2482
  /**
@@ -2466,6 +2620,32 @@ declare class DictionaryHandle<Keys extends string = string> {
2466
2620
  resolveLabel(key: string, locale: string, fallback?: string | readonly string[]): Promise<string | undefined>;
2467
2621
  }
2468
2622
 
2623
+ /**
2624
+ * Infer the allowed Unicode scripts for a BCP-47 locale, with asymmetric
2625
+ * Latin tolerance. A script subtag (`th-Latn`) wins over the base
2626
+ * language. Unknown locales default to `['Latin']`.
2627
+ */
2628
+ declare function inferScripts(locale: string): readonly string[];
2629
+ /** A non-fatal script violation recorded under `'filter'`/`'warn'` modes. */
2630
+ interface ScriptWarning {
2631
+ readonly field: string;
2632
+ readonly locale: string;
2633
+ readonly expected: readonly string[];
2634
+ readonly sample: string;
2635
+ }
2636
+ /**
2637
+ * Enforce a field's script constraint over an i18nText value map.
2638
+ *
2639
+ * - No `script` option ⇒ returns the value unchanged.
2640
+ * - `onScriptViolation: 'reject'` (default) ⇒ throws {@link ScriptViolationError}.
2641
+ * - `'filter'` ⇒ returns a copy with disallowed characters stripped + warnings.
2642
+ * - `'warn'` ⇒ returns the value unchanged + warnings.
2643
+ */
2644
+ declare function enforceScript(value: Record<string, unknown>, field: string, descriptor: I18nTextDescriptor): {
2645
+ value: Record<string, unknown>;
2646
+ warnings: ScriptWarning[];
2647
+ };
2648
+
2469
2649
  /**
2470
2650
  * Strategy seam for the optional i18n (multi-locale + dictionary)
2471
2651
  * subsystem. Core imports `I18nStrategy` type-only + `NO_I18N` stub;
@@ -2527,13 +2707,23 @@ interface I18nStrategy {
2527
2707
  * return a new object. Returns the input unchanged under
2528
2708
  * `NO_I18N`.
2529
2709
  */
2530
- applyI18nLocale(record: Record<string, unknown>, fields: Record<string, I18nTextDescriptor>, locale: string, fallback?: string | readonly string[]): Record<string, unknown>;
2710
+ applyI18nLocale(record: Record<string, unknown>, fields: Record<string, I18nTextDescriptor>, locale: string, fallback?: string | readonly string[], layer?: Layer): Record<string, unknown>;
2531
2711
  /**
2532
2712
  * Validate that an i18nText field's value satisfies its descriptor
2533
2713
  * (required locales present, etc.). Throws under `NO_I18N` —
2534
2714
  * declaring i18nFields without opting in is a misconfiguration.
2535
2715
  */
2536
2716
  validateI18nTextValue(value: unknown, field: string, descriptor: I18nTextDescriptor): void;
2717
+ /**
2718
+ * Enforce per-locale script constraints over an i18nText value map
2719
+ * (write-time). Returns the (possibly filtered) value plus any
2720
+ * non-fatal warnings. Returns the value unchanged when the field has
2721
+ * no `script` option, or under `NO_I18N`.
2722
+ */
2723
+ enforceScript(value: Record<string, unknown>, field: string, descriptor: I18nTextDescriptor): {
2724
+ value: Record<string, unknown>;
2725
+ warnings: ScriptWarning[];
2726
+ };
2537
2727
  /**
2538
2728
  * Construct a typed `DictionaryHandle` for the named dictionary.
2539
2729
  * Throws under `NO_I18N`.
@@ -2541,6 +2731,326 @@ interface I18nStrategy {
2541
2731
  buildDictionaryHandle<Keys extends string = string>(opts: BuildDictionaryHandleOptions<Keys>): DictionaryHandle<Keys>;
2542
2732
  }
2543
2733
 
2734
+ /**
2735
+ * Observable write-queue.
2736
+ *
2737
+ * Tracks outstanding in-flight *logical* writes (a full Collection.put /
2738
+ * delete, including ledger + cache + derivation + MV dispatch — not just
2739
+ * the adapter call). The hub holds one tracker per instance; it is
2740
+ * framework-agnostic (no Vue/React dependency). UI layers subscribe via
2741
+ * onChange(); the migration drain (Slice 2) quiesces via onFlush().
2742
+ */
2743
+ /** Public, read-only view of the hub's write-queue. */
2744
+ interface WriteQueue {
2745
+ /** True while one or more writes are in flight (`depth > 0`). */
2746
+ readonly pending: boolean;
2747
+ /** Count of outstanding write operations. */
2748
+ readonly depth: number;
2749
+ /**
2750
+ * Subscribe to depth changes (fires on every begin and settle).
2751
+ * Returns an unsubscribe function. Intended for reactive wrappers
2752
+ * (e.g. `@noy-db/in-vue` turns this into a `ref`).
2753
+ */
2754
+ onChange(handler: () => void): () => void;
2755
+ /**
2756
+ * Resolves once `depth` reaches 0. If a write settled with an error
2757
+ * while this flush was waiting, the returned promise REJECTS with that
2758
+ * error instead — so a drain caller surfaces the failure rather than
2759
+ * hanging. Resolves immediately when already idle and error-free.
2760
+ */
2761
+ onFlush(): Promise<void>;
2762
+ }
2763
+ declare class WriteQueueTracker implements WriteQueue {
2764
+ #private;
2765
+ get pending(): boolean;
2766
+ get depth(): number;
2767
+ /** Mark one write as started. */
2768
+ begin(): void;
2769
+ /** Mark one write as finished. Pass the error if it failed. */
2770
+ settle(error?: Error): void;
2771
+ onChange(handler: () => void): () => void;
2772
+ onFlush(): Promise<void>;
2773
+ /**
2774
+ * Run `fn` as a tracked write: depth++ on entry, depth-- on settle
2775
+ * (success or failure). The fn's resolved value is returned; a thrown
2776
+ * error is re-thrown after the queue is decremented.
2777
+ */
2778
+ track<R>(fn: () => Promise<R>): Promise<R>;
2779
+ }
2780
+
2781
+ /**
2782
+ * Hub-level write lifecycle hooks. `onBeforeWrite` may abort (throw);
2783
+ * `onAfterWrite` is awaited and its errors are warned, not thrown. A
2784
+ * re-entrancy flag suppresses nested firing so a handler that writes can't
2785
+ * loop. Held on the Noydb instance, threaded into every Collection.
2786
+ */
2787
+ interface WriteEvent {
2788
+ readonly op: 'create' | 'update' | 'delete';
2789
+ readonly vault: string;
2790
+ readonly collection: string;
2791
+ readonly docId: string;
2792
+ readonly before: unknown;
2793
+ readonly after: unknown;
2794
+ readonly baseVersion: number;
2795
+ readonly version: number;
2796
+ readonly userId: string;
2797
+ readonly timestamp: number;
2798
+ readonly txId: string;
2799
+ }
2800
+ type WriteHook = (event: WriteEvent) => void | Promise<void>;
2801
+ type Unsubscribe$3 = () => void;
2802
+ declare class WriteHookRegistry {
2803
+ #private;
2804
+ /** True while handlers are running — used by the write path to skip nested firing. */
2805
+ get suppressed(): boolean;
2806
+ /** True when any hook is registered (cheap gate for the write path). */
2807
+ get hasHandlers(): boolean;
2808
+ onBeforeWrite(handler: WriteHook): Unsubscribe$3;
2809
+ onAfterWrite(handler: WriteHook): Unsubscribe$3;
2810
+ /** Run before-hooks (awaited, in order). A throw propagates and aborts the write. */
2811
+ runBefore(event: WriteEvent): Promise<void>;
2812
+ /** Run after-hooks (awaited, in order). Per-handler errors are warned, not thrown. */
2813
+ runAfter(event: WriteEvent): Promise<void>;
2814
+ }
2815
+
2816
+ /**
2817
+ * Generic per-instance **observe** bus. Observe-class
2818
+ * subsystems (devtools inspector, audit, sync-dirty notification) register
2819
+ * handlers against named lifecycle points instead of the kernel naming each
2820
+ * subsystem. Mirrors the registry pattern of {@link WriteHookRegistry} but is
2821
+ * internal and keyed by lifecycle point.
2822
+ *
2823
+ * OBSERVE SEMANTICS: handlers react to a write that already happened. A
2824
+ * handler throw is warned, not propagated — it can never abort a write. Write-
2825
+ * *gating* subsystems (guards, periods) need a throw-propagating gate bus.
2826
+ * Add observe points by extending {@link LifecycleEventMap}. Write-*gating*
2827
+ * subsystems use the sibling gate API on this same class
2828
+ * (`registerGate`/`dispatchGate`, throw-propagating); see {@link GateEventMap}.
2829
+ *
2830
+ * @module
2831
+ */
2832
+
2833
+ /** Typed map of OBSERVE lifecycle point → event payload. Extend by adding keys. */
2834
+ interface LifecycleEventMap {
2835
+ afterPut: WriteEvent;
2836
+ afterDelete: WriteEvent;
2837
+ }
2838
+ type LifecyclePoint = keyof LifecycleEventMap;
2839
+ type BusHandler<P extends LifecyclePoint> = (event: LifecycleEventMap[P]) => void | Promise<void>;
2840
+ type Unsubscribe$2 = () => void;
2841
+ /** Payload for a `beforePut` gate — carries the data guards and periods need to validate or reject a write. */
2842
+ interface GatePutEvent {
2843
+ readonly op: 'create' | 'update';
2844
+ readonly vault: string;
2845
+ readonly collection: string;
2846
+ readonly docId: string;
2847
+ /** The record about to be written (pre schema-validation). */
2848
+ readonly incoming: unknown;
2849
+ /** Decrypted prior record, or null on create / when prior is unreadable. */
2850
+ readonly existing: unknown;
2851
+ /** Prior envelope version, or 0 when none. */
2852
+ readonly existingVersion: number;
2853
+ /** Prior envelope timestamp (`_ts` ISO string), or undefined when none — periods compares against this. */
2854
+ readonly existingTs: string | undefined;
2855
+ readonly userId: string;
2856
+ readonly role: Role;
2857
+ }
2858
+ /** Payload for a `beforeDelete` gate. Like {@link GatePutEvent} without `incoming`. */
2859
+ interface GateDeleteEvent {
2860
+ readonly vault: string;
2861
+ readonly collection: string;
2862
+ readonly docId: string;
2863
+ /** True for system-internal (housekeeping) deletes — handlers branch on this. */
2864
+ readonly internal: boolean;
2865
+ readonly existing: unknown;
2866
+ readonly existingVersion: number;
2867
+ readonly existingTs: string | undefined;
2868
+ readonly userId: string;
2869
+ readonly role: Role;
2870
+ }
2871
+ /** Typed map of GATE lifecycle point → event payload. Extend by adding keys. */
2872
+ interface GateEventMap {
2873
+ beforePut: GatePutEvent;
2874
+ beforeDelete: GateDeleteEvent;
2875
+ }
2876
+ type GatePoint = keyof GateEventMap;
2877
+ type GateHandler<P extends GatePoint> = (event: GateEventMap[P]) => void | Promise<void>;
2878
+ declare class SubsystemBus {
2879
+ #private;
2880
+ /** Register a handler for an observe point. Returns an unsubscribe fn. */
2881
+ register<P extends LifecyclePoint>(point: P, handler: BusHandler<P>): Unsubscribe$2;
2882
+ /** Cheap gate for the write path — true when any handler is registered for the point. */
2883
+ hasHandlers(point: LifecyclePoint): boolean;
2884
+ /**
2885
+ * True while one or more dispatches are in flight. Backed by a depth counter
2886
+ * so that two concurrent async dispatches (`Promise.all([put('a'), put('b')])`
2887
+ * each captured `busAfterPut=true` at their respective put() tops while depth
2888
+ * was 0) both proceed independently — the counter stays > 0 until BOTH finish,
2889
+ * so any nested write attempted by a handler still sees `dispatching === true`
2890
+ * and is suppressed by the write-path gate in `collection.ts`
2891
+ * (`busAfterPut = hasHandlers('afterPut') && !dispatching`). Re-entrancy
2892
+ * suppression lives exclusively on that write-path gate; concurrent independent
2893
+ * dispatches must not drop each other's events.
2894
+ */
2895
+ get dispatching(): boolean;
2896
+ /**
2897
+ * Dispatch in registration order, awaited. Per-handler errors are warned, not
2898
+ * thrown — an observe handler must never abort a completed write. A
2899
+ * re-entrancy guard suppresses nested firing so a handler that itself writes
2900
+ * cannot loop (same rationale as WriteHookRegistry.#suppressed).
2901
+ */
2902
+ dispatch<P extends LifecyclePoint>(point: P, event: LifecycleEventMap[P]): Promise<void>;
2903
+ /** Register a write-gating handler. A throw from the handler ABORTS the write. Returns an unsubscribe fn. */
2904
+ registerGate<P extends GatePoint>(point: P, handler: GateHandler<P>): Unsubscribe$2;
2905
+ /** Cheap gate for the write path — true when any gate handler is registered for the point. */
2906
+ hasGateHandlers(point: GatePoint): boolean;
2907
+ /**
2908
+ * Run gate handlers in registration order, awaited. Unlike `dispatch`
2909
+ * (observe), a handler throw is NOT swallowed — it PROPAGATES, aborting the
2910
+ * write before it reaches the store. The first throw stops the remaining
2911
+ * handlers (fail-fast). This is the seam guards/periods migrate onto.
2912
+ *
2913
+ * Note: gate handlers are validators that read, not write. A gate handler
2914
+ * that writes back into the same collection would re-enter the write path
2915
+ * and re-dispatch this point; loop-suppression for that case is deferred to
2916
+ * the migration slice (contract: gate handlers must not perform writes that
2917
+ * re-trigger their own point).
2918
+ */
2919
+ dispatchGate<P extends GatePoint>(point: P, event: GateEventMap[P]): Promise<void>;
2920
+ }
2921
+
2922
+ /**
2923
+ * Schema-update strategy framework types (M12 §3a).
2924
+ *
2925
+ * The hub core detects a schema change (SchemaDelta) and dispatches it
2926
+ * through a collection's ordered strategy list. Strategies decide what
2927
+ * happens; the core only knows this interface.
2928
+ */
2929
+ /** A single changed top-level property in a schema delta. */
2930
+ interface FieldChange {
2931
+ readonly field: string;
2932
+ /** True when the field's required-ness flipped. */
2933
+ readonly requiredChanged: boolean;
2934
+ /** True when the field's subschema shape changed. */
2935
+ readonly shapeChanged: boolean;
2936
+ }
2937
+ /** The classified difference between a stored and a freshly-derived schema. */
2938
+ interface SchemaDelta {
2939
+ readonly collection: string;
2940
+ readonly kind: 'none' | 'additive' | 'non-additive';
2941
+ /** Top-level properties present in the new schema but not the old. */
2942
+ readonly added: readonly string[];
2943
+ /** Top-level properties present in the old schema but not the new. */
2944
+ readonly removed: readonly string[];
2945
+ /** Top-level properties present in both but altered. */
2946
+ readonly changed: readonly FieldChange[];
2947
+ }
2948
+ /** Context handed to a strategy alongside the delta. */
2949
+ interface UpdateContext {
2950
+ readonly collection: string;
2951
+ }
2952
+ /** Bulk transform run by the coordinatedCutover strategy. */
2953
+ type TransformFn = (doc: Record<string, unknown>) => Record<string, unknown>;
2954
+ /**
2955
+ * A strategy's verdict on a detected schema change.
2956
+ * - `allow` — no objection; the dispatcher falls through to the next strategy.
2957
+ * - `reject` — terminal: refuse the change; `error` is thrown at the write path.
2958
+ * - `cutover` — terminal: run a coordinated drain-barrier (handled by coordinatedCutover).
2959
+ * New terminal actions may be added without breaking existing strategies.
2960
+ */
2961
+ type UpdateDecision = {
2962
+ readonly action: 'allow';
2963
+ } | {
2964
+ readonly action: 'reject';
2965
+ readonly error: Error;
2966
+ } | {
2967
+ readonly action: 'cutover';
2968
+ readonly transform: TransformFn;
2969
+ };
2970
+ /** A pluggable schema-evolution policy. */
2971
+ interface SchemaUpdateStrategy {
2972
+ readonly name: string;
2973
+ onSchemaDelta(delta: SchemaDelta, ctx: UpdateContext): UpdateDecision | Promise<UpdateDecision>;
2974
+ }
2975
+
2976
+ /**
2977
+ * Per-collection write gate. Holds the (async) update decision
2978
+ * computed at registration; `Collection.put`/`delete` await it before
2979
+ * writing and throw the strategy's rejection error.
2980
+ *
2981
+ * Detection FAILURE (the promise rejecting) is deliberately NOT a write
2982
+ * block — schema detection is a fingerprint safety net, not a correctness
2983
+ * invariant (matches how persisted-schema write failures are swallowed).
2984
+ * Only an explicit `reject` decision blocks writes.
2985
+ */
2986
+
2987
+ declare class SchemaUpdateGate {
2988
+ #private;
2989
+ constructor(decision: Promise<UpdateDecision>);
2990
+ assertWritable(): Promise<void>;
2991
+ }
2992
+
2993
+ /**
2994
+ * Schema-fence document. Vault-level generation counter + drain
2995
+ * state, stored at `_meta/schema-fence` using the plaintext-envelope
2996
+ * pattern of `_meta/policy` (no PII — a counter + a state enum).
2997
+ */
2998
+
2999
+ type FenceState = 'normal' | 'draining' | 'migrating' | 'complete';
3000
+ interface FenceDoc {
3001
+ readonly currentSchemaVersion: number;
3002
+ readonly fenceState: FenceState;
3003
+ }
3004
+
3005
+ /**
3006
+ * Vault-level schema-fence controller.
3007
+ *
3008
+ * Owns the open-time generation snapshot, the pending-cutover registry,
3009
+ * and the cutover orchestration. 3a: single-client (the caller is the
3010
+ * migrator). 3b: a cooperative ack-barrier — after `draining`, the
3011
+ * migrator waits for the active client set (registry heartbeats) to ack
3012
+ * the draining generation before transforming. No leader election.
3013
+ */
3014
+
3015
+ /** Runs one collection's transform; supplied by the Vault (binds to a Collection). */
3016
+ type RunTransform = (collection: string, transform: TransformFn) => Promise<void>;
3017
+ declare class SchemaFenceController {
3018
+ #private;
3019
+ constructor(opts: {
3020
+ store: NoydbStore;
3021
+ vault: string;
3022
+ onFlush: () => Promise<void>;
3023
+ clientId?: string;
3024
+ now?: () => number;
3025
+ staleMs?: number;
3026
+ quiesceTimeoutMs?: number;
3027
+ emit?: (e: {
3028
+ currentSchemaVersion: number;
3029
+ fenceState: FenceState;
3030
+ }) => void;
3031
+ });
3032
+ /** Capture the generation snapshot at vault-open. */
3033
+ init(): Promise<void>;
3034
+ /** Record a per-collection pending cutover (from a registration `cutover` decision). */
3035
+ registerPendingCutover(collection: string, transform: TransformFn): void;
3036
+ /** Write-path gate. Throws when behind, fenced, or this collection is cutover-pending. */
3037
+ assertWritable(collection: string): Promise<void>;
3038
+ /**
3039
+ * Admin trigger. Drain → wait for the active set to quiesce (or time out)
3040
+ * → migrate each pending transform → bump → complete → normal. The
3041
+ * migrator excludes itself from the barrier (it drained synchronously
3042
+ * here). `onPoll` (tests) advances other clients between barrier checks;
3043
+ * production falls back to a short real delay.
3044
+ */
3045
+ runCutover(run: RunTransform, opts?: {
3046
+ onPoll?: () => Promise<void>;
3047
+ }): Promise<{
3048
+ migrated: number;
3049
+ }>;
3050
+ /** Recover a stuck drain: reset fenceState to normal at the current version (no bump). */
3051
+ abort(): Promise<void>;
3052
+ }
3053
+
2544
3054
  /**
2545
3055
  * Zero-dependency JSON diff.
2546
3056
  * Produces a flat list of changes between two plain objects.
@@ -3091,7 +3601,7 @@ declare class SyncEngine {
3091
3601
  }
3092
3602
 
3093
3603
  /**
3094
- * **Wrap-DEKs primitive (#44)** — a single canonical shape for the
3604
+ * **Wrap-DEKs primitive** — a single canonical shape for the
3095
3605
  * pattern of "serialize a DEK set, encrypt it under a credential-derived
3096
3606
  * AES-GCM key." Used by:
3097
3607
  *
@@ -3109,7 +3619,7 @@ declare class SyncEngine {
3109
3619
  * `PIN_PBKDF2_ITERATIONS` and the threat-model rationale in its
3110
3620
  * module docstring.
3111
3621
  *
3112
- * Before #44, the same crypto lived in two places: `mintPaperRecoveryEntry`
3622
+ * Previously, the same crypto lived in two places: `mintPaperRecoveryEntry`
3113
3623
  * (in `team/recovery.ts`) and `enrollPasswordAuthenticator` (in
3114
3624
  * `@noy-db/on-password`). Both functions did identical work — PBKDF2
3115
3625
  * the credential, AES-GCM-encrypt the JSON-serialized DEK set — but
@@ -3136,7 +3646,7 @@ declare class SyncEngine {
3136
3646
  * Composition: `PaperRecoveryEntry extends WrappedDeksBlob` plus
3137
3647
  * `{ codeId, enrolledAt }`. `KeyringAuthenticatorWrappingDEKs`
3138
3648
  * carries the same three fields with `salt` stored in `meta` for
3139
- * slot-format back-compat (#44 defers moving it to top-level).
3649
+ * slot-format back-compat (defers moving it to top-level).
3140
3650
  */
3141
3651
  interface WrappedDeksBlob {
3142
3652
  /** Base64 PBKDF2 salt for the credential-derived wrapping key. */
@@ -3193,9 +3703,9 @@ interface ShamirRecoveryProvider {
3193
3703
  }
3194
3704
 
3195
3705
  /**
3196
- * Recovery profile persistence + dispatch — issue #10.
3706
+ * Recovery profile persistence + dispatch.
3197
3707
  *
3198
- * v0.1.0-pre.5 wires the **paper** profile end-to-end through
3708
+ * Wires the **paper** profile end-to-end through
3199
3709
  * `@noy-db/on-recovery`. The other three profiles (Shamir,
3200
3710
  * multi-channel, admin-mediated) ship the API surface and throw
3201
3711
  * {@link RecoveryProfileNotImplementedError} during use; per-profile
@@ -3232,7 +3742,7 @@ interface ShamirRecoveryProvider {
3232
3742
  * PBKDF2-derived key), and it sidesteps the non-extractable-KEK
3233
3743
  * constraint cleanly.
3234
3744
  *
3235
- * Type-level composition (#44): `PaperRecoveryEntry extends
3745
+ * Type-level composition: `PaperRecoveryEntry extends
3236
3746
  * WrappedDeksBlob` — the three crypto fields (`salt`, `iv`,
3237
3747
  * `wrappedDeks`) come from the shared primitive; `codeId` and
3238
3748
  * `enrolledAt` are paper-recovery's own metadata. Wire format
@@ -3346,7 +3856,7 @@ declare function unwrapDeksFromShamirEntry(provider: ShamirRecoveryProvider, ent
3346
3856
  * {@link savePaperRecoveryEntries}). The recovery flow unwraps the
3347
3857
  * DEK set, then mints a fresh KEK from the user's new passphrase.
3348
3858
  *
3349
- * Thin wrapper over {@link mintWrappedDeksBlob} (#44) — the crypto
3859
+ * Thin wrapper over {@link mintWrappedDeksBlob} — the crypto
3350
3860
  * lives in the shared primitive; this function just adds paper-
3351
3861
  * recovery's own metadata (`codeId`, `enrolledAt`).
3352
3862
  *
@@ -3361,14 +3871,14 @@ declare function mintPaperRecoveryEntry(deks: Map<string, CryptoKey>, code: stri
3361
3871
  * Decrypt a recovery entry to recover the raw DEK set. Used by the
3362
3872
  * `recoverPassphrase` flow after the user's code has been parsed.
3363
3873
  *
3364
- * Thin wrapper over {@link unwrapDeksFromBlob} (#44).
3874
+ * Thin wrapper over {@link unwrapDeksFromBlob}.
3365
3875
  *
3366
3876
  * @throws when the code does not match the entry (AES-GCM auth tag fail).
3367
3877
  */
3368
3878
  declare function unwrapDeksFromPaperEntry(entry: PaperRecoveryEntry, code: string): Promise<Map<string, CryptoKey>>;
3369
3879
 
3370
3880
  /**
3371
- * Tier-2 authenticator slot management — issue #11.
3881
+ * Tier-2 authenticator slot management.
3372
3882
  *
3373
3883
  * Each slot independently wraps the SAME KEK under a method-specific
3374
3884
  * derived key (LUKS pattern). Enrolling adds a slot; removing drops
@@ -3418,15 +3928,14 @@ type EnrollAuthenticatorOptions = EnrollAuthenticatorWrappingKEKOptions | Enroll
3418
3928
  */
3419
3929
  declare function enrollAuthenticator(store: NoydbStore, vault: string, keyring: UnlockedKeyring, options: EnrollAuthenticatorOptions): Promise<UnlockedKeyring>;
3420
3930
  /**
3421
- * Caller payload for {@link updateAuthenticator} (#55). Mutates only
3931
+ * Caller payload for {@link updateAuthenticator}. Mutates only
3422
3932
  * `meta` — the slot's id, method, and wrap material are immutable
3423
3933
  * through this primitive, preserving the anti-slot-swap guard.
3424
3934
  *
3425
3935
  * `meta` is **merged** at the top level: keys absent from the patch
3426
3936
  * are preserved, keys present overwrite. To clear a meta key, pass
3427
- * `null` for that key explicitly. (Same semantics as #57's
3428
- * `UserApi.updateMe`, scoped to this top-level merge no recursion
3429
- * into nested meta values.)
3937
+ * `null` for that key explicitly. (Same top-level merge semantics as
3938
+ * `UserApi.updateMe`, non-recursive meta is a flat label bag.)
3430
3939
  */
3431
3940
  interface UpdateAuthenticatorOptions {
3432
3941
  readonly meta?: Record<string, unknown>;
@@ -3448,7 +3957,6 @@ interface UpdateAuthenticatorOptions {
3448
3957
  * @throws `NoAccessError` when no slot with the given id exists.
3449
3958
  * @throws `ValidationError` when no patch field is provided.
3450
3959
  *
3451
- * @see #55
3452
3960
  */
3453
3961
  declare function updateAuthenticator(store: NoydbStore, vault: string, keyring: UnlockedKeyring, slotId: string, options: UpdateAuthenticatorOptions): Promise<UnlockedKeyring>;
3454
3962
  /**
@@ -3465,7 +3973,7 @@ declare function findAuthenticator(keyring: UnlockedKeyring, slotId: string): Ke
3465
3973
 
3466
3974
  /**
3467
3975
  * Tier-1 change flows — `rotatePassphrase` (user remembers old) and
3468
- * `recoverPassphrase` (user supplies a recovery proof). Issue #10.
3976
+ * `recoverPassphrase` (user supplies a recovery proof).
3469
3977
  *
3470
3978
  * The two flows share the post-verification half — fresh salt, fresh
3471
3979
  * KEK, rewrap every DEK — and differ only in how they re-derive the
@@ -3531,10 +4039,9 @@ interface RotatePassphraseInput {
3531
4039
  * Map of slot id → re-enrolment ceremony. Slots whose id appears
3532
4040
  * here are PRESERVED across rotation (the ceremony re-derives the
3533
4041
  * method-specific wrapping under the new keyring); slots whose id
3534
- * is absent are DROPPED (the pre-#29 behavior).
4042
+ * is absent are DROPPED (the pre-slot-ceremony behavior).
3535
4043
  *
3536
- * Without this map, `rotatePassphrase` retains the pre-pre.8
3537
- * behavior of wiping every tier-2 slot. Consumers building a
4044
+ * Without this map, `rotatePassphrase` wipes every tier-2 slot. Consumers building a
3538
4045
  * "rotate without losing my biometric" flow supply ceremonies for
3539
4046
  * each slot they want to keep.
3540
4047
  *
@@ -3542,7 +4049,7 @@ interface RotatePassphraseInput {
3542
4049
  * state. Callers wrap individual ceremonies in try/catch + return
3543
4050
  * a sentinel if they want graceful degradation per slot.
3544
4051
  *
3545
- * Added in pre.8 (#29).
4052
+ * Added when slot-ceremony rewrapping landed.
3546
4053
  */
3547
4054
  readonly slotCeremonies?: {
3548
4055
  readonly [slotId: string]: SlotRewrapCeremony;
@@ -3553,10 +4060,10 @@ interface RotatePassphraseInput {
3553
4060
  * under a freshly-derived KEK from `newPassphrase`, and persist.
3554
4061
  *
3555
4062
  * Tier-2 authenticator slots are dropped UNLESS the caller supplies
3556
- * a `slotCeremonies` map (#29) — each ceremony re-derives its
4063
+ * a `slotCeremonies` map — each ceremony re-derives its
3557
4064
  * method-specific wrapping under the new keyring, and hub persists
3558
4065
  * the rewrapped slots atomically with the rotation. Slots whose id
3559
- * isn't in the map are still dropped (pre-pre.8 behavior).
4066
+ * isn't in the map are still dropped.
3560
4067
  *
3561
4068
  * @throws `InvalidKeyError` if `oldPassphrase` does not unwrap the keyring.
3562
4069
  * @throws `WeakPassphraseError` if `newPassphrase` fails the strength rule.
@@ -3567,7 +4074,7 @@ declare function rotatePassphrase(store: NoydbStore, vault: string, userId: stri
3567
4074
  /**
3568
4075
  * Caller payload for {@link recoverPassphrase}.
3569
4076
  *
3570
- * As of #196 slice 1, `paper` and `shamir` are wired end-to-end.
4077
+ * `paper` and `shamir` are wired end-to-end.
3571
4078
  * The remaining two profiles (`multi-channel`, `admin-mediated`)
3572
4079
  * stay outside the union and throw
3573
4080
  * {@link RecoveryProfileNotImplementedError} at the runtime guard
@@ -3597,7 +4104,7 @@ interface RecoverPassphraseInput {
3597
4104
  * After a successful paper-recovery, replace ALL remaining recovery
3598
4105
  * entries with freshly-minted ones. Defaults to `true` (defensive).
3599
4106
  *
3600
- * Rationale (issue #36): the user just demonstrated they had access
4107
+ * Rationale: the user just demonstrated they had access
3601
4108
  * to AT LEAST one code. The remaining codes from the same printed
3602
4109
  * sheet may also be compromised — photographed, leaked via a
3603
4110
  * screen-share slip, or in the hands of whoever stole the sheet.
@@ -3647,7 +4154,7 @@ interface RecoverPassphraseResult {
3647
4154
  readonly newCodes: readonly string[];
3648
4155
  }
3649
4156
  /**
3650
- * Input for {@link Noydb.rotateRecovery} (#121) — deliberate
4157
+ * Input for {@link Noydb.rotateRecovery} — deliberate
3651
4158
  * recovery-credential regeneration when the user knows their
3652
4159
  * passphrase but wants a fresh sheet (paper) or fresh shares
3653
4160
  * (shamir). Symmetric to {@link RotatePassphraseInput}.
@@ -3698,7 +4205,7 @@ interface EnrollRecoveryResult {
3698
4205
  }
3699
4206
  /**
3700
4207
  * Input shape for {@link Noydb.enrollRecovery} and
3701
- * {@link Noydb.openVaultAndEnrollRecovery} (#195). Discriminated
4208
+ * {@link Noydb.openVaultAndEnrollRecovery}. Discriminated
3702
4209
  * union over recovery profiles.
3703
4210
  *
3704
4211
  * - `paper`: caller pre-mints entries (typically via
@@ -3724,9 +4231,8 @@ type RecoveryEnrollmentInput = {
3724
4231
  readonly entryId?: string;
3725
4232
  };
3726
4233
  /**
3727
- * Reset the user's passphrase using a recovery proof. v0.1.0-pre.5
3728
- * supports the `'paper'` profile via `@noy-db/on-recovery` entries
3729
- * persisted in `_meta/recovery-paper`. The other three profiles throw
4234
+ * Reset the user's passphrase using a recovery proof.
4235
+ * Supports `'paper'` and `'shamir'` profiles. The other profiles throw
3730
4236
  * {@link RecoveryProfileNotImplementedError}.
3731
4237
  *
3732
4238
  * On success, the used recovery entry is burned (deleted from the
@@ -3735,7 +4241,7 @@ type RecoveryEnrollmentInput = {
3735
4241
  declare function recoverPassphrase(provider: ShamirRecoveryProvider | undefined, store: NoydbStore, vault: string, userId: string, input: RecoverPassphraseInput): Promise<UnlockedKeyring>;
3736
4242
 
3737
4243
  /**
3738
- * Atomic peer-recovery primitive — issues #33 + #34.
4244
+ * Atomic peer-recovery primitive.
3739
4245
  *
3740
4246
  * `recoverUser` is a SEPARATE operation from `revoke + grant`. It
3741
4247
  * exists because peer-recovery has different semantics than account
@@ -3764,7 +4270,7 @@ declare function recoverPassphrase(provider: ShamirRecoveryProvider | undefined,
3764
4270
  *
3765
4271
  * Caller must be at least as privileged as the target. The hub
3766
4272
  * `db.recoverUser` method gates this with the `peer-recover-user`
3767
- * policy gate (#33's factor-proof requirement); the function below
4273
+ * policy gate (the `peer-recover-user` factor-proof requirement); the function below
3768
4274
  * enforces only the role + anti-privilege-escalation invariants.
3769
4275
  *
3770
4276
  * @module
@@ -3920,7 +4426,64 @@ declare function validatePublicEnvelopeInput(input: SetPublicEnvelopeInput, sche
3920
4426
  declare function isPublicEnvelope(x: unknown): x is PublicEnvelope;
3921
4427
 
3922
4428
  /**
3923
- * Per-vault tier-3 (PIN / quick-resume) state — issue #11.
4429
+ * Multi-tab coordination: primary/secondary election (Web Locks)
4430
+ * + presence heartbeat (BroadcastChannel). Browser-only; opt-in; no-op
4431
+ * when the APIs are absent. The lock/channel interfaces are hub-local
4432
+ * (structurally compatible with @noy-db/by-peer + @noy-db/by-tabs, but
4433
+ * not imported — those packages depend on hub).
4434
+ */
4435
+ type TabRole = 'primary' | 'secondary' | 'unknown';
4436
+ interface TabPresence {
4437
+ readonly tabId: string;
4438
+ readonly lastSeen: number;
4439
+ readonly role: TabRole;
4440
+ }
4441
+ type Unsubscribe$1 = () => void;
4442
+ /** Structural subset of the Web Locks API / by-peer's MinimalLockManager. */
4443
+ interface TabLockManager {
4444
+ request<T>(name: string, options: {
4445
+ mode?: 'exclusive' | 'shared';
4446
+ signal?: AbortSignal;
4447
+ }, callback: (lock: unknown) => Promise<T>): Promise<T>;
4448
+ }
4449
+ /** Structural subset of by-peer's PeerChannel / a BroadcastChannel wrapper. */
4450
+ interface TabChannel {
4451
+ send(payload: string): void;
4452
+ on(event: 'message', listener: (payload: string) => void): Unsubscribe$1;
4453
+ on(event: 'close', listener: () => void): Unsubscribe$1;
4454
+ close(): void;
4455
+ readonly isOpen: boolean;
4456
+ }
4457
+ interface TabCoordinationOptions {
4458
+ readonly lockManager?: TabLockManager;
4459
+ readonly channel?: TabChannel;
4460
+ readonly tabId?: string;
4461
+ readonly lockName?: string;
4462
+ readonly heartbeatMs?: number;
4463
+ readonly staleMs?: number;
4464
+ readonly now?: () => number;
4465
+ /**
4466
+ * Close the channel on `dispose()`. Set this only for a channel the
4467
+ * coordinator owns (e.g. the one `defaultChannel()` created); leave it
4468
+ * false for a caller-injected channel so the coordinator never closes a
4469
+ * channel it didn't create. Default: false.
4470
+ */
4471
+ readonly closeChannelOnDispose?: boolean;
4472
+ /**
4473
+ * Also propagate committed writes to other tabs. Default true:
4474
+ * when tab coordination is enabled and a channel is available, a write in
4475
+ * one tab refreshes that document in every other tab. Set false to opt out.
4476
+ */
4477
+ readonly propagateWrites?: boolean;
4478
+ /**
4479
+ * Channel for write propagation — distinct from the presence
4480
+ * channel. Default: an inline BroadcastChannel on `noydb:tab-writes`.
4481
+ */
4482
+ readonly writeChannel?: TabChannel;
4483
+ }
4484
+
4485
+ /**
4486
+ * Per-vault tier-3 (PIN / quick-resume) state.
3924
4487
  *
3925
4488
  * The hub holds a `PinResumeState`-shaped record in memory, keyed by
3926
4489
  * vault. `enrollUnlock` populates it; `unlockViaPin` consumes it via
@@ -3967,6 +4530,69 @@ declare class QuickUnlockStore {
3967
4530
  private clearTimer;
3968
4531
  }
3969
4532
 
4533
+ /**
4534
+ * Cadence policy for automatic snapshots. Borrows the vocabulary of the sync
4535
+ * `SyncPolicy` (debounce / interval / minInterval / onUnload) but is a separate,
4536
+ * snapshot-specific shape — automatic snapshots write the single rolling
4537
+ * `<vault>__auto` key, never the immutable on-demand pool.
4538
+ *
4539
+ * Default mode is `'manual'`: no timers, snapshots stay on-demand.
4540
+ */
4541
+ type SnapshotMode = 'manual' | 'debounce' | 'interval';
4542
+ interface SnapshotPolicy {
4543
+ /** Trigger mode. Default `'manual'` — no automatic snapshots. */
4544
+ readonly mode?: SnapshotMode;
4545
+ /** Idle delay (ms) after a write before an auto-snapshot fires. `mode:'debounce'`. Default 30_000. */
4546
+ readonly debounceMs?: number;
4547
+ /** Fixed interval (ms). `mode:'interval'`. Default 300_000. */
4548
+ readonly intervalMs?: number;
4549
+ /** Hard floor (ms) between auto-snapshots regardless of mode. Default 0. */
4550
+ readonly minIntervalMs?: number;
4551
+ /** Flush a pending auto-snapshot on tab-hide / process exit. Default true for non-manual modes. */
4552
+ readonly onUnload?: boolean;
4553
+ /** Label applied to each auto-snapshot. Default `'auto'`. */
4554
+ readonly label?: string;
4555
+ }
4556
+
4557
+ interface SnapshotMeta {
4558
+ readonly version: string;
4559
+ readonly label?: string;
4560
+ readonly note?: string;
4561
+ readonly exportedAt: string;
4562
+ readonly exportedBy: string;
4563
+ readonly size: number;
4564
+ /**
4565
+ * `'verified'` — bundle was produced by `writeNoydbBundle(vault, {})`, which embeds
4566
+ * ledgerHead metadata; `vault.load()` runs `verifyBackupIntegrity()` on restore.
4567
+ * `'legacy-unverifiable'` — reserved for v2 import paths that read pre-existing bundles
4568
+ * lacking a ledgerHead; not produced by the current engine.
4569
+ */
4570
+ readonly integrity: 'verified' | 'legacy-unverifiable';
4571
+ /** `true` for the rolling auto-snapshot; absent on on-demand checkpoints. */
4572
+ readonly auto?: true;
4573
+ }
4574
+ interface RetentionPolicy {
4575
+ readonly keepLast?: number;
4576
+ readonly maxAgeDays?: number;
4577
+ readonly prune?: boolean;
4578
+ }
4579
+ /** @internal */
4580
+ interface SnapshotStrategy {
4581
+ snapshot(vault: unknown, by: string, opts?: {
4582
+ label?: string;
4583
+ note?: string;
4584
+ }): Promise<SnapshotMeta>;
4585
+ listSnapshots(vaultId: string): Promise<SnapshotMeta[]>;
4586
+ restoreSnapshot(vault: unknown, version: string): Promise<void>;
4587
+ /** Rolling auto-snapshot to the fixed `<vault>__auto` key. */
4588
+ autoSnapshot(vault: unknown, by: string, opts?: {
4589
+ label?: string;
4590
+ note?: string;
4591
+ }): Promise<SnapshotMeta>;
4592
+ /** Configured cadence policy. Undefined or `mode:'manual'` ⇒ no scheduler is wired. */
4593
+ readonly policy?: SnapshotPolicy;
4594
+ }
4595
+
3970
4596
  /**
3971
4597
  * Multi-record atomic transactions.
3972
4598
  *
@@ -4036,7 +4662,7 @@ interface StagedOp {
4036
4662
  expectedVersion?: number;
4037
4663
  /**
4038
4664
  * Optional human-readable tag forwarded to the resulting ledger
4039
- * entry's `reason` field (#1). Set by callers via
4665
+ * entry's `reason` field. Set by callers via
4040
4666
  * `tx.vault(v).collection(c).put(id, record, { reason })`.
4041
4667
  */
4042
4668
  reason?: string;
@@ -4068,6 +4694,8 @@ interface AmendmentTxOptions {
4068
4694
  * facade; its `put`/`delete`/`get` calls stage ops against the tx.
4069
4695
  */
4070
4696
  declare class TxContext {
4697
+ /** Stable id for this transaction; shared by all writes it performs. */
4698
+ readonly txId: string;
4071
4699
  /** @internal */
4072
4700
  readonly _ops: StagedOp[];
4073
4701
  /**
@@ -4076,7 +4704,7 @@ declare class TxContext {
4076
4704
  * restore prior state via `revertExecuted`. Side-effect writes (e.g.
4077
4705
  * recursive derivation outputs fired inside `Collection.put`) are
4078
4706
  * appended here in execution order so they roll back alongside the
4079
- * main staged ops (#133).
4707
+ * main staged ops.
4080
4708
  */
4081
4709
  readonly _executed: ExecutedOp[];
4082
4710
  /** @internal */
@@ -4148,12 +4776,40 @@ declare class TxCollection<T> {
4148
4776
  * in `noydb.ts`. `Collection.putManyAtomic` runs its own Phase 2 loop
4149
4777
  * but shares the `_activeTxContext` mechanism (and the `revertExecuted`
4150
4778
  * helper) so nested side-effect derivation writes get registered for
4151
- * revert alongside the bulk-put source ops (#133).
4779
+ * revert alongside the bulk-put source ops.
4152
4780
  */
4153
4781
  declare function runTransaction<T>(db: Noydb, fn: (tx: TxContext) => Promise<T> | T, options?: AmendmentTxOptions): Promise<T>;
4154
4782
 
4155
4783
  /**
4156
- * Policy gate DSL types issue #9.
4784
+ * Dry-run transactions. Runs the tx body to STAGE ops, then builds
4785
+ * the directly-affected diff (before = current committed via collection.get,
4786
+ * after = staged record) and collects guard violations — without executing
4787
+ * phase 2. No adapter writes, no write-hooks, no commit. MV/derivation
4788
+ * cascade is NOT simulated (v2). Mirrors the guard loop in
4789
+ * `Collection.putInternal` — keep the two in sync.
4790
+ */
4791
+
4792
+ interface AffectedDocument {
4793
+ readonly vault: string;
4794
+ readonly op: 'create' | 'update' | 'delete';
4795
+ readonly collection: string;
4796
+ readonly docId: string;
4797
+ readonly before: unknown;
4798
+ readonly after: unknown;
4799
+ }
4800
+ interface GuardViolation {
4801
+ readonly vault: string;
4802
+ readonly collection: string;
4803
+ readonly docId: string;
4804
+ readonly message: string;
4805
+ }
4806
+ interface DryRunResult {
4807
+ readonly affected: ReadonlyArray<AffectedDocument>;
4808
+ readonly guardViolations: ReadonlyArray<GuardViolation>;
4809
+ }
4810
+
4811
+ /**
4812
+ * Policy gate DSL types.
4157
4813
  *
4158
4814
  * Sensitive operations (rotate the passphrase, enroll an authenticator,
4159
4815
  * export plaintext, grant a user, …) are gated by a typed policy
@@ -4187,12 +4843,10 @@ declare function runTransaction<T>(db: Noydb, fn: (tx: TxContext) => Promise<T>
4187
4843
  * devices — policies can require ANY of them or insist on a count of 2
4188
4844
  * to force a mix.
4189
4845
  *
4190
- * Added in pre.8 (#30): `webauthn-platform`, `password`, `pin` —
4191
- * previously consumers with no off-device infrastructure (no TOTP,
4192
- * no email-OTP, paper recovery not enrolled) had to disable the
4193
- * factor requirement entirely on `rotate-passphrase`. Now they can
4194
- * pin "any second factor I have wired" without losing the freshness
4195
- * guarantee.
4846
+ * `webauthn-platform`, `password`, `pin` — for consumers with no
4847
+ * off-device infrastructure (no TOTP, no email-OTP, paper recovery not
4848
+ * enrolled) who want to require "any second factor I have wired"
4849
+ * without losing the freshness guarantee.
4196
4850
  */
4197
4851
  type FactorKind = 'totp' | 'email-otp' | 'recovery' | 'shamir' | 'webauthn-roaming' | 'webauthn-platform' | 'password' | 'pin';
4198
4852
  /**
@@ -4236,7 +4890,7 @@ interface GatePolicy {
4236
4890
  type BuiltInGateName = 'rotate-passphrase' | 'recover-passphrase' | 'enroll-authenticator' | 'remove-authenticator'
4237
4891
  /**
4238
4892
  * Authorize a deliberate paper-recovery-code regeneration —
4239
- * `db.rotateRecovery` (#121). Symmetric to `rotate-passphrase` for
4893
+ * `db.rotateRecovery`. Symmetric to `rotate-passphrase` for
4240
4894
  * the case where the user remembers their passphrase but wants a
4241
4895
  * fresh sheet (lost the printout, suspect compromise of the off-site
4242
4896
  * copy). PERSONAL allows tier-1; STRICT requires an off-device
@@ -4246,19 +4900,19 @@ type BuiltInGateName = 'rotate-passphrase' | 'recover-passphrase' | 'enroll-auth
4246
4900
  | 'rotate-recovery'
4247
4901
  /**
4248
4902
  * Authorize a meta-only mutation on an existing authenticator slot —
4249
- * `db.updateAuthenticator` (#55). The slot's wrap material, id, and
4903
+ * `db.updateAuthenticator`. The slot's wrap material, id, and
4250
4904
  * method are immutable through this gate; only the `meta` blob
4251
4905
  * (nicknames, method-specific labels) can change. Anti-slot-swap
4252
4906
  * guard is preserved structurally regardless of this gate's
4253
4907
  * settings.
4254
4908
  */
4255
4909
  | 'update-authenticator' | 'rotate-unlock' | 'enroll-user' | 'revoke-user' | 'export-bundle' | 'export-plaintext' | 'view-user-auth'
4256
- /** Authorize a write to one's own user envelope (#22). */
4910
+ /** Authorize a write to one's own user envelope. */
4257
4911
  | 'edit-own-profile'
4258
- /** Authorize reading other principals' user envelopes (#22). */
4912
+ /** Authorize reading other principals' user envelopes. */
4259
4913
  | 'view-team-profiles'
4260
4914
  /**
4261
- * Authorize an atomic peer-recovery — `db.recoverUser` (#33, #34).
4915
+ * Authorize an atomic peer-recovery — `db.recoverUser`.
4262
4916
  * Distinct from `revoke-user` because peer-recovery is intentional
4263
4917
  * re-issuance of someone's keyring under a temp passphrase, NOT
4264
4918
  * removal. Allows owner→owner natively (matches the threat model:
@@ -4268,7 +4922,7 @@ type BuiltInGateName = 'rotate-passphrase' | 'recover-passphrase' | 'enroll-auth
4268
4922
  */
4269
4923
  | 'peer-recover-user'
4270
4924
  /**
4271
- * Authorize a post-grant identity mutation — `db.updateUser` (#54).
4925
+ * Authorize a post-grant identity mutation — `db.updateUser`.
4272
4926
  * Covers `role`, `displayName`, `permissions` changes on an existing
4273
4927
  * keyring. Pure plaintext-header rewrite — no DEKs touched, no KEK
4274
4928
  * required. The role-elevation guard inside the implementation
@@ -4281,7 +4935,7 @@ type GateName = BuiltInGateName | `app:${string}`;
4281
4935
  /**
4282
4936
  * Top-level policy object. Persisted at `_meta/policy` once at vault
4283
4937
  * creation. The `passphrase` block configures the strength rules
4284
- * applied at every passphrase ingress (issue #7); `gates` configures
4938
+ * applied at every passphrase ingress; `gates` configures
4285
4939
  * the action-level requirements.
4286
4940
  */
4287
4941
  interface VaultPolicy {
@@ -4305,7 +4959,7 @@ interface FactorProof {
4305
4959
  * `db.recoverUser`, `db.enrollUnlock`, `db.describeUserAuth`,
4306
4960
  * `db.describeAllUsersAuth`.
4307
4961
  *
4308
- * Pre-#89 this type was inlined at every call site as
4962
+ * Previously this type was inlined at every call site as
4309
4963
  * `{ factors?: ReadonlyArray<FactorProof>; sharedDevice?: boolean }`
4310
4964
  * and parameter names alternated between `factors` and `presented`.
4311
4965
  * Now exported so consumers can name their helpers and so the param
@@ -4320,14 +4974,19 @@ type ActiveTier = 1 | 2 | 3;
4320
4974
 
4321
4975
  /** The top-level NOYDB instance. */
4322
4976
  declare class Noydb {
4977
+ #private;
4323
4978
  private readonly options;
4324
4979
  private readonly emitter;
4980
+ private readonly writeQueueTracker;
4981
+ private readonly writeHooks;
4982
+ private readonly subsystemBus;
4983
+ private readonly clientId;
4325
4984
  private readonly vaultCache;
4326
4985
  private readonly keyringCache;
4327
4986
  private readonly syncEngines;
4328
4987
  /**
4329
4988
  * Per-vault active session tier — defaults to `1` after a passphrase
4330
- * unlock; tier-2 / tier-3 unlocks (issue #11) downgrade it. Used by
4989
+ * unlock; tier-2 / tier-3 unlocks downgrade it. Used by
4331
4990
  * {@link checkGate} to evaluate `gate.minTier`.
4332
4991
  */
4333
4992
  private readonly activeTier;
@@ -4337,14 +4996,14 @@ declare class Noydb {
4337
4996
  */
4338
4997
  private readonly policyCache;
4339
4998
  /**
4340
- * One-shot bypass for the managed-mode strong-recovery check (#195).
4999
+ * One-shot bypass for the managed-mode strong-recovery check.
4341
5000
  * Set true by {@link openVaultAndEnrollRecovery} for the duration of
4342
5001
  * the bootstrap window so the keyring can be created before the
4343
5002
  * strong recovery is enrolled. Always cleared (try/finally).
4344
5003
  * @internal
4345
5004
  */
4346
5005
  private _skipNextManagedRecoveryCheck;
4347
- /** Per-vault tier-3 (PIN / quick-resume) state — issue #11. */
5006
+ /** Per-vault tier-3 (PIN / quick-resume) state. */
4348
5007
  private readonly quickUnlock;
4349
5008
  /**
4350
5009
  * Resolved public-envelope schema. Lazily computed once from
@@ -4354,19 +5013,26 @@ declare class Noydb {
4354
5013
  private readonly publicEnvelopeSchema;
4355
5014
  private closed;
4356
5015
  private sessionTimer;
5016
+ /** Same-device multi-tab coordinator; created on `enableTabCoordination()`. */
5017
+ private tabCoordinator;
5018
+ /** Cross-tab write relay; created on `enableTabCoordination()`. */
5019
+ private writeRelay;
4357
5020
  /** Per-vault policy enforcers. */
4358
5021
  private readonly policyEnforcers;
4359
5022
  private readonly txStrategy;
4360
5023
  private readonly sessionStrategy;
4361
5024
  private readonly syncStrategy;
5025
+ private readonly snapshotStrategy;
5026
+ private snapshotScheduler;
5027
+ private readonly dirtySnapshotVaults;
4362
5028
  /**
4363
5029
  * Currently-running multi-record transaction, set by
4364
5030
  * `runTransaction` at the start of Phase 2 (commit) and cleared in
4365
5031
  * the same function's `finally` block. Side-effect writes triggered
4366
5032
  * during a staged op's `Collection.put` (today: eager derivation
4367
5033
  * outputs) register their pre-write envelope on `_executed` here so
4368
- * a mid-batch failure rolls them back alongside the main staged ops
4369
- * (#133). `null` outside of Phase 2.
5034
+ * a mid-batch failure rolls them back alongside the main staged ops.
5035
+ * `null` outside of Phase 2.
4370
5036
  * @internal
4371
5037
  */
4372
5038
  private _activeTxContext;
@@ -4469,8 +5135,6 @@ declare class Noydb {
4469
5135
  * @throws `NoAccessError` when no keyring exists for the target.
4470
5136
  * @throws `PermissionDeniedError` when the role hierarchy rejects.
4471
5137
  * @throws `ValidationError` when no field is provided.
4472
- *
4473
- * @see #54
4474
5138
  */
4475
5139
  updateUser(vault: string, options: UpdateUserOptions, factors?: FactorProofBundle): Promise<void>;
4476
5140
  /**
@@ -4657,6 +5321,15 @@ declare class Noydb {
4657
5321
  * to the vault's ledger as `op: 'amendment'`.
4658
5322
  */
4659
5323
  transaction<T>(options: AmendmentTxOptions, fn: (tx: TxContext) => Promise<T> | T): Promise<T>;
5324
+ /**
5325
+ * Dry-run a transaction: run the body to stage ops, then return
5326
+ * the directly-affected diff + collected guard violations WITHOUT
5327
+ * committing (no adapter writes, no write hooks). MV/derivation cascade
5328
+ * is not simulated. Requires `withTransactions()`.
5329
+ */
5330
+ transaction(options: {
5331
+ readonly dryRun: true;
5332
+ }, fn: (tx: TxContext) => unknown): Promise<DryRunResult>;
4660
5333
  /**
4661
5334
  * Create a sync transaction for the given vault.
4662
5335
  * The vault must already be open via `openVault()`.
@@ -4677,7 +5350,7 @@ declare class Noydb {
4677
5350
  * Phase 2. `Collection.dispatchDerivations` consults this so a
4678
5351
  * recursive derived-output write inside `Collection.put` can register
4679
5352
  * its envelope onto `ctx._executed` and roll back with the main
4680
- * staged ops on mid-batch failure (#133).
5353
+ * staged ops on mid-batch failure.
4681
5354
  *
4682
5355
  * @internal
4683
5356
  */
@@ -4702,7 +5375,7 @@ declare class Noydb {
4702
5375
  * `Collection.putManyAtomic` (via `derivationSource.createTxContext`)
4703
5376
  * to publish an active context for the duration of its bulk-atomic
4704
5377
  * Phase 2 loop, so recursive derivation-output writes register on
4705
- * `ctx._executed` and roll back together with the source ops (#133).
5378
+ * `ctx._executed` and roll back together with the source ops.
4706
5379
  *
4707
5380
  * @internal
4708
5381
  */
@@ -4721,6 +5394,54 @@ declare class Noydb {
4721
5394
  private getSyncEngine;
4722
5395
  on<K extends keyof NoydbEventMap>(event: K, handler: (data: NoydbEventMap[K]) => void): void;
4723
5396
  off<K extends keyof NoydbEventMap>(event: K, handler: (data: NoydbEventMap[K]) => void): void;
5397
+ /**
5398
+ * Observable write-queue for this hub instance. Reflects outstanding
5399
+ * in-flight writes across all collections. See {@link WriteQueue}.
5400
+ *
5401
+ * @example
5402
+ * window.addEventListener('beforeunload', (e) => {
5403
+ * if (db.writeQueue.pending) { e.preventDefault(); e.returnValue = '' }
5404
+ * })
5405
+ */
5406
+ get writeQueue(): WriteQueue;
5407
+ /**
5408
+ * @internal Mutable tracker behind {@link writeQueue}. Threaded into
5409
+ * each Collection (via Vault) so `put`/`delete` can `track()` writes.
5410
+ * Not part of the public surface — consumers use `writeQueue`.
5411
+ */
5412
+ get _writeQueueTracker(): WriteQueueTracker;
5413
+ /**
5414
+ * Register a hook that runs before each write. Awaited; a throw
5415
+ * aborts the write. Returns an unsubscribe function.
5416
+ */
5417
+ onBeforeWrite(handler: WriteHook): Unsubscribe$3;
5418
+ /**
5419
+ * Register a hook that runs after each committed write. Awaited;
5420
+ * a handler error is warned, never rolled back. Returns an unsubscribe fn.
5421
+ */
5422
+ onAfterWrite(handler: WriteHook): Unsubscribe$3;
5423
+ /** Subscribe to cross-tab write conflicts. Returns an unsubscribe. */
5424
+ onWriteConflict(fn: (c: WriteConflict) => void): Unsubscribe$3;
5425
+ /**
5426
+ * Enable same-device multi-tab coordination: primary/secondary
5427
+ * election + presence. Browser-only — a graceful no-op (role 'unknown')
5428
+ * when Web Locks / BroadcastChannel are unavailable and nothing is
5429
+ * injected. Idempotent; returns a disposer.
5430
+ */
5431
+ enableTabCoordination(opts?: TabCoordinationOptions): {
5432
+ dispose: () => void;
5433
+ };
5434
+ private disableTabCoordination;
5435
+ get tabRole(): TabRole;
5436
+ activeTabs(): TabPresence[];
5437
+ onTabRoleChange(fn: (r: TabRole) => void): Unsubscribe$3;
5438
+ onActiveTabsChange(fn: (t: TabPresence[]) => void): Unsubscribe$3;
5439
+ /** @internal The write-hook registry, threaded into each Collection. */
5440
+ get _writeHooks(): WriteHookRegistry;
5441
+ /** @internal The observe bus, threaded into every Collection. */
5442
+ get _subsystemBus(): SubsystemBus;
5443
+ /** @internal Stable per-instance id for schema-cutover coordination. */
5444
+ get _clientId(): string;
4724
5445
  /**
4725
5446
  * Soft-lock a single vault: clear its in-memory keyring, DEKs, vault
4726
5447
  * instance, sync engine, policy enforcer, and active-tier entry —
@@ -4737,10 +5458,6 @@ declare class Noydb {
4737
5458
  * survives lock; nothing about it changes when DEKs are scrubbed).
4738
5459
  *
4739
5460
  * No-op when `vault` is not currently in cache (idempotent).
4740
- *
4741
- * Unblocks vLannaAi/niwat#33.
4742
- *
4743
- * @see #17
4744
5461
  */
4745
5462
  lockVault(vault: string): void;
4746
5463
  close(): void;
@@ -4774,7 +5491,7 @@ declare class Noydb {
4774
5491
  */
4775
5492
  updatePolicy(vault: string, override: Partial<VaultPolicy>): Promise<VaultPolicy>;
4776
5493
  /**
4777
- * Read the current vault-level user-directory toggle (#122). Returns
5494
+ * Read the current vault-level user-directory toggle. Returns
4778
5495
  * the default-on shape (`{ enabled: true }`) when no `_meta/directory`
4779
5496
  * document has been persisted yet.
4780
5497
  *
@@ -4782,7 +5499,7 @@ declare class Noydb {
4782
5499
  */
4783
5500
  getDirectoryEnabled(vault: string): Promise<boolean>;
4784
5501
  /**
4785
- * Toggle the vault's user-directory listing on or off (#122).
5502
+ * Toggle the vault's user-directory listing on or off.
4786
5503
  * Owner-only. When disabled, `listUsersWithEnvelopes()` throws
4787
5504
  * {@link import('./errors.js').DirectoryDisabledError} for callers
4788
5505
  * whose role is neither `owner` nor `admin`.
@@ -4814,7 +5531,7 @@ declare class Noydb {
4814
5531
  *
4815
5532
  * Two enforcement modes:
4816
5533
  *
4817
- * 1. **Managed-mode mandatory strong-recovery (#195).** When
5534
+ * 1. **Managed-mode mandatory strong-recovery.** When
4818
5535
  * `passphraseMode === 'managed'`, the vault MUST have at least
4819
5536
  * one **strong** recovery profile (Shamir today). Paper alone is
4820
5537
  * rejected because under managed mode the user has no memorized
@@ -4834,7 +5551,7 @@ declare class Noydb {
4834
5551
  */
4835
5552
  private assertRecoveryEnrolled;
4836
5553
  /**
4837
- * Internal accessor used by tier-2/tier-3 unlock paths (issue #11)
5554
+ * Internal accessor used by tier-2/tier-3 unlock paths
4838
5555
  * to mark the active session tier.
4839
5556
  * @internal
4840
5557
  */
@@ -4858,7 +5575,7 @@ declare class Noydb {
4858
5575
  * `remove-authenticator`.
4859
5576
  */
4860
5577
  removeAuthenticator(vault: string, slotId: string, factors?: FactorProofBundle): Promise<void>;
4861
- /** Read the slot list for a vault. Internal — `describeAuthConfig` (#13) consumes this. */
5578
+ /** Read the slot list for a vault. Internal — `describeAuthConfig` consumes this. */
4862
5579
  listAuthenticators(vault: string): Promise<ReadonlyArray<KeyringAuthenticator>>;
4863
5580
  /**
4864
5581
  * Mutate the `meta` blob on an existing authenticator slot — slot
@@ -4867,7 +5584,7 @@ declare class Noydb {
4867
5584
  * are immutable through this method. Anti-slot-swap is structural,
4868
5585
  * not gate-driven.
4869
5586
  *
4870
- * `meta` patch semantics (#57-aligned):
5587
+ * `meta` patch semantics (top-level merge):
4871
5588
  * - Top-level merge — absent keys preserved
4872
5589
  * - `null` value — delete that meta key
4873
5590
  * - Other values — replace verbatim
@@ -4885,12 +5602,10 @@ declare class Noydb {
4885
5602
  *
4886
5603
  * @throws `NoAccessError` when no slot with the given id exists.
4887
5604
  * @throws `ValidationError` when no patch field is provided.
4888
- *
4889
- * @see #55
4890
5605
  */
4891
5606
  updateAuthenticator(vault: string, slotId: string, options: UpdateAuthenticatorOptions, factors?: FactorProofBundle): Promise<void>;
4892
5607
  /**
4893
- * Native WebAuthn enrollment using the **real** internal keyring (#16).
5608
+ * Native WebAuthn enrollment using the **real** internal keyring.
4894
5609
  *
4895
5610
  * Why this exists: when a consumer is using `createNoydb({ secret })`,
4896
5611
  * they cannot reach the live `UnlockedKeyring` to feed it to
@@ -4933,8 +5648,6 @@ declare class Noydb {
4933
5648
  * a server-side allowlist).
4934
5649
  *
4935
5650
  * Gated by `enroll-authenticator` like `enrollAuthenticator()` itself.
4936
- *
4937
- * @see #16
4938
5651
  */
4939
5652
  enrollWebAuthn(vault: string, ceremony: (keyring: UnlockedKeyring) => Promise<EnrollAuthenticatorOptions>, factors?: FactorProofBundle): Promise<{
4940
5653
  credentialId: string;
@@ -4945,8 +5658,6 @@ declare class Noydb {
4945
5658
  * deciding when a new device prompt should appear. Identity is
4946
5659
  * `id` + `enrolled_at`; the `meta.credentialId` (base64) is used by
4947
5660
  * `allowCredentials` at unlock time.
4948
- *
4949
- * @see #16
4950
5661
  */
4951
5662
  listWebAuthnSlots(vault: string): Promise<ReadonlyArray<{
4952
5663
  id: string;
@@ -5010,8 +5721,7 @@ declare class Noydb {
5010
5721
  *
5011
5722
  * Tier-2 authenticator slots are dropped — each slot wraps the old
5012
5723
  * KEK and would need its derivation key to be re-presented. Re-enrol
5013
- * via `db.enrollAuthenticator` after rotation. Tracked as a
5014
- * v0.1.0-pre.5 limitation.
5724
+ * via `db.enrollAuthenticator` after rotation.
5015
5725
  *
5016
5726
  * @throws `WeakPassphraseError` on a weak new phrase.
5017
5727
  * @throws `PolicyDeniedError` when the gate denies (missing factor, …).
@@ -5020,14 +5730,14 @@ declare class Noydb {
5020
5730
  rotatePassphrase(vault: string, input: RotatePassphraseInput, factors?: FactorProofBundle): Promise<void>;
5021
5731
  /**
5022
5732
  * Reset the passphrase using a recovery proof (user forgot the old).
5023
- * v0.1.0-pre.5 supports the `'paper'` profile end-to-end; the
5024
- * other three profiles throw {@link RecoveryProfileNotImplementedError}.
5733
+ * Currently supports the `'paper'` profile end-to-end; the
5734
+ * other profiles throw {@link RecoveryProfileNotImplementedError}.
5025
5735
  *
5026
5736
  * Burns the used recovery entry on success.
5027
5737
  */
5028
5738
  recoverPassphrase(vault: string, input: RecoverPassphraseInput, factors?: FactorProofBundle): Promise<RecoverPassphraseResult>;
5029
5739
  /**
5030
- * Deliberate paper-recovery-code regeneration (#121). User knows their
5740
+ * Deliberate paper-recovery-code regeneration. User knows their
5031
5741
  * passphrase but wants a fresh sheet — they lost the printout or
5032
5742
  * suspect compromise of the off-site copy.
5033
5743
  *
@@ -5037,7 +5747,7 @@ declare class Noydb {
5037
5747
  *
5038
5748
  * Gated by the `rotate-recovery` policy gate:
5039
5749
  * - PERSONAL_POLICY: `{ minTier: 1 }` — knowing the passphrase
5040
- * suffices, matching the pre-#121 low-level flow's bar.
5750
+ * suffices, matching the lower-level flow's bar.
5041
5751
  * - STRICT_POLICY: `{ minTier: 1, factors: [{ anyOf: ['totp',
5042
5752
  * 'email-otp', 'webauthn-roaming'] }] }` — rotation is an
5043
5753
  * off-site-trust event; require an off-device factor so a
@@ -5073,7 +5783,7 @@ declare class Noydb {
5073
5783
  private rotateRecoveryPaper;
5074
5784
  private rotateRecoveryShamir;
5075
5785
  /**
5076
- * **Atomic create-and-enroll for managed-mode vaults (#195).**
5786
+ * **Atomic create-and-enroll for managed-mode vaults.**
5077
5787
  *
5078
5788
  * Bootstraps a managed-mode vault and enrolls strong recovery in
5079
5789
  * a single ceremony. Under `passphraseMode: 'managed'`, every
@@ -5118,7 +5828,7 @@ declare class Noydb {
5118
5828
  readonly recoveryEnrollments: ReadonlyArray<EnrollRecoveryResult>;
5119
5829
  }>;
5120
5830
  /**
5121
- * **Recovery flow under managed-passphrase mode (#195).**
5831
+ * **Recovery flow under managed-passphrase mode.**
5122
5832
  *
5123
5833
  * Replaces the sealed passphrase of a managed-mode vault with a
5124
5834
  * fresh 256-bit random, sealed under the configured
@@ -5135,7 +5845,7 @@ declare class Noydb {
5135
5845
  * 5. Drop the keyring cache so the next operation re-derives.
5136
5846
  *
5137
5847
  * The vault's strong-recovery enrollment is preserved across
5138
- * recovery (Shamir entries are not burned on use — see #196).
5848
+ * recovery (Shamir entries are not burned on use).
5139
5849
  *
5140
5850
  * @throws ValidationError if the Noydb instance is not in managed mode.
5141
5851
  */
@@ -5145,7 +5855,7 @@ declare class Noydb {
5145
5855
  }): Promise<void>;
5146
5856
  /**
5147
5857
  * Atomic peer-recovery — re-wraps an EXISTING user's keyring under
5148
- * a fresh temp passphrase in a single store write. Closes #34's
5858
+ * a fresh temp passphrase in a single store write. Closes the
5149
5859
  * partial-failure window (the previous compose-from-primitives
5150
5860
  * pattern was `db.revoke + db.grant`, two writes — if the issuer
5151
5861
  * cancelled between them the target was locked out entirely).
@@ -5155,7 +5865,7 @@ declare class Noydb {
5155
5865
  * - Same `userId`, role, permissions, capabilities preserved.
5156
5866
  * - DEKs unchanged → every other principal in the vault keeps
5157
5867
  * access. No key rotation.
5158
- * - Allows owner→owner natively (#33). The existing
5868
+ * - Allows owner→owner natively. The existing
5159
5869
  * `db.revoke` retains its block — peer-recovery is a separate,
5160
5870
  * intentionally-named operation.
5161
5871
  * - Tier-2 slots dropped (they wrap the old KEK).
@@ -5184,11 +5894,10 @@ declare class Noydb {
5184
5894
  * @throws `PrivilegeEscalationError` when the caller lacks a DEK
5185
5895
  * the target previously had access to.
5186
5896
  *
5187
- * @see #33 #34 — the issues this method closes.
5188
5897
  */
5189
5898
  recoverUser(vault: string, options: RecoverUserOptions, factors?: FactorProofBundle): Promise<void>;
5190
5899
  /**
5191
- * Persist a recovery enrollment. v0.1.0-pre.5 accepts the `'paper'`
5900
+ * Persist a recovery enrollment. Accepts the `'paper'`
5192
5901
  * profile.
5193
5902
  *
5194
5903
  * The hub wraps the user's DEK set (not the KEK) under a code-derived
@@ -5208,7 +5917,7 @@ declare class Noydb {
5208
5917
  * showCodesToUser(codes)
5209
5918
  * ```
5210
5919
  *
5211
- * As of pre.8, `@noy-db/on-recovery`'s `generateRecoveryCodeSet`
5920
+ * `@noy-db/on-recovery`'s `generateRecoveryCodeSet`
5212
5921
  * delegates to `mintPaperRecoveryEntry` internally — its output is
5213
5922
  * fed directly to this API. Pick whichever fits your code-gen layer:
5214
5923
  *
@@ -5219,7 +5928,7 @@ declare class Noydb {
5219
5928
  * ```
5220
5929
  */
5221
5930
  enrollRecovery(vault: string, enrollment: RecoveryEnrollmentInput): Promise<EnrollRecoveryResult>;
5222
- /** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig` (#13). */
5931
+ /** Read the persisted recovery entries (paper + Shamir). Used by `describeAuthConfig`. */
5223
5932
  listRecoveryEntries(vault: string): Promise<{
5224
5933
  paper: ReadonlyArray<PaperRecoveryEntry>;
5225
5934
  shamir: ReadonlyArray<ShamirRecoveryEntry>;
@@ -5247,11 +5956,11 @@ declare class Noydb {
5247
5956
  /** Drop the tier-3 state for a vault — explicit logout. */
5248
5957
  clearQuickUnlock(vault: string): void;
5249
5958
  /**
5250
- * Public accessor for the unlocked keyring of a vault — issue #28.
5959
+ * Public accessor for the unlocked keyring of a vault.
5251
5960
  *
5252
5961
  * Returns a **defensive shallow copy** so consumers can read the DEK
5253
5962
  * map and authenticator list without the risk of mutating the hub's
5254
- * internal cache (#88). Internal hub code paths use a live reference
5963
+ * internal cache. Internal hub code paths use a live reference
5255
5964
  * via `getKeyringInternal`; ceremonies and external consumers always
5256
5965
  * get a snapshot.
5257
5966
  *
@@ -5287,6 +5996,34 @@ declare class Noydb {
5287
5996
  * should use {@link getKeyring}, which returns a defensive copy.
5288
5997
  */
5289
5998
  private getKeyringInternal;
5999
+ /**
6000
+ * Take an on-demand checkpoint of the given vault.
6001
+ * Requires `snapshotStrategy: withSnapshots({ store })` in `createNoydb`.
6002
+ * @throws ValidationError when the vault is not open
6003
+ */
6004
+ snapshot(vault: string, opts?: {
6005
+ label?: string;
6006
+ note?: string;
6007
+ }): Promise<SnapshotMeta>;
6008
+ /**
6009
+ * Wire the automatic-snapshot cadence when a non-manual `snapshotPolicy` is
6010
+ * configured. Subscribes to `onAfterWrite` to mark the written vault dirty and
6011
+ * nudge the scheduler; the scheduler fires `autoSnapshot()` per dirty vault.
6012
+ * No-op for `mode:'manual'` or no policy.
6013
+ */
6014
+ private initSnapshotCadence;
6015
+ /**
6016
+ * List all snapshots for the given vault, newest first.
6017
+ * Reads only the sidecar index — does not download snapshot bytes.
6018
+ */
6019
+ listSnapshots(vault: string): Promise<SnapshotMeta[]>;
6020
+ /**
6021
+ * Restore the vault to a previously snapshotted state.
6022
+ * Runs `verifyBackupIntegrity()` automatically on restore.
6023
+ * @throws SnapshotNotFoundError when `version` doesn't exist in the store
6024
+ * @throws ValidationError when the vault is not open
6025
+ */
6026
+ restoreSnapshot(vault: string, version: string): Promise<void>;
5290
6027
  }
5291
6028
  /** Create a new NOYDB instance. */
5292
6029
  declare function createNoydb(options: NoydbOptions): Promise<Noydb>;
@@ -5492,8 +6229,8 @@ interface GuardStrategy<T extends Record<string, unknown>> {
5492
6229
  * })
5493
6230
  * ```
5494
6231
  *
5495
- * Also skipped on system-internal deletes (derivation tombstones from
5496
- * #144, MV refresh from Dim 14 v2) — those use `_internalDelete`
6232
+ * Also skipped on system-internal deletes (derivation tombstones,
6233
+ * MV refresh from Dim 14 v2) — those use `_internalDelete`
5497
6234
  * which bypasses every user-facing delete hook. Housekeeping ops are
5498
6235
  * NOT user-initiated and should not trip user invariants.
5499
6236
  *
@@ -5552,14 +6289,14 @@ interface RecordOutputSpec {
5552
6289
  * `undefined`) for this output key. The executor interprets that as
5553
6290
  * "no output for this invocation": a previously-emitted output at
5554
6291
  * the same id is deleted (mirroring the empty-group / empty-aggregate
5555
- * semantics flagged in #142); a never-emitted output is a silent
6292
+ * semantics for empty groups); a never-emitted output is a silent
5556
6293
  * no-op. When `false` (default), returning `null` throws
5557
6294
  * `DerivationOutputShapeError` — same as v1.
5558
6295
  */
5559
6296
  optional?: boolean;
5560
6297
  }
5561
6298
  /**
5562
- * Array-shape output (#200) — one source row produces a variable-length
6299
+ * Array-shape output — one source row produces a variable-length
5563
6300
  * list of output rows, each with its own id (from the `key` extractor).
5564
6301
  *
5565
6302
  * On every source-row change, the dispatcher diffs the previously
@@ -5629,6 +6366,17 @@ interface DerivationStrategy<TSource extends Record<string, unknown>, TOutputs e
5629
6366
  * sibling records via `ctx.vault.collection<T>(name).get(id)` /
5630
6367
  * `.list()` / `.query()`. The vault accessor is read-only; there is
5631
6368
  * no path to a writer from `ctx`.
6369
+ *
6370
+ * **Per-output omission (runtime-supported):** returning `null` or
6371
+ * `undefined` for an individual output key is handled at runtime via
6372
+ * the `optional: true` flag on the output's `RecordOutputSpec` —
6373
+ * the executor deletes any previously-emitted output at that id, or
6374
+ * silently no-ops if none exists. For `shape: 'array'` outputs, null
6375
+ * / undefined is treated as an empty array (clears all prior rows for
6376
+ * that source). The *top-level* return must always be a `TOutputs`
6377
+ * object; returning `null` for the whole result is not supported.
6378
+ * (#297 — MV `unionSources` map drop-row is the analogous feature for
6379
+ * materialized views.)
5632
6380
  */
5633
6381
  derive: (source: TSource, ctx: DerivationContext) => Promise<TOutputs> | TOutputs;
5634
6382
  /**
@@ -5669,6 +6417,17 @@ declare class DerivationRegistry {
5669
6417
  register(spec: DerivationStrategy<any, any>): Promise<void>;
5670
6418
  strategiesForSource(source: string): ReadonlyArray<RegisteredStrategy>;
5671
6419
  strategiesProducingOutput(collection: string): ReadonlyArray<RegisteredStrategy>;
6420
+ /**
6421
+ * All registered strategies as a flat, deduplicated array.
6422
+ * Each strategy is indexed once per source (not once per output key),
6423
+ * so iterating `_bySource.values()` naturally yields each strategy
6424
+ * exactly once per source — deduplication is handled by flattening
6425
+ * the per-source arrays and collecting into a Set by identity.
6426
+ *
6427
+ * Used by `dumpSchema()` / `describeDerivations()` in the introspection
6428
+ * walker to populate the derivations map.
6429
+ */
6430
+ all(): ReadonlyArray<RegisteredStrategy>;
5672
6431
  /**
5673
6432
  * Cycle detection over the source → output → … graph. Call after all
5674
6433
  * `register()` calls complete (i.e. at vault open). Throws
@@ -5750,8 +6509,14 @@ interface UnionSource<TRow extends Record<string, unknown>> {
5750
6509
  * Called once per source row at materialization time. Each arm's
5751
6510
  * mapped output is concatenated into a single stream before
5752
6511
  * `groupBy` + `aggregate` run.
6512
+ *
6513
+ * Returning `null` or `undefined` **omits** the source row from the
6514
+ * materialized output entirely — the row is not pushed into the
6515
+ * unified stream and never reaches `groupBy` / `aggregate`. This
6516
+ * removes the need for sentinel rows (e.g. `{ amount: 0 }`) whose
6517
+ * sole purpose is to be aggregated away (#297).
5753
6518
  */
5754
- readonly map: (sourceRow: Record<string, unknown>) => TRow;
6519
+ readonly map: (sourceRow: Record<string, unknown>) => TRow | null | undefined;
5755
6520
  }
5756
6521
  /**
5757
6522
  * Registration shape passed to `withMaterializedView()`.
@@ -5782,7 +6547,7 @@ interface MaterializedViewStrategy<TRow extends Record<string, unknown>> {
5782
6547
  */
5783
6548
  query?: (db: MVQueryContext) => Query<TRow>;
5784
6549
  /**
5785
- * UNION-form sources (#165): an explicit list of sibling collections
6550
+ * UNION-form sources: an explicit list of sibling collections
5786
6551
  * that contribute rows to a single MV. Each arm's `map` projects a
5787
6552
  * source row into the MV's unified row shape; the mapped streams are
5788
6553
  * concatenated, then {@link groupBy} + {@link aggregate} run on the
@@ -5798,7 +6563,7 @@ interface MaterializedViewStrategy<TRow extends Record<string, unknown>> {
5798
6563
  */
5799
6564
  unionSources?: ReadonlyArray<UnionSource<TRow>>;
5800
6565
  /**
5801
- * Group-key field(s) for UNION mode (#165). Applied to the
6566
+ * Group-key field(s) for UNION mode. Applied to the
5802
6567
  * concatenated mapped-row stream from {@link unionSources} before
5803
6568
  * {@link aggregate} runs. Accepts a single field name or a tuple of
5804
6569
  * field names for multi-key grouping (same shape as
@@ -5810,7 +6575,7 @@ interface MaterializedViewStrategy<TRow extends Record<string, unknown>> {
5810
6575
  */
5811
6576
  groupBy?: string | ReadonlyArray<string>;
5812
6577
  /**
5813
- * Aggregation spec for UNION mode (#165). Applied per-group after
6578
+ * Aggregation spec for UNION mode. Applied per-group after
5814
6579
  * {@link groupBy} buckets the concatenated mapped-row stream from
5815
6580
  * {@link unionSources}. Same shape as the `AggregateSpec` passed to
5816
6581
  * `Query.aggregate()`.
@@ -5821,11 +6586,11 @@ interface MaterializedViewStrategy<TRow extends Record<string, unknown>> {
5821
6586
  /**
5822
6587
  * Pure function from a materialized row → stable id used in the
5823
6588
  * output collection. Required — explicit always beats default-with-pitfalls
5824
- * (see niwat-review of #149 round 1 for the slash-collision rationale).
6589
+ * (explicit always beats default-with-pitfalls; see the slash-collision rationale).
5825
6590
  */
5826
6591
  rowKey: (row: TRow) => string;
5827
6592
  /**
5828
- * Explicit source collections (#152). Required when `query()` returns
6593
+ * Explicit source collections. Required when `query()` returns
5829
6594
  * an `Aggregation` or `GroupedAggregation` rather than a `Query<T>`
5830
6595
  * — the dependency analyzer can't introspect through `groupBy().aggregate()`
5831
6596
  * back to the source. Optional for plain `Query<T>` results — the
@@ -5835,7 +6600,7 @@ interface MaterializedViewStrategy<TRow extends Record<string, unknown>> {
5835
6600
  */
5836
6601
  sources?: ReadonlyArray<string>;
5837
6602
  /**
5838
- * Declared deterministic predicates (#153). Each entry pairs a
6603
+ * Declared deterministic predicates. Each entry pairs a
5839
6604
  * consumer-stable `hash` with a function. The `query()` callback's
5840
6605
  * Query<T> can invoke them via `.wherePredicate(name, ctx?)`. The
5841
6606
  * predicate's `hash` + a canonical-JSON hash of `ctx` both fold
@@ -5872,8 +6637,8 @@ interface MaterializedViewStrategy<TRow extends Record<string, unknown>> {
5872
6637
  *
5873
6638
  * - `'delete'` (default) — tombstone the prior MV row via
5874
6639
  * `Collection._internalDelete` (system housekeeping bypasses user
5875
- * `onDelete` guards on the output collection — see PR #148's
5876
- * composition fix).
6640
+ * `onDelete` guards on the output collection — the housekeeping
6641
+ * bypass composition fix).
5877
6642
  * - `'keep'` — leave the prior MV row in place. Useful when zero
5878
6643
  * is a meaningful state.
5879
6644
  */
@@ -5881,7 +6646,7 @@ interface MaterializedViewStrategy<TRow extends Record<string, unknown>> {
5881
6646
  /**
5882
6647
  * `true` re-throws on any row-write failure → composes with
5883
6648
  * `withTransactions` to roll back the source-write atomically via
5884
- * `revertExecuted` (#133). Default `false` (failed rows are
6649
+ * `revertExecuted`. Default `false` (failed rows are
5885
6650
  * isolated; other rows commit).
5886
6651
  */
5887
6652
  strict?: boolean;
@@ -5915,7 +6680,7 @@ interface RegisteredMV {
5915
6680
  * Top-level FieldClauses on the partition field, captured at
5916
6681
  * registration time. Used by the cycle detector to resolve
5917
6682
  * same-collection-as-source edges via the partition-discriminator
5918
- * check (#152). Empty when `spec.output?.partition` is undefined.
6683
+ * check. Empty when `spec.output?.partition` is undefined.
5919
6684
  */
5920
6685
  readonly partitionClauses: readonly FieldClause[];
5921
6686
  }
@@ -5967,7 +6732,7 @@ declare class MaterializedViewRegistry {
5967
6732
  }
5968
6733
 
5969
6734
  /**
5970
- * Read-shadow overlay primitive (#154, MV v2 spec § Composition with
6735
+ * Read-shadow overlay primitive (MV v2 spec § Composition with
5971
6736
  * operator-editable lifecycle). Binds an MV's read-only base output
5972
6737
  * to a separate user-writable overlay collection; reads merge via a
5973
6738
  * single shadow predicate, writes route to the overlay.
@@ -6045,6 +6810,14 @@ declare class OverlayedViewRegistry {
6045
6810
  /** All overlay virtual names. */
6046
6811
  names(): ReadonlySet<string>;
6047
6812
  isOverlay(name: string): boolean;
6813
+ /**
6814
+ * All registered overlay strategies as a flat array.
6815
+ * Each strategy carries `name`, `base`, and `overlay` fields that
6816
+ * `describeOverlays()` in the introspection walker reads directly.
6817
+ *
6818
+ * Used by `dumpSchema()` to populate the `overlayViews` map.
6819
+ */
6820
+ all(): ReadonlyArray<OverlayedViewStrategy>;
6048
6821
  /**
6049
6822
  * Resolve the `rowKey` function for an overlay's base MV. Returns
6050
6823
  * `undefined` if the base isn't an MV (raw source collection) or
@@ -6070,6 +6843,11 @@ declare class GuardRegistry {
6070
6843
  register<T extends Record<string, unknown>>(spec: GuardStrategy<T>): void;
6071
6844
  /** All guards registered against `collection` in registration order. */
6072
6845
  guardsFor(collection: string): ReadonlyArray<AnyGuard>;
6846
+ /** Per-collection guard counts, for introspection. */
6847
+ summary(): {
6848
+ collection: string;
6849
+ count: number;
6850
+ }[];
6073
6851
  /**
6074
6852
  * Run every guard's `check` for this collection. First throw wins —
6075
6853
  * remaining guards are not invoked. Guards without a `check` skip.
@@ -6506,7 +7284,7 @@ declare function magicLinkGrantRecordId(token: string, index: number): string;
6506
7284
  declare function isMagicLinkGrantExpired(payload: MagicLinkGrantPayload, now?: Date): boolean;
6507
7285
 
6508
7286
  /**
6509
- * Type surface for the user-list visibility subsystem (#122).
7287
+ * Type surface for the user-list visibility subsystem.
6510
7288
  *
6511
7289
  * Two complementary flags:
6512
7290
  * - {@link DirectoryConfig} — vault-level "is the directory listing
@@ -6557,7 +7335,7 @@ interface UserVisibility {
6557
7335
  * own keyringId. **Own-only write rule** is structural — no method
6558
7336
  * exists to write someone else's envelope.
6559
7337
  * - Read-anyone: `get` / `list` — read other principals' envelopes
6560
- * (subject to `view-team-profiles` policy gate, wired in #22).
7338
+ * (subject to `view-team-profiles` policy gate).
6561
7339
  * - Reactive: `subscribe` / `live` — in-process event emission on local
6562
7340
  * writes. Cross-instance updates land via the team/sync engine and
6563
7341
  * surface to subscribers when the sync diff replays through this API.
@@ -6577,7 +7355,7 @@ type DeepPartial<T> = T extends object ? {
6577
7355
  } : T;
6578
7356
  /**
6579
7357
  * Recursive partial with `null` allowed at every level — used by
6580
- * `updateMe` (#57) to express deletion intent in addition to merge.
7358
+ * `updateMe` to express deletion intent in addition to merge.
6581
7359
  *
6582
7360
  * Semantics inside `updateMe`:
6583
7361
  * - `undefined` (or absent key) — skip; source value preserved
@@ -6586,8 +7364,8 @@ type DeepPartial<T> = T extends object ? {
6586
7364
  * replace for primitives / arrays)
6587
7365
  *
6588
7366
  * Matches lodash `_.merge` behavior on `null` and Firestore's
6589
- * `FieldValue.delete()` semantics. Loosened from `DeepPartial<T>` per
6590
- * #57; consumers wanting the original "merge-only" surface can keep
7367
+ * `FieldValue.delete()` semantics. Loosened from `DeepPartial<T>`.
7368
+ * Consumers wanting the original "merge-only" surface can keep
6591
7369
  * importing `DeepPartial` and avoid passing `null`.
6592
7370
  */
6593
7371
  type DeepPartialOrNull<T> = T extends object ? {
@@ -6659,7 +7437,7 @@ declare class UserApi {
6659
7437
  * the envelope on first call. Optimistic-concurrency safe — a stale
6660
7438
  * `_v` (parallel writer on another device) throws `ConflictError`.
6661
7439
  *
6662
- * Patch semantics (#57):
7440
+ * Patch semantics:
6663
7441
  * - `undefined` (or omitted key) — skip; existing value preserved
6664
7442
  * - `null` — delete the field from the merged result
6665
7443
  * - any other value — overwrite (deep-merge for plain objects,
@@ -6813,6 +7591,29 @@ interface PersistedSchemaEnvelope {
6813
7591
  * @module
6814
7592
  */
6815
7593
 
7594
+ /** Flat snapshot of a vault's registered schema. */
7595
+ interface SchemaIntrospection {
7596
+ readonly collections: ReadonlyArray<{
7597
+ name: string;
7598
+ docCount: number;
7599
+ }>;
7600
+ readonly guards: ReadonlyArray<{
7601
+ collection: string;
7602
+ count: number;
7603
+ }>;
7604
+ readonly materializedViews: ReadonlyArray<{
7605
+ name: string;
7606
+ sourceCollections: string[];
7607
+ }>;
7608
+ readonly schemaUpdate: ReadonlyArray<{
7609
+ collection: string;
7610
+ strategies: string[];
7611
+ }>;
7612
+ readonly grants: ReadonlyArray<{
7613
+ collection: string;
7614
+ permission: Permission;
7615
+ }>;
7616
+ }
6816
7617
  /** Where the field-level info in the snapshot came from. */
6817
7618
  type FieldSource = 'persisted' | 'live-validator' | 'sampled' | 'unknown';
6818
7619
  interface FieldDescriptor {
@@ -6921,6 +7722,7 @@ interface VaultIntrospectState {
6921
7722
 
6922
7723
  /** A vault (tenant namespace) containing collections. */
6923
7724
  declare class Vault {
7725
+ #private;
6924
7726
  private readonly adapter;
6925
7727
  /** The vault's name as passed to `openVault()`. Stable for the instance lifetime. */
6926
7728
  readonly name: string;
@@ -6966,23 +7768,23 @@ declare class Vault {
6966
7768
  * `null` for vaults that never register any guard strategy. The
6967
7769
  * runtime class is dynamic-imported on demand so consumers that
6968
7770
  * never use guards don't pull `GuardRegistry`/`GuardExecutor` into
6969
- * their bundle (#130).
7771
+ * their bundle.
6970
7772
  */
6971
7773
  private guardRegistry;
6972
7774
  /**
6973
7775
  * Per-vault derivation registry. Same lazy-load contract as
6974
7776
  * `guardRegistry` — `null` until `_initDerivations()` runs with at
6975
- * least one strategy handle. See #130 for the bundle motivation.
7777
+ * least one strategy handle.
6976
7778
  */
6977
7779
  private derivationRegistry;
6978
7780
  /**
6979
- * Per-vault materialized-view registry (#143/#150). Same lazy-load
7781
+ * Per-vault materialized-view registry. Same lazy-load
6980
7782
  * contract as `derivationRegistry` — `null` until
6981
7783
  * `_initMaterializedViews()` runs with at least one MV handle.
6982
7784
  */
6983
7785
  private materializedViewRegistry;
6984
7786
  /**
6985
- * Per-vault overlay registry (#154). Same lazy-load contract as
7787
+ * Per-vault overlay registry. Same lazy-load contract as
6986
7788
  * `materializedViewRegistry` — `null` until `_initOverlayedViews()`
6987
7789
  * runs with at least one handle.
6988
7790
  */
@@ -7003,7 +7805,7 @@ declare class Vault {
7003
7805
  * target this vault session's keyringId. There is no method to write
7004
7806
  * another principal's envelope (own-only write rule, structural).
7005
7807
  * - Read-anyone: `get(keyringId)`, `list()` — read other principals'
7006
- * envelopes, subject to the `view-team-profiles` policy gate (#22).
7808
+ * envelopes, subject to the `view-team-profiles` policy gate.
7007
7809
  * - Reactive: `subscribe(id, cb)`, `live(id)` — fire on local writes.
7008
7810
  *
7009
7811
  * @see docs/superpowers/specs/2026-05-05-user-envelope-design.md
@@ -7023,12 +7825,20 @@ declare class Vault {
7023
7825
  */
7024
7826
  private readonly reloadKeyring;
7025
7827
  private readonly collectionCache;
7828
+ /** Vault-level schema cutover fence/controller. */
7829
+ readonly schemaFence: SchemaFenceController;
7026
7830
  /**
7027
7831
  * per-collection `blobFields` retention/TTL config.
7028
7832
  * Populated on `collection({ blobFields })` and read by
7029
7833
  * `vault.compact()`. Indexed by collection name.
7030
7834
  */
7031
7835
  private readonly blobFieldsRegistry;
7836
+ /**
7837
+ * Per-collection attestation field-schema (issue side). Populated on
7838
+ * `collection({ attestation })` and read by `issueAttestation()`.
7839
+ * Indexed by collection name.
7840
+ */
7841
+ private readonly attestationRegistry;
7032
7842
  /**
7033
7843
  * Per-vault ledger store. Lazy-initialized on first
7034
7844
  * `collection()` call (which passes it through to the Collection)
@@ -7089,8 +7899,7 @@ declare class Vault {
7089
7899
  * Cache of closed/opened accounting periods.
7090
7900
  * Populated on first `closePeriod` / `openPeriod` / `listPeriods` /
7091
7901
  * per-collection write call. Kept in memory as an ordered list (by
7092
- * `closedAt`) so the `periodGuard` hook runs synchronously against
7093
- * each collection's put/delete path.
7902
+ * `closedAt`) so period checks run fast when the gate bus fires.
7094
7903
  *
7095
7904
  * Sentinel `null` means "not yet loaded" — the first consumer
7096
7905
  * triggers a one-time `loadPeriods()` pass. Every subsequent
@@ -7249,6 +8058,15 @@ declare class Vault {
7249
8058
  * @see docs/superpowers/specs/2026-05-22-schema-dump-design.md
7250
8059
  */
7251
8060
  persistJsonSchema?: boolean;
8061
+ /**
8062
+ * Ordered schema-update strategies. On a detected schema
8063
+ * change, evaluated in order; the first non-`allow` decision wins.
8064
+ * A `reject` is enforced at the write path (`put`/`delete` throw).
8065
+ * Requires `persistJsonSchema: true` (detection needs the baseline).
8066
+ */
8067
+ schemaUpdate?: readonly SchemaUpdateStrategy[];
8068
+ /** — declare the per-field schema for document attestation (issue side). */
8069
+ attestation?: AttestationFieldSchema;
7252
8070
  }): Collection<T>;
7253
8071
  /**
7254
8072
  * Await all background persisted-schema writes triggered by
@@ -7256,6 +8074,45 @@ declare class Vault {
7256
8074
  * Used in tests; production code does not need to call this.
7257
8075
  */
7258
8076
  _drainPendingSchemaWrites(): Promise<void>;
8077
+ /**
8078
+ * Run a coordinated schema cutover. Drains pending writes, waits
8079
+ * for the active client set to quiesce (the ack-barrier), applies every
8080
+ * pending collection transform in bulk, bumps the vault schema generation,
8081
+ * and clears the fence. Returns the count of collections migrated.
8082
+ * `opts.onPoll` (tests) advances other clients between barrier checks.
8083
+ */
8084
+ runSchemaCutover(opts?: {
8085
+ onPoll?: () => Promise<void>;
8086
+ }): Promise<{
8087
+ migrated: number;
8088
+ }>;
8089
+ /**
8090
+ * Refresh a loaded collection's view of one document from a peer
8091
+ * tab's broadcast. No-op when the collection isn't loaded in this tab
8092
+ * (it will read fresh on next open). Mirrors `#runCutoverTransform`'s guard.
8093
+ */
8094
+ _applyRemoteWrite(collectionName: string, docId: string, action: 'put' | 'delete'): Promise<void>;
8095
+ /**
8096
+ * For a detected conflict: capture this tab's clobbered record,
8097
+ * read the common ancestor from history, converge the cache to the store's
8098
+ * authoritative value (the re-read), and return all three for the
8099
+ * WriteConflict payload. Returns null when the collection isn't loaded.
8100
+ */
8101
+ _captureAndConverge(collectionName: string, docId: string, action: 'put' | 'delete', baseV: number): Promise<{
8102
+ local: unknown;
8103
+ remote: unknown;
8104
+ base: unknown;
8105
+ } | null>;
8106
+ /** Recover a stuck cutover fence — reset to normal without bumping. */
8107
+ abortSchemaCutover(): Promise<void>;
8108
+ /** Current schema-cutover fence state for this vault. Thin live read. */
8109
+ schemaFenceState(): Promise<FenceDoc>;
8110
+ /** @internal Start the per-client heartbeat + fence watcher once a cutover is registered. */
8111
+ _ensureFenceCoordination(): void;
8112
+ /** @internal Stop the heartbeat/watcher (vault lock/close). */
8113
+ _stopFenceCoordination(): void;
8114
+ /** @internal Drive one heartbeat + watch cycle deterministically (tests). */
8115
+ _fenceTick(): Promise<void>;
7259
8116
  /**
7260
8117
  * Validate i18nText fields on a `put()`. Called by Collection just
7261
8118
  * before the adapter write, after schema validation. Throws
@@ -7431,6 +8288,22 @@ declare class Vault {
7431
8288
  */
7432
8289
  compact(options?: CompactRunOptions): Promise<CompactionResult>;
7433
8290
  exportBlobs(options?: ExportBlobsOptions): ExportBlobsHandle;
8291
+ issueAttestation(collectionName: string, id: string): Promise<{
8292
+ docId: string;
8293
+ qr: string;
8294
+ keyId: string;
8295
+ publicKeyB64: string;
8296
+ }>;
8297
+ getDocumentSigningPublicKey(): Promise<{
8298
+ keyId: string;
8299
+ publicKeyB64: string;
8300
+ }>;
8301
+ private makeIssueContext;
8302
+ revokeAttestation(docId: string): Promise<void>;
8303
+ unrevokeAttestation(docId: string): Promise<void>;
8304
+ getRevokedDocIds(): Promise<string[]>;
8305
+ publishRevocationList(): Promise<RevocationList>;
8306
+ private makeRevokeContext;
7434
8307
  private writeExportAudit;
7435
8308
  /**
7436
8309
  * Read-only accessor for the invoking keyring's export capability,
@@ -7550,7 +8423,7 @@ declare class Vault {
7550
8423
  * Dynamic-imports `GuardRegistry` + `ReadOnlyVaultFacade` and seeds
7551
8424
  * the registry with the supplied strategy handles. No-op when the
7552
8425
  * handles array is empty — keeps the guard subsystem out of the
7553
- * floor bundle for consumers that don't use guards (#130).
8426
+ * floor bundle for consumers that don't use guards.
7554
8427
  *
7555
8428
  * The read-only facade is eagerly instantiated here so the sync
7556
8429
  * accessor `_getReadOnlyFacade()` (called from the tx amendment
@@ -7558,10 +8431,9 @@ declare class Vault {
7558
8431
  */
7559
8432
  _initGuards(handles: ReadonlyArray<GuardStrategyHandleAny>): Promise<void>;
7560
8433
  /**
7561
- * @internal — Collection.put calls into this. Returns `null` for
7562
- * vaults that never registered any guard strategy. Callers MUST
7563
- * gate on null (the existing `if (this.guardSource)` branches in
7564
- * `Collection` already do this transitively).
8434
+ * @internal — The gate handler in Noydb.#registerGuardGate calls into
8435
+ * this. Returns `null` for vaults that never registered any guard
8436
+ * strategy. Callers MUST gate on null.
7565
8437
  */
7566
8438
  _getGuardRegistry(): GuardRegistry | null;
7567
8439
  /**
@@ -7570,7 +8442,7 @@ declare class Vault {
7570
8442
  * derivation strategies (async because `strategyHash` computation
7571
8443
  * goes through `crypto.subtle.digest`). No-op when the handles
7572
8444
  * array is empty — keeps the derivation subsystem out of the floor
7573
- * bundle for consumers that don't use derivations (#130). Throws
8445
+ * bundle for consumers that don't use derivations. Throws
7574
8446
  * `DerivationCycleError` if a cycle is detected after registration.
7575
8447
  */
7576
8448
  _initDerivations(handles: ReadonlyArray<DerivationStrategyHandle>): Promise<void>;
@@ -7585,7 +8457,7 @@ declare class Vault {
7585
8457
  * MV spec (which invokes its `query()` once for dependency
7586
8458
  * analysis), then runs the unified cycle detection across the MV +
7587
8459
  * derivation graphs. No-op when the handles array is empty — keeps
7588
- * the MV subsystem out of the floor bundle (mirrors v1 #130).
8460
+ * the MV subsystem out of the floor bundle (mirrors the derivation lazy-import pattern).
7589
8461
  * Throws `MaterializedViewCycleError` if a cycle is detected.
7590
8462
  */
7591
8463
  _initMaterializedViews(handles: ReadonlyArray<MaterializedViewStrategyHandle>): Promise<void>;
@@ -7607,13 +8479,13 @@ declare class Vault {
7607
8479
  */
7608
8480
  _getOverlayedViewRegistry(): OverlayedViewRegistry | null;
7609
8481
  /**
7610
- * Manual re-materialize for a single registered MV (#151). Useful
8482
+ * Manual re-materialize for a single registered MV. Useful
7611
8483
  * for `refresh: 'manual'` MVs (whose consumer drives refreshes
7612
8484
  * externally), for stale-bit recovery on vault re-open, and as the
7613
8485
  * explicit bulk-recompute escape hatch after a strategy change.
7614
8486
  *
7615
- * Returns `{ written, deleted, failed }`. `deleted` is always 0 in
7616
- * foundation + this sub-issue — tombstoning lands in #152.
8487
+ * Returns `{ written, deleted, failed }`. `deleted` is always 0
8488
+ * when tombstoning is not enabled.
7617
8489
  *
7618
8490
  * Throws if `name` is not a registered MV.
7619
8491
  */
@@ -7636,20 +8508,17 @@ declare class Vault {
7636
8508
  /**
7637
8509
  * @internal — exposed for `runTransaction({ amendment: true })` so
7638
8510
  * the amendment invariant runner can pass the SAME read-only vault
7639
- * facade that the per-record `Collection.put` guard hook uses
7640
- * (`guardSource.readOnlyVault()` above). Eagerly instantiated by
7641
- * `_initGuards()` so this accessor stays synchronous; returns
7642
- * `null` for vaults that never registered any guard (amendments
7643
- * require at least one guard, so the caller should never see null).
8511
+ * facade that the gate handler in Noydb.#registerGuardGate uses.
8512
+ * Eagerly instantiated by `_initGuards()` so this accessor stays
8513
+ * synchronous; returns `null` for vaults that never registered any
8514
+ * guard (amendments require at least one guard, so the caller should
8515
+ * never see null).
7644
8516
  */
7645
8517
  _getReadOnlyFacade(): ReadOnlyVaultFacade | null;
7646
8518
  /**
7647
- * Internal lazy-allocator for the read-only facade. Used by the
7648
- * per-collection `guardSource.readOnlyVault` callback when guards
7649
- * ARE configured but `_initGuards()` raced with the first guard
7650
- * invocation (theoretically impossible — `Noydb.openVault` awaits
7651
- * `_initGuards` before returning — but we keep the defensive lazy
7652
- * path so the closure's contract stays "always returns a facade").
8519
+ * Internal lazy-allocator for the read-only facade. Used as a
8520
+ * defensive fallback; in practice `_initGuards()` eagerly
8521
+ * instantiates this, so the lazy path is a no-op.
7653
8522
  */
7654
8523
  private _ensureReadOnlyFacade;
7655
8524
  /**
@@ -7856,7 +8725,7 @@ declare class Vault {
7856
8725
  listPeriods(): Promise<readonly PeriodRecord[]>;
7857
8726
  /** Look up a single period by name. Returns `null` if not found. */
7858
8727
  getPeriod(name: string): Promise<PeriodRecord | null>;
7859
- /** @internal — periodGuard callback installed on every Collection. */
8728
+ /** @internal — called by the gate bus before put/delete. */
7860
8729
  _assertTsWritable(existing: {
7861
8730
  ts: string | null;
7862
8731
  record: Record<string, unknown> | null;
@@ -7886,6 +8755,14 @@ declare class Vault {
7886
8755
  * @see docs/superpowers/specs/2026-05-22-schema-dump-design.md
7887
8756
  */
7888
8757
  dumpSchema(opts?: DumpSchemaOptions): Promise<VaultSchemaSnapshot>;
8758
+ /**
8759
+ * Lightweight read of the vault's registered schema: collections
8760
+ * (+ doc counts), guards, materialized views, schema-update strategies,
8761
+ * and the unlocked user's grants. Cheap — one `adapter.list` per
8762
+ * collection, no decryption. For a full snapshot + stats use dumpSchema().
8763
+ * Post-unlock by construction (a Vault only exists with an unlocked keyring).
8764
+ */
8765
+ introspect(): Promise<SchemaIntrospection>;
7889
8766
  /**
7890
8767
  * Internal accessor for {@link dumpVaultSchema}. Exposes the structural
7891
8768
  * state the walker needs (collection cache, registries, ref registry,
@@ -8362,12 +9239,19 @@ interface CacheStats extends LruStats {
8362
9239
  }
8363
9240
  /** A typed collection of records within a vault. */
8364
9241
  declare class Collection<T> {
9242
+ #private;
8365
9243
  private readonly adapter;
8366
9244
  private readonly vault;
8367
9245
  private readonly name;
8368
9246
  private readonly keyring;
8369
9247
  private readonly encrypted;
8370
9248
  private readonly emitter;
9249
+ private readonly writeQueue;
9250
+ private readonly schemaUpdateGate;
9251
+ private readonly schemaFence;
9252
+ private readonly writeHooks;
9253
+ private readonly subsystemBus;
9254
+ private readonly activeTxId;
8371
9255
  private readonly getDEK;
8372
9256
  private readonly onDirty;
8373
9257
  private readonly historyConfig;
@@ -8414,6 +9298,12 @@ declare class Collection<T> {
8414
9298
  * caller site.
8415
9299
  */
8416
9300
  private readonly indexState;
9301
+ /**
9302
+ * In-memory unique-constraint enforcement for eager mode.
9303
+ * `null` when no `unique:true` indexes are declared on this collection,
9304
+ * or when the collection is in lazy mode (which throws at registration).
9305
+ */
9306
+ private readonly uniqueConstraints;
8417
9307
  /**
8418
9308
  * True once `_idx/*` side-cars have been bulk-loaded into
8419
9309
  * `persistedIndexes`. Flipped by `ensurePersistedIndexesLoaded()` on
@@ -8548,42 +9438,14 @@ declare class Collection<T> {
8548
9438
  private readonly syncAdapter;
8549
9439
  /** — consent-audit hook, no-op when no scope is active. */
8550
9440
  private readonly onAccess;
8551
- /**
8552
- * accounting-period write guard. Called BEFORE any
8553
- * adapter write with:
8554
- * - `existing` — the prior envelope's `_ts` and decrypted record
8555
- * (or `null` if no prior envelope exists)
8556
- * - `incoming` — the record being written (or `null` for delete)
8557
- *
8558
- * Throws `PeriodClosedError` if either side falls inside a closed
8559
- * period. Installed by Vault; no-op when no period has been closed.
8560
- * Async so the Vault can lazy-load the period list from the
8561
- * adapter on first use.
8562
- */
8563
- private readonly periodGuard;
8564
- /**
8565
- * Optional back-reference to the owning vault's guard registry + a
8566
- * read-only vault facade. When present, `Collection.put` and
8567
- * `Collection.delete` consult the registry for guards declared
8568
- * against this collection and run their `check` + `frozenFields`
8569
- * before the adapter write. Absent in unit tests that construct
8570
- * a Collection directly; production code always sets it via
8571
- * `Vault.collection()`.
8572
- *
8573
- * Typed structurally rather than as `Vault` to avoid a circular
8574
- * import (mirrors the `refEnforcer` / `joinResolver` pattern).
8575
- */
8576
- private readonly guardSource;
8577
9441
  /**
8578
9442
  * Vault-internal hook for derivation dispatch. When set,
8579
9443
  * `Collection.put` consults the registry after the source-write
8580
9444
  * commits and writes derived outputs through `getCollection(name).put`.
8581
- * Same structural-interface pattern as `guardSource` to avoid a
8582
- * circular Vault import.
8583
9445
  */
8584
9446
  private readonly derivationSource;
8585
9447
  /**
8586
- * Vault-internal hook for materialized-view dispatch (#143/#150).
9448
+ * Vault-internal hook for materialized-view dispatch.
8587
9449
  * Parallel to `derivationSource` — when set, `Collection.put` fires
8588
9450
  * `MaterializedViewRegistry.onSourceWrite` after the source-write
8589
9451
  * commits + after `dispatchDerivations` has run.
@@ -8635,6 +9497,23 @@ declare class Collection<T> {
8635
9497
  keyring: UnlockedKeyring;
8636
9498
  encrypted: boolean;
8637
9499
  emitter: NoydbEventEmitter;
9500
+ /**
9501
+ * Vault-level in-flight write tracker. When present,
9502
+ * `put`/`delete` run inside `writeQueue.track()` so `hub.writeQueue`
9503
+ * reflects outstanding writes. Optional so direct Collection
9504
+ * construction in tests still works untracked.
9505
+ */
9506
+ writeQueue?: WriteQueueTracker | undefined;
9507
+ /** Per-collection schema-update gate; `put`/`delete` await it. */
9508
+ schemaUpdateGate?: SchemaUpdateGate | undefined;
9509
+ /** Vault-level fence controller; `put`/`delete` consult it. */
9510
+ schemaFence?: SchemaFenceController | undefined;
9511
+ /** Hub-level write-hook registry; fired around put/delete. */
9512
+ writeHooks?: WriteHookRegistry | undefined;
9513
+ /** The observe bus, threaded from Noydb. */
9514
+ subsystemBus?: SubsystemBus | undefined;
9515
+ /** Active transaction id supplier (null outside a transaction). */
9516
+ activeTxId?: (() => string | null) | undefined;
8638
9517
  getDEK: (collectionName: string) => Promise<CryptoKey>;
8639
9518
  historyConfig?: HistoryConfig | undefined;
8640
9519
  onDirty?: OnDirtyCallback | undefined;
@@ -8838,33 +9717,19 @@ declare class Collection<T> {
8838
9717
  * to the ledger.
8839
9718
  */
8840
9719
  onCrossTierAccess?: ((event: CrossTierAccessEvent) => void) | undefined;
8841
- periodGuard?: (existing: {
8842
- ts: string | null;
8843
- record: Record<string, unknown> | null;
8844
- } | null, incoming: Record<string, unknown> | null) => Promise<void>;
8845
9720
  /**
8846
- * Optional back-reference to the owning vault's guard registry +
8847
- * read-only facade. When present, put/delete consult registered
8848
- * guards for this collection. Same structural-interface pattern
8849
- * as `refEnforcer` to avoid a circular Vault import.
8850
- */
8851
- guardSource?: {
8852
- registry(): GuardRegistry;
8853
- readOnlyVault(): ReadOnlyVaultFacade$1;
8854
- } | undefined;
8855
9721
  /**
8856
9722
  * Optional back-reference to the owning vault's derivation
8857
9723
  * registry + collection accessor. When present, successful
8858
9724
  * `put()` dispatches registered derivation strategies for the
8859
- * source collection. Same structural-interface pattern as
8860
- * `guardSource` to avoid a circular Vault import.
9725
+ * source collection.
8861
9726
  */
8862
9727
  derivationSource?: {
8863
9728
  registry(): DerivationRegistry;
8864
9729
  getCollection(name: string): Collection<Record<string, unknown>>;
8865
9730
  /**
8866
9731
  * Read-only vault facade handed to `derive(source, ctx)` so a
8867
- * derivation can fetch sibling records (#147). Same shape and
9732
+ * derivation can fetch sibling records. Same shape and
8868
9733
  * instance the guards subsystem uses for `check(incoming, ctx)`.
8869
9734
  */
8870
9735
  getReadOnlyFacade(): ReadOnlyVaultFacade$1;
@@ -8873,13 +9738,13 @@ declare class Collection<T> {
8873
9738
  * transaction context, or `null` when no transaction is running.
8874
9739
  * `dispatchDerivations` consults this so a recursive derived-output
8875
9740
  * write can register its pre-write envelope onto `ctx._executed`
8876
- * and roll back alongside the source op on mid-batch failure (#133).
9741
+ * and roll back alongside the source op on mid-batch failure.
8877
9742
  */
8878
9743
  getActiveTxContext(): TxContext | null;
8879
9744
  /**
8880
9745
  * Construct a transient TxContext bound to the owning Noydb. Used
8881
9746
  * by `Collection.putManyAtomic` to publish an active context for
8882
- * its Phase 2 loop (#133).
9747
+ * its Phase 2 loop.
8883
9748
  */
8884
9749
  createTxContext(): TxContext;
8885
9750
  /** Publish a TxContext for the duration of a bulk-atomic loop. */
@@ -8888,7 +9753,7 @@ declare class Collection<T> {
8888
9753
  clearActiveTxContext(ctx: TxContext): void;
8889
9754
  } | undefined;
8890
9755
  /**
8891
- * Vault-internal hook for materialized-view dispatch (#143/#150).
9756
+ * Vault-internal hook for materialized-view dispatch.
8892
9757
  * Parallel to `derivationSource`. When set, `Collection.put` fires
8893
9758
  * registered MV `onSourceWrite` after the standard derivation
8894
9759
  * dispatch.
@@ -8950,24 +9815,27 @@ declare class Collection<T> {
8950
9815
  pollIntervalMs?: number;
8951
9816
  }): PresenceHandle<P>;
8952
9817
  /**
8953
- * Create or update a record.
9818
+ * Create or update a record. Runs inside the hub's write-queue tracker
9819
+ * so `hub.writeQueue.pending` reflects this write.
8954
9820
  *
8955
9821
  * @param id Record identifier.
8956
9822
  * @param record The record body (validated by the collection's schema
8957
9823
  * if one was attached at `vault.collection(...)` time).
8958
9824
  * @param options Optional metadata for audit + import workflows.
8959
9825
  * `reason` is stamped onto the resulting ledger entry
8960
- * (see #1) so audit consumers can filter via
9826
+ * so audit consumers can filter via
8961
9827
  * `entries.filter(e => e.reason?.startsWith('import:'))`.
8962
9828
  */
8963
9829
  put(id: string, record: T, options?: {
8964
9830
  readonly reason?: string;
8965
9831
  }): Promise<void>;
9832
+ /** @internal Untracked put body — call {@link put}, not this. */
9833
+ private putInternal;
8966
9834
  /**
8967
9835
  * Fire registered MV strategies whose dependency set includes this
8968
9836
  * collection. Eager-mode MVs re-materialize inline via
8969
9837
  * `MaterializedViewExecutor.refresh`; lazy / manual modes are
8970
- * no-ops in the foundation (subtask #150) wired in #151.
9838
+ * no-ops in the foundation; wired in the lazy-mode implementation.
8971
9839
  *
8972
9840
  * Skips entirely when the record being written is itself an
8973
9841
  * MV-emitted row (carries `_materializedFrom`) — defensive guard
@@ -8988,12 +9856,23 @@ declare class Collection<T> {
8988
9856
  * cycle detection.
8989
9857
  */
8990
9858
  private dispatchDerivations;
8991
- /** Delete a record by ID. */
9859
+ /**
9860
+ * Delete a record by ID. Runs inside the hub's write-queue tracker
9861
+ * so `hub.writeQueue.pending` reflects this write.
9862
+ */
8992
9863
  delete(id: string): Promise<void>;
9864
+ /**
9865
+ * @internal — bulk-rewrite every record through a cutover transform.
9866
+ * Raw adapter path (bypasses the write gate + guards — the transform is
9867
+ * trusted and runs only during the `migrating` phase). Bumps each
9868
+ * record's `_v` and appends a ledger `op:'migration'` entry.
9869
+ */
9870
+ _applyCutoverTransform(transform: (doc: Record<string, unknown>) => Record<string, unknown>): Promise<number>;
9871
+ /** @internal Untracked delete body — call {@link delete}, not this. */
9872
+ private deleteInternal;
8993
9873
  /**
8994
9874
  * @internal — system-internal delete that bypasses user-facing
8995
- * delete hooks (`onDelete`, accounting-period guard, FK ref
8996
- * enforcer). Used by derivation tombstones (#144) and MV refresh
9875
+ * delete hooks (`onDelete`, FK ref enforcer). Used by derivation tombstones and MV refresh
8997
9876
  * (Dim 14 v2) — system housekeeping shouldn't trip user invariants
8998
9877
  * registered against the output collection. The ledger entry and
8999
9878
  * history snapshot still fire so backup integrity and time-travel
@@ -9005,7 +9884,7 @@ declare class Collection<T> {
9005
9884
  *
9006
9885
  * When a `txCtx` is supplied, the prior envelope is captured and
9007
9886
  * pushed onto `txCtx._executed` BEFORE the delete fires — mirrors
9008
- * the #133 rollback hardening for puts. Callers outside a
9887
+ * the rollback hardening for puts. Callers outside a
9009
9888
  * multi-record transaction pass `null` and skip the tracking.
9010
9889
  *
9011
9890
  * Amendment composition: if `_internalDelete` runs while a vault's
@@ -9032,7 +9911,7 @@ declare class Collection<T> {
9032
9911
  private _doDelete;
9033
9912
  /**
9034
9913
  * Cascade deletes of array-shape derived rows when a source row is
9035
- * deleted (#200). Reads each registered strategy's fanout sidecar
9914
+ * deleted. Reads each registered strategy's fanout sidecar
9036
9915
  * for this source id, deletes every listed derived row, then
9037
9916
  * deletes the sidecar itself.
9038
9917
  *
@@ -9043,8 +9922,8 @@ declare class Collection<T> {
9043
9922
  */
9044
9923
  private dispatchArrayDerivationsOnDelete;
9045
9924
  /**
9046
- * Mirror of {@link dispatchMaterializedViews} for the delete path
9047
- * (#181). No record content is available (it's gone), so the
9925
+ * Mirror of {@link dispatchMaterializedViews} for the delete path.
9926
+ * No record content is available (it's gone), so the
9048
9927
  * `_materializedFrom` skip used by the put-side dispatch doesn't
9049
9928
  * apply here — instead, the recursion guard is the `internal` gate
9050
9929
  * at the `_doDelete` call site above.
@@ -9126,7 +10005,7 @@ declare class Collection<T> {
9126
10005
  * the filtered records directly (the API). Prefer the chainable
9127
10006
  * form for new code.
9128
10007
  *
9129
- * **Lazy-MV gap (#157):** `query()` is synchronous and does NOT
10008
+ * **Lazy-MV gap:** `query()` is synchronous and does NOT
9130
10009
  * trigger lazy materialized-view resolve-on-read. If this
9131
10010
  * collection is a lazy MV's output and the MV is currently stale,
9132
10011
  * `query().toArray()` returns the pre-stale snapshot. To force a
@@ -9287,7 +10166,7 @@ declare class Collection<T> {
9287
10166
  * .aggregate({ total: sum('amount'), n: count() })
9288
10167
  * ```
9289
10168
  *
9290
- * **Lazy-MV gap (#157):** `scan()` is synchronous-build and does
10169
+ * **Lazy-MV gap:** `scan()` is synchronous-build and does
9291
10170
  * NOT trigger lazy materialized-view resolve-on-read. For lazy
9292
10171
  * MVs, call `list()` (which DOES resolve) or `vault.refreshView(name)`
9293
10172
  * before scanning. Same shape as the `query()` limitation.
@@ -9326,6 +10205,15 @@ declare class Collection<T> {
9326
10205
  * gone before the tx and the revert deleted it again).
9327
10206
  */
9328
10207
  _invalidateCacheEntry(id: string): Promise<void>;
10208
+ /**
10209
+ * Apply a peer tab's committed write to THIS tab's in-memory view:
10210
+ * re-read the (already-persisted) envelope from the shared store + refresh
10211
+ * cache/indexes, then emit a `change` event so reactive consumers re-render.
10212
+ * Never writes to the store and never fires write hooks, so it cannot loop.
10213
+ */
10214
+ _applyRemoteChange(id: string, action: 'put' | 'delete'): Promise<void>;
10215
+ /** @internal — the current in-memory record without a store read (for conflict capture). */
10216
+ _peekCached(id: string): T | null;
9329
10217
  private ensureHydrated;
9330
10218
  /** Hydrate from a pre-loaded snapshot (used by Vault). */
9331
10219
  hydrateFromSnapshot(records: Record<string, EncryptedEnvelope>): Promise<void>;
@@ -9340,6 +10228,11 @@ declare class Collection<T> {
9340
10228
  * 1K–50K records this completes in single-digit milliseconds.
9341
10229
  */
9342
10230
  private rebuildEagerIndexesFromCache;
10231
+ /**
10232
+ * Rebuild unique-constraint maps from the current in-memory cache.
10233
+ * Called after any bulk hydration alongside `rebuildEagerIndexesFromCache`.
10234
+ */
10235
+ private rebuildUniqueConstraintsFromCache;
9343
10236
  /**
9344
10237
  * Rebuild every declared index from scratch.
9345
10238
  *
@@ -9751,6 +10644,7 @@ interface ShadowStrategy {
9751
10644
  */
9752
10645
  interface TxStrategy {
9753
10646
  runTransaction<T>(db: Noydb, fn: (tx: TxContext) => Promise<T> | T, options?: AmendmentTxOptions): Promise<T>;
10647
+ runDryRun(db: Noydb, fn: (tx: TxContext) => unknown): Promise<DryRunResult>;
9754
10648
  }
9755
10649
 
9756
10650
  /**
@@ -9881,7 +10775,7 @@ interface SessionStrategy {
9881
10775
  }
9882
10776
 
9883
10777
  /**
9884
- * Managed-passphrase mode — issue #14, rubber-hose-resistant vaults.
10778
+ * Managed-passphrase mode — rubber-hose-resistant vaults.
9885
10779
  *
9886
10780
  * A vault mode where the passphrase is machine-generated and never
9887
10781
  * exposed to the user, sealed under a developer-provided
@@ -9897,6 +10791,18 @@ interface SessionStrategy {
9897
10791
  * - {@link MemorySealingKeyProvider} — in-memory test provider; uses
9898
10792
  * a deterministic per-instance "key" so two providers with
9899
10793
  * different ids cannot unseal each other's outputs.
10794
+ * - {@link RecipientHint} — public material a sender uses to seal
10795
+ * plaintext for a specific recipient; published by
10796
+ * {@link RecipientSealer.publishRecipientHint} and transported
10797
+ * out-of-band to the sender before bundle writes.
10798
+ * - {@link RecipientSealer} — interface for asymmetric/granted
10799
+ * providers that support recipient-target sealing (RSA-OAEP,
10800
+ * cloud-KMS asymmetric, etc.); distinct from self-only
10801
+ * {@link SealingKeyProvider} (macOS Keychain, WebAuthn-PRF).
10802
+ * - {@link MemoryRecipientSealer} — in-process reference
10803
+ * implementation of both `RecipientSealer` and
10804
+ * `SealingKeyProvider` using real WebCrypto RSA-OAEP + AES-GCM;
10805
+ * safe for tests and same-process sender/recipient scenarios.
9900
10806
  * - {@link loadSealedPassphrase} / {@link saveSealedPassphrase} —
9901
10807
  * plaintext envelope storage at `_meta/sealed-passphrase`.
9902
10808
  * Mirrors the `_meta/handle` and `_meta/public-envelope` AES-
@@ -9908,9 +10814,9 @@ interface SessionStrategy {
9908
10814
  * Returns the plaintext passphrase string that the rest of the
9909
10815
  * `createNoydb` keyring path consumes.
9910
10816
  *
9911
- * Slice 1 of #14. Deferred to follow-ups:
10817
+ * Deferred to follow-ups:
9912
10818
  * - Block `rotate-passphrase` policy gate under managed mode.
9913
- * - Mandatory strong-recovery enforcement (depends on #10).
10819
+ * - Mandatory strong-recovery enforcement.
9914
10820
  * - Recovery flow under managed mode (generates fresh sealed phrase).
9915
10821
  *
9916
10822
  * @see docs/subsystems/session-tiers.md → Managed-passphrase mode
@@ -9977,6 +10883,78 @@ declare class MemorySealingKeyProvider implements SealingKeyProvider {
9977
10883
  seal(passphrase: Uint8Array): Promise<Uint8Array>;
9978
10884
  unseal(sealed: Uint8Array): Promise<Uint8Array>;
9979
10885
  }
10886
+ /**
10887
+ * Public material a sender uses to seal-for-this-recipient. Published by
10888
+ * a recipient's RecipientSealer; transported to the sender out-of-band
10889
+ * (email, S3, in-app message). The sender obtains the hint, supplies it
10890
+ * to writeNoydbBundle's sealedCredentials.perUser[userId].hint, and the
10891
+ * hub seals each user's credential against it. Per foundation §11.4.
10892
+ */
10893
+ type RecipientHint = {
10894
+ readonly v: 1;
10895
+ /** Recipient's provider id; matches the SealedAutoUnlockEntry.pid they'll unseal under. */
10896
+ readonly pid: string;
10897
+ /** Algorithm the sender uses to produce the seal. Slice 1 ships RSA-OAEP-SHA256 only. */
10898
+ readonly alg: 'rsa-oaep-sha256';
10899
+ /** Public material — alg-specific. For 'rsa-oaep-sha256': { publicKeyPem: string }. */
10900
+ readonly material: Readonly<Record<string, unknown>>;
10901
+ };
10902
+ /**
10903
+ * Handover-capable provider. Implemented additionally by asymmetric/granted
10904
+ * providers (cloud-KMS asymmetric, Azure RSA Key Vault, AWS KMS with grant).
10905
+ * Self-only providers (macOS Keychain, env-var, WebAuthn-PRF) do NOT
10906
+ * implement this — the §11.2 capability matrix lives in the type system.
10907
+ *
10908
+ * Per foundation §11.4. A function that requires recipient-target sealing
10909
+ * takes `RecipientSealer`, not `SealingKeyProvider` — the compiler rejects
10910
+ * passing a self-only provider at the spec site.
10911
+ */
10912
+ interface RecipientSealer {
10913
+ readonly id: string;
10914
+ /** Produce hint material a sender uses to seal-for-this-recipient. */
10915
+ publishRecipientHint(): Promise<RecipientHint>;
10916
+ /**
10917
+ * Seal plaintext for the recipient described by `hint`. Returns opaque
10918
+ * bytes — same contract as `SealingKeyProvider.seal()`. The bundle
10919
+ * layer base64-encodes the bytes into `SealedAutoUnlockEntry.sealed`
10920
+ * without inspecting them.
10921
+ */
10922
+ sealForRecipient(plaintext: Uint8Array, hint: RecipientHint): Promise<Uint8Array>;
10923
+ }
10924
+ /**
10925
+ * Reference implementation of `RecipientSealer` + `SealingKeyProvider`.
10926
+ * Uses WebCrypto RSA-OAEP-SHA256 (2048-bit) to wrap a fresh 32-byte
10927
+ * AES-GCM CEK, AES-GCM-encrypts plaintext under it, and packs the
10928
+ * result into a self-describing TLV:
10929
+ *
10930
+ * byte 0 : version (0x01)
10931
+ * bytes 1..256 : RSA-OAEP-wrapped CEK (fixed 256 bytes at RSA-2048)
10932
+ * bytes 257..268: AES-GCM IV (12 bytes)
10933
+ * bytes 269.. : AES-GCM ciphertext ‖ 16-byte tag
10934
+ *
10935
+ * Implements BOTH interfaces. `seal(plaintext)` (self-target) is just
10936
+ * `sealForRecipient(plaintext, this own hint)` — same TLV. Convenient
10937
+ * for tests where one provider plays both ends. Real cloud providers
10938
+ * (`at-aws-kms`, etc.) will pick their own internal layouts; the only
10939
+ * contract is round-trip identity.
10940
+ *
10941
+ * SAFE for production within its scope — the cryptography is real
10942
+ * (RSA-OAEP + AES-GCM via WebCrypto), but the keypair lives in-process
10943
+ * and is regenerated on every construction. Not suitable as a managed
10944
+ * keychain; use it for tests and for shipping bundles where the
10945
+ * recipient instance lives in the same process as the sender (rare).
10946
+ */
10947
+ declare class MemoryRecipientSealer implements SealingKeyProvider, RecipientSealer {
10948
+ readonly id: string;
10949
+ private readonly keypair;
10950
+ constructor(opts: {
10951
+ id: string;
10952
+ });
10953
+ publishRecipientHint(): Promise<RecipientHint>;
10954
+ sealForRecipient(plaintext: Uint8Array, hint: RecipientHint): Promise<Uint8Array>;
10955
+ seal(plaintext: Uint8Array): Promise<Uint8Array>;
10956
+ unseal(bytes: Uint8Array): Promise<Uint8Array>;
10957
+ }
9980
10958
  /** Reserved id for the managed-passphrase envelope under `_meta`. */
9981
10959
  declare const SEALED_PASSPHRASE_RECORD_ID: "sealed-passphrase";
9982
10960
  /** Plaintext payload stored inside the `_meta/sealed-passphrase` envelope. */
@@ -9998,12 +10976,12 @@ interface SealedPassphrase {
9998
10976
  *
9999
10977
  * v1 shape (this release): `{ v: 1, _noydb_sealed: 1, pid, payload }`.
10000
10978
  *
10001
- * Legacy shape (pre.14, pre.15): `{ _noydb_sealed: 1, providerId, sealed }`
10979
+ * Legacy shape (earlier releases): `{ _noydb_sealed: 1, providerId, sealed }`
10002
10980
  * — accepted on read for backwards compatibility; never produced on
10003
10981
  * write going forward.
10004
10982
  */
10005
10983
  interface SealedEnvelope {
10006
- /** Envelope schema version. v1 is the shape shipped in pre.16. */
10984
+ /** Envelope schema version. v1 is the current shape. */
10007
10985
  readonly v: 1;
10008
10986
  /** Magic marker for forensics + legacy-shape detection. */
10009
10987
  readonly _noydb_sealed: 1;
@@ -10017,9 +10995,9 @@ interface SealedEnvelope {
10017
10995
  * in-memory {@link SealedPassphrase} representation. Accepts both:
10018
10996
  *
10019
10997
  * 1. v1 wire format `{ v: 1, _noydb_sealed: 1, pid, payload }` —
10020
- * the shape produced from pre.16 onward.
10998
+ * the current shape.
10021
10999
  * 2. Legacy wire format `{ _noydb_sealed: 1, providerId, sealed }` —
10022
- * the shape produced in pre.14/pre.15. Read-only; never written
11000
+ * read-only; never written
10023
11001
  * going forward.
10024
11002
  *
10025
11003
  * Returns `undefined` for any input that doesn't match either shape,
@@ -10404,9 +11382,9 @@ interface ImportCapability {
10404
11382
  */
10405
11383
  type VaultPolicyOnDisk = Record<string, unknown>;
10406
11384
  /**
10407
- * Recovery profile enrolled at vault creation (issue #10).
11385
+ * Recovery profile enrolled at vault creation.
10408
11386
  *
10409
- * - `paper` — `on-recovery` codes (the only end-to-end profile in v0.1.0-pre.5).
11387
+ * - `paper` — `on-recovery` codes (the standard end-to-end profile).
10410
11388
  * - `shamir` / `multi-channel` / `admin-mediated` — API surface ships;
10411
11389
  * per-profile dispatch lands in follow-up issues. Calling
10412
11390
  * `db.recoverPassphrase` against these throws
@@ -10469,7 +11447,7 @@ interface KeyringAuthenticatorBase {
10469
11447
  * extractable KEK from its own credential — WebAuthn (PRF-derived
10470
11448
  * wrapping key) and split-key OIDC.
10471
11449
  *
10472
- * `wrapKind` is optional/absent on slots written before pre.8 — those
11450
+ * `wrapKind` is optional/absent on older slots — those
10473
11451
  * legacy slots are treated as wrap-KEK by default at unlock time.
10474
11452
  */
10475
11453
  interface KeyringAuthenticatorWrappingKEK extends KeyringAuthenticatorBase {
@@ -10532,11 +11510,11 @@ interface KeyringFile {
10532
11510
  readonly granted_by: string;
10533
11511
  /**
10534
11512
  * Passphrase canary — base64 AES-KW-wrapped form of a known constant
10535
- * 256-bit value, wrapped under the keyring's KEK (#113).
11513
+ * 256-bit value, wrapped under the keyring's KEK.
10536
11514
  *
10537
- * Optional: pre-#113 keyrings load with no canary and fall back to
10538
- * the multi-DEK corruption heuristic from #82. Keyrings written after
10539
- * #113 carry one and let `loadKeyring` distinguish wrong-passphrase
11515
+ * Optional: older keyrings load with no canary and fall back to
11516
+ * the multi-DEK corruption heuristic. Newer keyrings
11517
+ * carry one and let `loadKeyring` distinguish wrong-passphrase
10540
11518
  * from corruption even when ALL DEKs (including a single-DEK keyring's
10541
11519
  * sole DEK) are corrupted.
10542
11520
  *
@@ -10758,6 +11736,23 @@ interface Conflict {
10758
11736
  */
10759
11737
  readonly resolve?: (winner: EncryptedEnvelope | null) => void;
10760
11738
  }
11739
+ /**
11740
+ * A same-device cross-tab write conflict: another tab overwrote a
11741
+ * document this tab had written, having diverged from an older base. Records
11742
+ * are decrypted (cross-tab handlers reconcile in plaintext). `base` is the
11743
+ * common ancestor from history, or null when history is unavailable.
11744
+ */
11745
+ interface WriteConflict {
11746
+ readonly vault: string;
11747
+ readonly collection: string;
11748
+ readonly docId: string;
11749
+ readonly local: unknown;
11750
+ readonly remote: unknown;
11751
+ readonly base: unknown;
11752
+ readonly localVersion: number;
11753
+ readonly remoteVersion: number;
11754
+ readonly baseVersion: number;
11755
+ }
10761
11756
  type ConflictStrategy = 'local-wins' | 'remote-wins' | 'version' | ((conflict: Conflict) => 'local' | 'remote');
10762
11757
  /**
10763
11758
  * Collection-level conflict policy.
@@ -10844,9 +11839,20 @@ interface ChangeEvent {
10844
11839
  interface NoydbEventMap {
10845
11840
  'change': ChangeEvent;
10846
11841
  'error': Error;
11842
+ /**
11843
+ * Same-instance signal that this vault's schema-fence state changed.
11844
+ * For UI integration. Cross-client coordination goes
11845
+ * through the store, not this event.
11846
+ */
11847
+ 'schema:fence-changed': {
11848
+ vault: string;
11849
+ currentSchemaVersion: number;
11850
+ fenceState: 'normal' | 'draining' | 'migrating' | 'complete';
11851
+ };
10847
11852
  'sync:push': PushResult;
10848
11853
  'sync:pull': PullResult;
10849
11854
  'sync:conflict': Conflict;
11855
+ 'write:conflict': WriteConflict;
10850
11856
  'sync:online': void;
10851
11857
  'sync:offline': void;
10852
11858
  'sync:backup-error': {
@@ -10941,7 +11947,7 @@ interface GrantOptions {
10941
11947
  readonly initialProfile?: unknown;
10942
11948
  }
10943
11949
  /**
10944
- * Caller payload for `db.updateUser` (#54). Mutate one or more
11950
+ * Caller payload for `db.updateUser`. Mutate one or more
10945
11951
  * identity fields on an existing keyring without rotating any keys.
10946
11952
  *
10947
11953
  * `role`, `displayName`, and `permissions` live in the plaintext header
@@ -10955,7 +11961,7 @@ interface GrantOptions {
10955
11961
  * `null` on `displayName` clears the field (stored as the empty string;
10956
11962
  * UI consumers typically render the empty case by falling back to the
10957
11963
  * user id). `undefined` / absent leaves the field untouched. Mirrors
10958
- * the `null`-as-clear convention `UserApi.updateMe` uses (#57).
11964
+ * the `null`-as-clear convention `UserApi.updateMe` uses.
10959
11965
  *
10960
11966
  * `permissions`, however, is a **full replacement** at the map level —
10961
11967
  * passing `{ invoices: 'rw' }` REPLACES the entire permissions map,
@@ -10969,8 +11975,6 @@ interface GrantOptions {
10969
11975
  * do anything. Non-admin callers (operator/viewer/client) cannot call
10970
11976
  * `db.updateUser` at all — for self-displayName changes, use
10971
11977
  * `vault.user.updateMe` (the user-envelope API).
10972
- *
10973
- * @see #54
10974
11978
  */
10975
11979
  interface UpdateUserOptions {
10976
11980
  readonly userId: string;
@@ -11546,6 +12550,13 @@ interface NoydbOptions {
11546
12550
  * @internal
11547
12551
  */
11548
12552
  readonly syncStrategy?: SyncStrategy;
12553
+ /**
12554
+ * Tree-shake seam — optional snapshot-lifecycle subsystem. Pass
12555
+ * `withSnapshots({ store })` from `@noy-db/hub/snapshots` to enable
12556
+ * `db.snapshot()`, `db.listSnapshots()`, and `db.restoreSnapshot()`.
12557
+ * When omitted, all three methods throw with a pointer at the subpath.
12558
+ */
12559
+ readonly snapshotStrategy?: SnapshotStrategy;
11549
12560
  /**
11550
12561
  * Optional guard strategies — collection-level write guards. Each
11551
12562
  * handle is the output of `withGuard()` from `@noy-db/hub/guards`.
@@ -11562,7 +12573,7 @@ interface NoydbOptions {
11562
12573
  */
11563
12574
  readonly derivationStrategies?: ReadonlyArray<DerivationStrategyHandle>;
11564
12575
  /**
11565
- * Optional materialized-view strategies (#143, foundation in #150).
12576
+ * Optional materialized-view strategies.
11566
12577
  * Each handle returned by `withMaterializedView()` from
11567
12578
  * `@noy-db/hub/materialized-views`. The vault runs unified cycle
11568
12579
  * detection across the MV + derivation graphs at `openVault`; a
@@ -11570,7 +12581,7 @@ interface NoydbOptions {
11570
12581
  */
11571
12582
  readonly materializedViewStrategies?: ReadonlyArray<MaterializedViewStrategyHandle>;
11572
12583
  /**
11573
- * Optional overlay strategies (#154). Each handle returned by
12584
+ * Optional overlay strategies. Each handle returned by
11574
12585
  * `withOverlayedView()` from `@noy-db/hub/overlay-views`. The vault
11575
12586
  * validates name uniqueness + base concreteness + overlay
11576
12587
  * availability at `openVault`; a clash throws one of the
@@ -11623,7 +12634,7 @@ interface NoydbOptions {
11623
12634
  */
11624
12635
  readonly getKeyring?: (vault: string) => Promise<UnlockedKeyring>;
11625
12636
  /**
11626
- * Passphrase mode (#14). Default `'standard'`.
12637
+ * Passphrase mode. Default `'standard'`.
11627
12638
  *
11628
12639
  * - `'standard'` — the legacy flow. `secret` supplies the
11629
12640
  * plaintext passphrase, the user knows it, and the policy gate
@@ -11684,14 +12695,14 @@ interface NoydbOptions {
11684
12695
  readonly sessionPolicy?: SessionPolicy;
11685
12696
  /**
11686
12697
  * Validate passphrase strength against the phrase format
11687
- * (`@noy-db/hub` issue #7) on first-time keyring creation. When
12698
+ * on first-time keyring creation. When
11688
12699
  * `true`, weak phrases throw {@link WeakPassphraseError} from
11689
12700
  * `createNoydb()` / `db.rotatePassphrase()`. Default: `false` for
11690
- * back-compat in v0.1.x; planned to flip to `true` at v1.0.
12701
+ * back-compat; planned to flip to `true` in a future major release.
11691
12702
  */
11692
12703
  readonly validatePassphrase?: boolean;
11693
12704
  /**
11694
- * Vault-level policy gate document (issue #9). When present, the hub
12705
+ * Vault-level policy gate document. When present, the hub
11695
12706
  * persists the merged policy at `_meta/policy` on first-time vault
11696
12707
  * creation and gates sensitive operations (`db.rotatePassphrase`,
11697
12708
  * `db.export*`, …) against it. Omitted ⇒ the engine uses
@@ -11707,14 +12718,14 @@ interface NoydbOptions {
11707
12718
  */
11708
12719
  readonly policy?: VaultPolicy;
11709
12720
  /**
11710
- * Mandatory recovery profile enrollment (issue #10). Vaults with
12721
+ * Mandatory recovery profile enrollment. Vaults with
11711
12722
  * `recover-passphrase` enabled MUST register at least one profile
11712
12723
  * before being production-ready, otherwise `createNoydb()` throws
11713
12724
  * {@link RecoveryNotEnrolledError}. Set
11714
12725
  * `policy.gates['recover-passphrase'].enabled = false` to
11715
12726
  * deliberately opt out of recovery (passphrase loss = data loss).
11716
12727
  *
11717
- * v0.1.0-pre.5 supports the `'paper'` profile end-to-end. Other
12728
+ * The `'paper'` profile is supported end-to-end. Other
11718
12729
  * profiles ship the API shape and throw
11719
12730
  * {@link RecoveryProfileNotImplementedError} during use.
11720
12731
  */
@@ -11722,9 +12733,9 @@ interface NoydbOptions {
11722
12733
  /**
11723
12734
  * When `true`, `createNoydb` rejects vaults with no recovery
11724
12735
  * entries persisted (per the spec's mandatory-enrollment
11725
- * requirement). Default `false` for v0.1.x back-compat; planned to
11726
- * flip to `true` at v1.0. Apps in regulated environments should
11727
- * turn this on now.
12736
+ * requirement). Default `false` for back-compat; planned to
12737
+ * flip to `true` in a future major release. Apps in regulated
12738
+ * environments should turn this on now.
11728
12739
  */
11729
12740
  readonly requireRecovery?: boolean;
11730
12741
  /**
@@ -11851,4 +12862,4 @@ interface DeleteManyResult {
11851
12862
  }>;
11852
12863
  }
11853
12864
 
11854
- export { type ConsentAuditEntry as $, type BlobObject as A, type BlobStrategy as B, type BlobPutOptions as C, DICT_COLLECTION_PREFIX as D, type BlobResponseOptions as E, BlobSet as F, type BlobStrategyOpenArgs as G, type CompactRunOptions as H, type I18nStrategy as I, type CompactionContext as J, type CompactionResult as K, DEFAULT_CHUNK_SIZE as L, EXPORT_AUDIT_COLLECTION as M, ExportBlobsAbortedError as N, type ExportBlobsAuditEntry as O, PolicyEnforcer as P, type ExportBlobsHandle as Q, type ExportBlobsOptions as R, type SessionStrategy as S, type ExportedBlob as T, type SlotInfo as U, type SlotRecord as V, type VersionRecord as W, createExportBlobsHandle as X, runCompaction as Y, type ConsentStrategy as Z, CONSENT_AUDIT_COLLECTION as _, type DictEntry as a, VaultInstant as a$, type ConsentAuditFilter as a0, type ConsentContext as a1, type ConsentOp as a2, loadConsentEntries as a3, writeConsentEntry as a4, type PeriodsStrategy as a5, type CarryForwardContext as a6, type ClosePeriodOptions as a7, type OpenPeriodOptions as a8, PERIODS_COLLECTION as a9, type DerivationStrategyHandle as aA, type DerivedFromMeta as aB, type OutputSpec as aC, type RecordOutputSpec as aD, type MaterializedViewStrategy as aE, type MaterializedViewStrategyHandle as aF, type OverlayedViewStrategy as aG, Collection as aH, OverlayedViewRegistry as aI, type OverlayedViewStrategyHandle as aJ, type SyncStrategy as aK, type Role as aL, type UnlockedKeyring as aM, type HistoryStrategy as aN, type NoydbStore as aO, type HistoryOptions as aP, type EncryptedEnvelope as aQ, type PruneOptions as aR, type AppendInput as aS, type ChangeType as aT, CollectionInstant as aU, type DiffEntry as aV, type JsonPatch as aW, type JsonPatchOp as aX, type LedgerEntry as aY, LedgerStore as aZ, type VaultEngine as a_, type PeriodRecord as aa, type ReadOnlyCollection as ab, appendPeriodLedgerEntry as ac, assertTsWritable as ad, chainAnchor as ae, loadPeriods as af, validatePeriodName as ag, type GuardStrategy as ah, type GuardChange as ai, type GuardContext as aj, GuardRegistry as ak, type GuardStrategyHandle as al, ReadOnlyVaultFacade as am, type ShadowStrategy as an, CollectionFrame as ao, VaultFrame as ap, type TxStrategy as aq, type AmendmentTxOptions as ar, TxCollection as as, TxContext as at, TxVault as au, runTransaction as av, type DerivationStrategy as aw, type DerivationContext as ax, type ArrayOutputSpec as ay, DerivationRegistry as az, type DictKeyDescriptor as b, type FactorRequirement as b$, type VerifyResult as b0, applyPatch as b1, canonicalJson as b2, computePatch as b3, diff as b4, formatDiff as b5, hashEntry as b6, paddedIndex as b7, parseIndex as b8, sha256Hex as b9, type CollectionDescriptor as bA, type CollectionStats as bB, type Conflict as bC, type ConflictPolicy as bD, type ConflictStrategy as bE, type CrossTierAccessEvent as bF, DEFAULT_PUBLIC_ENVELOPE_SCHEMA as bG, DELEGATIONS_COLLECTION as bH, type DeepPartial as bI, type DeepPartialOrNull as bJ, type DelegationToken as bK, type DeleteManyResult as bL, type DerivationDescriptor as bM, type DirtyEntry as bN, type DumpSchemaOptions as bO, ELEVATION_AUDIT_COLLECTION as bP, ElevatedHandle as bQ, type EnrollAuthenticatorOptions as bR, type EnrollAuthenticatorWrappingDEKsOptions as bS, type EnrollAuthenticatorWrappingKEKOptions as bT, type EnrollRecoveryResult as bU, type ExportCapability as bV, type ExportChunk as bW, type ExportFormat as bX, type ExportStreamOptions as bY, type FactorKind as bZ, type FactorProofBundle as b_, type MVQueryContext as ba, type RegisteredMV as bb, MaterializedViewRegistry as bc, type MaterializedFromMeta as bd, type MaterializedViewOutput as be, type UnionSource as bf, type UserEnvelope as bg, type PublicEnvelope as bh, type GateName as bi, type GatePolicy as bj, type VaultPolicy as bk, type ActiveTier as bl, type FactorProof as bm, type PersistedSchemaEnvelope as bn, type DirectoryConfig as bo, type UserVisibility as bp, Vault as bq, type AccessibleVault as br, BUNDLE_STORE_POLICY as bs, type BuiltInGateName as bt, type BundleRecipient as bu, type CacheOptions as bv, type CacheStats as bw, type ChangeEvent as bx, type CollectionChangeEvent as by, type CollectionConflictResolver as bz, DictionaryHandle as c, type PutManyItemOptions as c$, type FieldDescriptor as c0, type FieldSource as c1, type GhostRecord as c2, type GrantOptions as c3, type HistoryConfig as c4, type HistoryEntry as c5, INDEXED_STORE_POLICY as c6, type ImportCapability as c7, type InferOutput as c8, type InternalCollectionStats as c9, type NoydbBundleStore as cA, type NoydbEventMap as cB, type NoydbOptions as cC, type OverlayViewDescriptor as cD, PUBLIC_ENVELOPE_FIELDS as cE, type PaperRecoveryDoc as cF, type PaperRecoveryEntry as cG, type PassphrasePolicy as cH, type PassphraseValidationResult as cI, type Permission as cJ, type Permissions as cK, type PersistedSchemaKind as cL, type PlaintextTranslatorContext as cM, type PlaintextTranslatorFn as cN, PresenceHandle as cO, type PresencePeer as cP, type PublicEnvelopeField as cQ, type PublicEnvelopeSchema as cR, type PublicEnvelopeText as cS, type PullMode as cT, type PullOptions as cU, type PullPolicy as cV, type PullResult as cW, type PushMode as cX, type PushOptions as cY, type PushPolicy as cZ, type PushResult as c_, type IssueDelegationOptions as ca, type IssueMagicLinkGrantOptions as cb, type KeyringAuthenticator as cc, type KeyringAuthenticatorWrappingDEKs as cd, type KeyringAuthenticatorWrappingKEK as ce, type KeyringFile as cf, type ListAccessibleVaultsOptions as cg, type ListPageResult as ch, type ListUsersOptions as ci, type LiveUserEnvelope as cj, type LocaleReadOptions as ck, Lru as cl, type LruOptions as cm, type LruStats as cn, MAGIC_LINK_CONTENT_INFO_PREFIX as co, MAGIC_LINK_GRANTS_COLLECTION as cp, MAGIC_LINK_KEK_INFO_PREFIX as cq, type MagicLinkGrantPayload as cr, type MagicLinkGrantRecord as cs, type MaterializedViewDescriptor as ct, MemorySealingKeyProvider as cu, NOYDB_BACKUP_VERSION as cv, NOYDB_FORMAT_VERSION as cw, NOYDB_KEYRING_VERSION as cx, NOYDB_SYNC_VERSION as cy, Noydb as cz, type DictionaryOptions as d, type WeakPassphraseReason as d$, type PutManyOptions as d0, type PutManyResult as d1, type QueryAcrossOptions as d2, type QueryAcrossResult as d3, type QuickUnlockState as d4, QuickUnlockStore as d5, type ReAuthOperation as d6, type RecoverPassphraseInput as d7, type RecoverPassphraseResult as d8, type RecoverUserOptions as d9, type SyncPolicy as dA, SyncScheduler as dB, type SyncSchedulerStatus as dC, type SyncStatus as dD, type SyncTarget as dE, type SyncTargetRole as dF, SyncTransaction as dG, type SyncTransactionResult as dH, type TierMode as dI, type TranslatorAuditEntry as dJ, type TxOp as dK, USER_ENVELOPE_COLLECTION as dL, USER_ENVELOPE_MAX_BYTES as dM, type Unsubscribe as dN, type UpdateAuthenticatorOptions as dO, type UpdateUserOptions as dP, UserApi as dQ, type UserEnvelopeCheckGate as dR, UserEnvelopeOversizedError as dS, type UserEnvelopePresented as dT, type UserInfo as dU, type VaultBackup as dV, type VaultPolicyOnDisk as dW, type VaultSchemaSnapshot as dX, type VaultSnapshot as dY, type WarningRules as dZ, WeakPassphraseError as d_, type RecoveryProof as da, type ResolvedPublicEnvelopeSchema as db, type RevokeOptions as dc, type RotatePassphraseInput as dd, type RotateRecoveryOptions as de, type RotateRecoveryResult as df, SEALED_PASSPHRASE_RECORD_ID as dg, type SealedEnvelope as dh, type SealedPassphrase as di, type SealingKeyProvider as dj, type SessionPolicy as dk, type SetPublicEnvelopeInput as dl, type ShamirRecoveryDoc as dm, type ShamirRecoveryEntry as dn, type ShamirRecoveryProvider as dp, type SlotRewrapCeremony as dq, type SlotRewrapContext as dr, type StandardSchemaV1 as ds, type StandardSchemaV1Issue as dt, type StandardSchemaV1SyncResult as du, type StoreAuth as dv, type StoreAuthKind as dw, type StoreCapabilities as dx, SyncEngine as dy, type SyncMetadata as dz, type I18nTextDescriptor as e, type WrappedDeksBlob as e0, assertStrongPassphrase as e1, buildRecipientKeyringFile as e2, burnPaperRecoveryEntry as e3, createNoydb as e4, createStore as e5, deriveMagicLinkContentKey as e6, enrollAuthenticator as e7, estimateEntropy as e8, evaluateExportCapability as e9, revokeDelegation as eA, revokeMagicLinkGrant as eB, savePaperRecoveryEntries as eC, saveSealedPassphrase as eD, saveShamirRecoveryEntries as eE, unwrapDeksFromBlob as eF, unwrapDeksFromPaperEntry as eG, unwrapDeksFromShamirEntry as eH, unwrapMagicLinkGrant as eI, validatePassphrase as eJ, validatePublicEnvelopeInput as eK, validateSchemaInput as eL, validateSchemaOutput as eM, writeMagicLinkGrant as eN, changeSecret as eO, createOwnerKeyring as eP, ensureCollectionDEK as eQ, grant as eR, loadKeyring as eS, persistKeyring as eT, revoke as eU, updateAuthenticator as eV, updateKeyringIdentity as eW, evaluateImportCapability as ea, findAuthenticator as eb, hasExportCapability as ec, hasImportCapability as ed, hasRecoveryEnrolled as ee, isMagicLinkGrantExpired as ef, isPublicEnvelope as eg, issueDelegation as eh, recoverPassphrase as ei, rotatePassphrase as ej, listMagicLinkGrants as ek, listUsers as el, listUsersWithEnvelopes as em, loadActiveDelegations as en, loadPaperRecoveryEntries as eo, loadSealedPassphrase as ep, loadShamirRecoveryEntries as eq, magicLinkGrantRecordId as er, mintPaperRecoveryEntry as es, mintShamirRecoveryEntry as et, mintWrappedDeksBlob as eu, parseSealedEnvelope as ev, readMagicLinkGrantRecord as ew, recoverUser as ex, removeAuthenticator as ey, resolveSchema as ez, type I18nTextOptions as f, applyI18nLocale as g, dictCollectionName as h, dictKey as i, i18nText as j, isDictCollectionName as k, isDictKeyDescriptor as l, isI18nTextDescriptor as m, createEnforcer as n, validateSessionPolicy as o, BLOB_CHUNKS_COLLECTION as p, BLOB_COLLECTION as q, resolveI18nText as r, BLOB_EVICTION_AUDIT_COLLECTION as s, BLOB_INDEX_COLLECTION as t, BLOB_SLOTS_PREFIX as u, validateI18nTextValue as v, BLOB_VERSIONS_PREFIX as w, type BlobEvictionEntry as x, type BlobFieldPolicy as y, type BlobFieldsConfig as z };
12865
+ export { type ExportBlobsAuditEntry as $, BLOB_COLLECTION as A, type BlobStrategy as B, BLOB_EVICTION_AUDIT_COLLECTION as C, DICT_COLLECTION_PREFIX as D, BLOB_INDEX_COLLECTION as E, BLOB_SLOTS_PREFIX as F, BLOB_VERSIONS_PREFIX as G, type BlobEvictionEntry as H, type I18nStrategy as I, type BlobFieldPolicy as J, type BlobFieldsConfig as K, type Layer as L, type BlobObject as M, type BlobPutOptions as N, type OnMissing as O, PolicyEnforcer as P, type BlobResponseOptions as Q, type ResolveI18nOptions as R, type ScriptWarning as S, BlobSet as T, type BlobStrategyOpenArgs as U, type CompactRunOptions as V, type CompactionContext as W, type CompactionResult as X, DEFAULT_CHUNK_SIZE as Y, EXPORT_AUDIT_COLLECTION as Z, ExportBlobsAbortedError as _, type DictEntry as a, type SyncStrategy as a$, type ExportBlobsHandle as a0, type ExportBlobsOptions as a1, type ExportedBlob as a2, type SlotInfo as a3, type SlotRecord as a4, type VersionRecord as a5, createExportBlobsHandle as a6, runCompaction as a7, type ConsentStrategy as a8, CONSENT_AUDIT_COLLECTION as a9, VaultFrame as aA, type NoydbBundleStore as aB, type RetentionPolicy as aC, type SnapshotPolicy as aD, type SnapshotStrategy as aE, type SnapshotMeta as aF, type SnapshotMode as aG, type TxStrategy as aH, type AmendmentTxOptions as aI, TxCollection as aJ, TxContext as aK, TxVault as aL, runTransaction as aM, type DerivationStrategy as aN, type DerivationContext as aO, type ArrayOutputSpec as aP, DerivationRegistry as aQ, type DerivationStrategyHandle as aR, type DerivedFromMeta as aS, type OutputSpec as aT, type RecordOutputSpec as aU, type MaterializedViewStrategy as aV, type MaterializedViewStrategyHandle as aW, type OverlayedViewStrategy as aX, Collection as aY, OverlayedViewRegistry as aZ, type OverlayedViewStrategyHandle as a_, type ConsentAuditEntry as aa, type ConsentAuditFilter as ab, type ConsentContext as ac, type ConsentOp as ad, loadConsentEntries as ae, writeConsentEntry as af, type PeriodsStrategy as ag, type CarryForwardContext as ah, type ClosePeriodOptions as ai, type OpenPeriodOptions as aj, PERIODS_COLLECTION as ak, type PeriodRecord as al, type ReadOnlyCollection as am, appendPeriodLedgerEntry as an, assertTsWritable as ao, chainAnchor as ap, loadPeriods as aq, validatePeriodName as ar, type GuardStrategy as as, type GuardChange as at, type GuardContext as au, GuardRegistry as av, type GuardStrategyHandle as aw, ReadOnlyVaultFacade as ax, type ShadowStrategy as ay, CollectionFrame as az, type DictKeyDescriptor as b, type CollectionStats as b$, type Role as b0, type UnlockedKeyring as b1, type HistoryStrategy as b2, type NoydbStore as b3, type HistoryOptions as b4, type EncryptedEnvelope as b5, type PruneOptions as b6, type AppendInput as b7, type ChangeType as b8, CollectionInstant as b9, type RegisteredMV as bA, MaterializedViewRegistry as bB, type MaterializedFromMeta as bC, type MaterializedViewOutput as bD, type UnionSource as bE, type UserEnvelope as bF, type GateName as bG, type GatePolicy as bH, type VaultPolicy as bI, type ActiveTier as bJ, type FactorProof as bK, type SchemaUpdateStrategy as bL, type TransformFn as bM, type PersistedSchemaEnvelope as bN, type UpdateDecision as bO, type DirectoryConfig as bP, type UserVisibility as bQ, type AccessibleVault as bR, type AffectedDocument as bS, BUNDLE_STORE_POLICY as bT, type BuiltInGateName as bU, type CacheOptions as bV, type CacheStats as bW, type ChangeEvent as bX, type CollectionChangeEvent as bY, type CollectionConflictResolver as bZ, type CollectionDescriptor as b_, type DiffEntry as ba, type JsonPatch as bb, type JsonPatchOp as bc, type LedgerEntry as bd, LedgerStore as be, type VaultEngine as bf, VaultInstant as bg, type VerifyResult as bh, applyPatch as bi, canonicalJson as bj, computePatch as bk, diff as bl, formatDiff as bm, hashEntry as bn, paddedIndex as bo, parseIndex as bp, sha256Hex as bq, type PublicEnvelope as br, type SealingKeyProvider as bs, type BundleRecipient as bt, type RecipientSealer as bu, type RecipientHint as bv, Vault as bw, type RecoveryEnrollmentInput as bx, type ShamirRecoveryProvider as by, type MVQueryContext as bz, DictionaryHandle as c, NOYDB_BACKUP_VERSION as c$, type Conflict as c0, type ConflictPolicy as c1, type ConflictStrategy as c2, type CrossTierAccessEvent as c3, DEFAULT_PUBLIC_ENVELOPE_SCHEMA as c4, DELEGATIONS_COLLECTION as c5, type DeepPartial as c6, type DeepPartialOrNull as c7, type DelegationToken as c8, type DeleteManyResult as c9, type HistoryEntry as cA, INDEXED_STORE_POLICY as cB, type ImportCapability as cC, type InferOutput as cD, type InternalCollectionStats as cE, type IssueDelegationOptions as cF, type IssueMagicLinkGrantOptions as cG, type KeyringAuthenticator as cH, type KeyringAuthenticatorWrappingDEKs as cI, type KeyringAuthenticatorWrappingKEK as cJ, type KeyringFile as cK, type ListAccessibleVaultsOptions as cL, type ListPageResult as cM, type ListUsersOptions as cN, type LiveUserEnvelope as cO, type LocaleReadOptions as cP, Lru as cQ, type LruOptions as cR, type LruStats as cS, MAGIC_LINK_CONTENT_INFO_PREFIX as cT, MAGIC_LINK_GRANTS_COLLECTION as cU, MAGIC_LINK_KEK_INFO_PREFIX as cV, type MagicLinkGrantPayload as cW, type MagicLinkGrantRecord as cX, type MaterializedViewDescriptor as cY, MemoryRecipientSealer as cZ, MemorySealingKeyProvider as c_, type DerivationDescriptor as ca, type DirtyEntry as cb, type DryRunResult as cc, type DumpSchemaOptions as cd, ELEVATION_AUDIT_COLLECTION as ce, ElevatedHandle as cf, type EnrollAuthenticatorOptions as cg, type EnrollAuthenticatorWrappingDEKsOptions as ch, type EnrollAuthenticatorWrappingKEKOptions as ci, type EnrollRecoveryResult as cj, type ExportCapability as ck, type ExportChunk as cl, type ExportFormat as cm, type ExportStreamOptions as cn, type FactorKind as co, type FactorProofBundle as cp, type FactorRequirement as cq, type FenceDoc as cr, type FenceState as cs, type FieldChange as ct, type FieldDescriptor as cu, type FieldSource as cv, type GhostRecord as cw, type GrantOptions as cx, type GuardViolation as cy, type HistoryConfig as cz, type DictionaryOptions as d, type StoreAuthKind as d$, NOYDB_FORMAT_VERSION as d0, NOYDB_KEYRING_VERSION as d1, NOYDB_SYNC_VERSION as d2, Noydb as d3, type NoydbEventMap as d4, type NoydbOptions as d5, type OverlayViewDescriptor as d6, PUBLIC_ENVELOPE_FIELDS as d7, type PaperRecoveryDoc as d8, type PaperRecoveryEntry as d9, type QuickUnlockState as dA, QuickUnlockStore as dB, type ReAuthOperation as dC, type RecoverPassphraseInput as dD, type RecoverPassphraseResult as dE, type RecoverUserOptions as dF, type RecoveryProof as dG, type ResolvedPublicEnvelopeSchema as dH, type RevokeOptions as dI, type RotatePassphraseInput as dJ, type RotateRecoveryOptions as dK, type RotateRecoveryResult as dL, SEALED_PASSPHRASE_RECORD_ID as dM, type SchemaDelta as dN, type SchemaIntrospection as dO, type SealedEnvelope as dP, type SealedPassphrase as dQ, type SessionPolicy as dR, type SetPublicEnvelopeInput as dS, type ShamirRecoveryDoc as dT, type ShamirRecoveryEntry as dU, type SlotRewrapCeremony as dV, type SlotRewrapContext as dW, type StandardSchemaV1 as dX, type StandardSchemaV1Issue as dY, type StandardSchemaV1SyncResult as dZ, type StoreAuth as d_, type PassphrasePolicy as da, type PassphraseValidationResult as db, type Permission as dc, type Permissions as dd, type PersistedSchemaKind as de, type PlaintextTranslatorContext as df, type PlaintextTranslatorFn as dg, PresenceHandle as dh, type PresencePeer as di, type PublicEnvelopeField as dj, type PublicEnvelopeSchema as dk, type PublicEnvelopeText as dl, type PullMode as dm, type PullOptions as dn, type PullPolicy as dp, type PullResult as dq, type PushMode as dr, type PushOptions as ds, type PushPolicy as dt, type PushResult as du, type PutManyItemOptions as dv, type PutManyOptions as dw, type PutManyResult as dx, type QueryAcrossOptions as dy, type QueryAcrossResult as dz, type I18nMap as e, listUsersWithEnvelopes as e$, type StoreCapabilities as e0, SyncEngine as e1, type SyncMetadata as e2, type SyncPolicy as e3, SyncScheduler as e4, type SyncSchedulerStatus as e5, type SyncStatus as e6, type SyncTarget as e7, type SyncTargetRole as e8, SyncTransaction as e9, type WeakPassphraseReason as eA, type WrappedDeksBlob as eB, type WriteConflict as eC, type WriteEvent as eD, type WriteHook as eE, type WriteQueue as eF, assertStrongPassphrase as eG, buildRecipientKeyringFile as eH, burnPaperRecoveryEntry as eI, createNoydb as eJ, createStore as eK, deriveMagicLinkContentKey as eL, enrollAuthenticator as eM, estimateEntropy as eN, evaluateExportCapability as eO, evaluateImportCapability as eP, findAuthenticator as eQ, hasExportCapability as eR, hasImportCapability as eS, hasRecoveryEnrolled as eT, isMagicLinkGrantExpired as eU, isPublicEnvelope as eV, issueDelegation as eW, recoverPassphrase as eX, rotatePassphrase as eY, listMagicLinkGrants as eZ, listUsers as e_, type SyncTransactionResult as ea, type TabChannel as eb, type TabCoordinationOptions as ec, type TabLockManager as ed, type TabPresence as ee, type TabRole as ef, type TierMode as eg, type TranslatorAuditEntry as eh, type TxOp as ei, USER_ENVELOPE_COLLECTION as ej, USER_ENVELOPE_MAX_BYTES as ek, type Unsubscribe as el, type UpdateAuthenticatorOptions as em, type UpdateContext as en, type UpdateUserOptions as eo, UserApi as ep, type UserEnvelopeCheckGate as eq, UserEnvelopeOversizedError as er, type UserEnvelopePresented as es, type UserInfo as et, type VaultBackup as eu, type VaultPolicyOnDisk as ev, type VaultSchemaSnapshot as ew, type VaultSnapshot as ex, type WarningRules as ey, WeakPassphraseError as ez, type I18nTextDescriptor as f, loadActiveDelegations as f0, loadPaperRecoveryEntries as f1, loadSealedPassphrase as f2, loadShamirRecoveryEntries as f3, magicLinkGrantRecordId as f4, mintPaperRecoveryEntry as f5, mintShamirRecoveryEntry as f6, mintWrappedDeksBlob as f7, parseSealedEnvelope as f8, readMagicLinkGrantRecord as f9, recoverUser as fa, removeAuthenticator as fb, resolveSchema as fc, revokeDelegation as fd, revokeMagicLinkGrant as fe, savePaperRecoveryEntries as ff, saveSealedPassphrase as fg, saveShamirRecoveryEntries as fh, unwrapDeksFromBlob as fi, unwrapDeksFromPaperEntry as fj, unwrapDeksFromShamirEntry as fk, unwrapMagicLinkGrant as fl, validatePassphrase as fm, validatePublicEnvelopeInput as fn, validateSchemaInput as fo, validateSchemaOutput as fp, writeMagicLinkGrant as fq, changeSecret as fr, createOwnerKeyring as fs, ensureCollectionDEK as ft, grant as fu, loadKeyring as fv, persistKeyring as fw, revoke as fx, updateAuthenticator as fy, updateKeyringIdentity as fz, type I18nTextOptions as g, type OnMissingPolicy as h, applyI18nLocale as i, dictCollectionName as j, dictKey as k, enforceScript as l, getAtPath as m, i18nText as n, inferScripts as o, isDictCollectionName as p, isDictKeyDescriptor as q, isI18nTextDescriptor as r, resolveI18nText as s, resolvePolicy as t, setAtPathInPlace as u, validateI18nTextValue as v, type SessionStrategy as w, createEnforcer as x, validateSessionPolicy as y, BLOB_CHUNKS_COLLECTION as z };