@noy-db/hub 0.4.0-pre.1 → 0.4.0-pre.11

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 (605) hide show
  1. package/README.md +3 -4
  2. package/dist/api-ZDKTWNBB.js +20 -0
  3. package/dist/attestation/index.js +18 -18
  4. package/dist/{backup-LU2DTZTI.js → backup-2UX7PRM5.js} +13 -13
  5. package/dist/blobs/index.js +30 -15
  6. package/dist/blobs/index.js.map +1 -1
  7. package/dist/broker/index.js +12 -12
  8. package/dist/broker/index.js.map +1 -1
  9. package/dist/cargo/index.js +53 -43
  10. package/dist/{chunk-YTGDPCPW.js → chunk-2J6UEN2V.js} +42 -18
  11. package/dist/chunk-2J6UEN2V.js.map +1 -0
  12. package/dist/{chunk-IY23KFNZ.js → chunk-35NMVJ7I.js} +3 -3
  13. package/dist/{chunk-S3BFKGET.js → chunk-3HUYRN24.js} +4 -4
  14. package/dist/chunk-3HUYRN24.js.map +1 -0
  15. package/dist/{walk-XS3PKM6I.js → chunk-3V3XR4S4.js} +4 -13
  16. package/dist/{walk-XS3PKM6I.js.map → chunk-3V3XR4S4.js.map} +1 -1
  17. package/dist/{chunk-RTEK4C4I.js → chunk-4JQK3L4V.js} +2 -2
  18. package/dist/{chunk-3ELCWU56.js → chunk-4ZKNC6A6.js} +2 -2
  19. package/dist/{chunk-VQNJ7UZL.js → chunk-55LKVALY.js} +106 -10
  20. package/dist/chunk-55LKVALY.js.map +1 -0
  21. package/dist/{chunk-VAIS3SOM.js → chunk-55LX6FSX.js} +2 -2
  22. package/dist/chunk-5D2ALSM5.js +19 -0
  23. package/dist/chunk-5D2ALSM5.js.map +1 -0
  24. package/dist/{chunk-6HPG2YNX.js → chunk-5DRROK5W.js} +2 -2
  25. package/dist/{chunk-S6Q37VGE.js → chunk-5GQEU4DN.js} +2 -2
  26. package/dist/chunk-5JOFWL4G.js +25 -0
  27. package/dist/chunk-5JOFWL4G.js.map +1 -0
  28. package/dist/{chunk-TOPYT5U5.js → chunk-63FE525P.js} +6 -6
  29. package/dist/chunk-6LJY7X5A.js +18 -0
  30. package/dist/chunk-6LJY7X5A.js.map +1 -0
  31. package/dist/{chunk-JVBO4SZL.js → chunk-6UBZ6I5Z.js} +2 -2
  32. package/dist/{chunk-BOKIKJJB.js → chunk-6ZNSB3H6.js} +5 -17
  33. package/dist/chunk-6ZNSB3H6.js.map +1 -0
  34. package/dist/{chunk-BMQWTQZ4.js → chunk-7YVBQJ7K.js} +13 -355
  35. package/dist/chunk-7YVBQJ7K.js.map +1 -0
  36. package/dist/{chunk-UFEVO57O.js → chunk-ABS7FMMD.js} +6 -6
  37. package/dist/chunk-AFHQATY4.js +47 -0
  38. package/dist/chunk-AFHQATY4.js.map +1 -0
  39. package/dist/{chunk-HOONYZY2.js → chunk-AKKRXT23.js} +7 -7
  40. package/dist/chunk-AKKRXT23.js.map +1 -0
  41. package/dist/{chunk-DDHM2TLX.js → chunk-AT2JJEKJ.js} +4 -4
  42. package/dist/chunk-AT2JJEKJ.js.map +1 -0
  43. package/dist/{chunk-M75ZZHSR.js → chunk-BLBAQRBM.js} +2 -2
  44. package/dist/{chunk-3SV2EPMN.js → chunk-BLCWSJLN.js} +88 -81
  45. package/dist/chunk-BLCWSJLN.js.map +1 -0
  46. package/dist/chunk-CADUYBQD.js +151 -0
  47. package/dist/chunk-CADUYBQD.js.map +1 -0
  48. package/dist/{chunk-G6EQYAL6.js → chunk-CI32CAFS.js} +5 -5
  49. package/dist/{chunk-FH52K7EG.js → chunk-CUZLNGD5.js} +2 -6
  50. package/dist/{chunk-FH52K7EG.js.map → chunk-CUZLNGD5.js.map} +1 -1
  51. package/dist/{chunk-2P7EGNKF.js → chunk-CXQW2NWO.js} +145 -3
  52. package/dist/chunk-CXQW2NWO.js.map +1 -0
  53. package/dist/{chunk-6B4DYGWO.js → chunk-DABACXEF.js} +3 -3
  54. package/dist/{chunk-F6T6L2UK.js → chunk-DMH3VZU5.js} +4 -4
  55. package/dist/chunk-DSDXAKIP.js +50 -0
  56. package/dist/chunk-DSDXAKIP.js.map +1 -0
  57. package/dist/{chunk-MQ6WOPSY.js → chunk-DZ2JXHWK.js} +5 -5
  58. package/dist/chunk-DZFQVICS.js +23 -0
  59. package/dist/chunk-DZFQVICS.js.map +1 -0
  60. package/dist/chunk-E23UGFVC.js +63 -0
  61. package/dist/chunk-E23UGFVC.js.map +1 -0
  62. package/dist/{chunk-UBNCR3YK.js → chunk-EE7UHLKH.js} +4 -4
  63. package/dist/chunk-EIPVYUEP.js +654 -0
  64. package/dist/chunk-EIPVYUEP.js.map +1 -0
  65. package/dist/{chunk-EGCRBCNA.js → chunk-EKRSCRNM.js} +3 -3
  66. package/dist/{chunk-YMUGBZN2.js → chunk-FQ5CHJR7.js} +1 -1
  67. package/dist/chunk-FQ5CHJR7.js.map +1 -0
  68. package/dist/{chunk-LB6OH6K2.js → chunk-FQZ6XTQ7.js} +17 -20
  69. package/dist/chunk-FQZ6XTQ7.js.map +1 -0
  70. package/dist/{chunk-IRQY7IR5.js → chunk-FRMPM7WG.js} +4 -4
  71. package/dist/{chunk-DVUZMT2W.js → chunk-G2JUIDFA.js} +9 -9
  72. package/dist/{chunk-DVUZMT2W.js.map → chunk-G2JUIDFA.js.map} +1 -1
  73. package/dist/{chunk-RND5ZPNP.js → chunk-G6WWZNJP.js} +13 -13
  74. package/dist/chunk-G6WWZNJP.js.map +1 -0
  75. package/dist/chunk-GJW4HX2O.js +1 -0
  76. package/dist/{chunk-T6GU5PPZ.js → chunk-GU3IP74F.js} +6 -6
  77. package/dist/{chunk-5OI6BR4H.js → chunk-GWWOGNBK.js} +308 -18
  78. package/dist/chunk-GWWOGNBK.js.map +1 -0
  79. package/dist/{chunk-UNCOC7QH.js → chunk-H2GPHUZD.js} +12 -13
  80. package/dist/chunk-H2GPHUZD.js.map +1 -0
  81. package/dist/{chunk-7CI6CP7Z.js → chunk-HMU5P6RL.js} +5 -5
  82. package/dist/chunk-HMU5P6RL.js.map +1 -0
  83. package/dist/{chunk-YJV2UZ7H.js → chunk-I5GEJX3R.js} +5 -5
  84. package/dist/chunk-I5GEJX3R.js.map +1 -0
  85. package/dist/{chunk-GWKAVKHI.js → chunk-IG6KUWBX.js} +2 -2
  86. package/dist/chunk-IG6KUWBX.js.map +1 -0
  87. package/dist/{chunk-MTQPRQHB.js → chunk-IPUG56GJ.js} +15 -5
  88. package/dist/{chunk-MTQPRQHB.js.map → chunk-IPUG56GJ.js.map} +1 -1
  89. package/dist/{chunk-SMIRGMS7.js → chunk-IXNCZTWA.js} +2 -2
  90. package/dist/{chunk-RM2XV574.js → chunk-IXTSNGYY.js} +3 -3
  91. package/dist/{chunk-KUPKLSX5.js → chunk-IYZISPGD.js} +7 -5
  92. package/dist/chunk-IYZISPGD.js.map +1 -0
  93. package/dist/{chunk-SMGEYW6G.js → chunk-JDOMW53O.js} +26 -16
  94. package/dist/chunk-JDOMW53O.js.map +1 -0
  95. package/dist/{chunk-PK6QCH2C.js → chunk-JJZGIE4U.js} +6 -5
  96. package/dist/{chunk-DRWBEDYO.js → chunk-JYJFGD2H.js} +2 -2
  97. package/dist/{chunk-TAGR72IR.js → chunk-KRCBZYTZ.js} +26 -177
  98. package/dist/chunk-KRCBZYTZ.js.map +1 -0
  99. package/dist/{chunk-HPUWOVR7.js → chunk-L2DGWBCT.js} +14 -14
  100. package/dist/chunk-L2DGWBCT.js.map +1 -0
  101. package/dist/{chunk-VEALDPHW.js → chunk-L3YYLT4E.js} +15 -2
  102. package/dist/chunk-L3YYLT4E.js.map +1 -0
  103. package/dist/{chunk-A7JJLVGH.js → chunk-LNPUSMZL.js} +29 -31
  104. package/dist/chunk-LNPUSMZL.js.map +1 -0
  105. package/dist/{chunk-KUIB4B5Q.js → chunk-LNZKOUJR.js} +2 -2
  106. package/dist/{chunk-IM3QVF53.js → chunk-MBKVNOJS.js} +6 -6
  107. package/dist/chunk-MCHBCNCE.js +367 -0
  108. package/dist/chunk-MCHBCNCE.js.map +1 -0
  109. package/dist/{chunk-VCBHAE4R.js → chunk-MHMLXJ3I.js} +2 -2
  110. package/dist/chunk-MHMLXJ3I.js.map +1 -0
  111. package/dist/{chunk-WNGGJWF7.js → chunk-MSMJJB4J.js} +2 -2
  112. package/dist/chunk-MSMJJB4J.js.map +1 -0
  113. package/dist/{chunk-T36RFODW.js → chunk-MXUT7FTE.js} +2 -2
  114. package/dist/chunk-MXUT7FTE.js.map +1 -0
  115. package/dist/{chunk-BGK233EA.js → chunk-N4BBNYIB.js} +11 -9
  116. package/dist/chunk-N4BBNYIB.js.map +1 -0
  117. package/dist/{chunk-3PF6CRDT.js → chunk-NFIBXF4V.js} +1082 -1583
  118. package/dist/chunk-NFIBXF4V.js.map +1 -0
  119. package/dist/{chunk-LOF2W3JU.js → chunk-NFNJLRY7.js} +368 -52
  120. package/dist/chunk-NFNJLRY7.js.map +1 -0
  121. package/dist/{chunk-N6WF5PKC.js → chunk-NFZGZ4AK.js} +2 -2
  122. package/dist/{chunk-JRBNCUNT.js → chunk-NGSJZFGL.js} +21 -4
  123. package/dist/chunk-NGSJZFGL.js.map +1 -0
  124. package/dist/chunk-NJ4DYYO5.js +81 -0
  125. package/dist/chunk-NJ4DYYO5.js.map +1 -0
  126. package/dist/{chunk-2IALOXZS.js → chunk-NM5UYF6Q.js} +2 -2
  127. package/dist/chunk-NM5UYF6Q.js.map +1 -0
  128. package/dist/{chunk-JBBSYJHP.js → chunk-NYEFP7DY.js} +4 -4
  129. package/dist/chunk-NYEFP7DY.js.map +1 -0
  130. package/dist/{chunk-CCOP5XDW.js → chunk-NZ24MX4J.js} +8 -46
  131. package/dist/chunk-NZ24MX4J.js.map +1 -0
  132. package/dist/{chunk-KWVJAOIN.js → chunk-OLEPKFTW.js} +4 -4
  133. package/dist/chunk-OLEPKFTW.js.map +1 -0
  134. package/dist/{chunk-OF75ACKS.js → chunk-PEATRIYJ.js} +9 -9
  135. package/dist/chunk-PEOZ34EM.js +13 -0
  136. package/dist/chunk-PEOZ34EM.js.map +1 -0
  137. package/dist/{chunk-UC2ETWF3.js → chunk-PTHKZWEH.js} +2 -14
  138. package/dist/{chunk-UC2ETWF3.js.map → chunk-PTHKZWEH.js.map} +1 -1
  139. package/dist/chunk-PVEWA2DU.js +23 -0
  140. package/dist/chunk-PVEWA2DU.js.map +1 -0
  141. package/dist/{chunk-LSASLXGC.js → chunk-QBAC3GXA.js} +1 -1
  142. package/dist/chunk-QBAC3GXA.js.map +1 -0
  143. package/dist/{chunk-RDXW3OBQ.js → chunk-QD46HPZG.js} +2 -2
  144. package/dist/chunk-QD46HPZG.js.map +1 -0
  145. package/dist/{chunk-N55W7KUL.js → chunk-QDIH2744.js} +2 -2
  146. package/dist/{chunk-6HY2X62D.js → chunk-QJNCB5SK.js} +2 -2
  147. package/dist/{chunk-4BRCIYFG.js → chunk-QX76XVQH.js} +6 -6
  148. package/dist/chunk-QX76XVQH.js.map +1 -0
  149. package/dist/{chunk-W2OG6JOL.js → chunk-QZWTRENR.js} +24 -7
  150. package/dist/chunk-QZWTRENR.js.map +1 -0
  151. package/dist/{chunk-6NQMXTYC.js → chunk-RO5NM6UI.js} +2 -2
  152. package/dist/chunk-RO5NM6UI.js.map +1 -0
  153. package/dist/chunk-SGDPRCC4.js +1075 -0
  154. package/dist/chunk-SGDPRCC4.js.map +1 -0
  155. package/dist/{chunk-2PCAVW2I.js → chunk-SHEEBRZ4.js} +2 -2
  156. package/dist/chunk-SMY5X46Z.js +14 -0
  157. package/dist/chunk-SMY5X46Z.js.map +1 -0
  158. package/dist/{chunk-ISMV2QVG.js → chunk-TG634KCO.js} +11 -2
  159. package/dist/chunk-TG634KCO.js.map +1 -0
  160. package/dist/{chunk-J6Q7UVQE.js → chunk-TUXQSFHZ.js} +4 -16
  161. package/dist/{chunk-J6Q7UVQE.js.map → chunk-TUXQSFHZ.js.map} +1 -1
  162. package/dist/{chunk-GULTLPFB.js → chunk-UADPR6F4.js} +3 -3
  163. package/dist/{chunk-GULTLPFB.js.map → chunk-UADPR6F4.js.map} +1 -1
  164. package/dist/{chunk-ZEQWCBCM.js → chunk-UDHBXYGG.js} +27 -5
  165. package/dist/chunk-UDHBXYGG.js.map +1 -0
  166. package/dist/{chunk-RA5VTXXG.js → chunk-V6UQZA2H.js} +2 -2
  167. package/dist/chunk-VG2ZSFYD.js +32 -0
  168. package/dist/chunk-VG2ZSFYD.js.map +1 -0
  169. package/dist/chunk-VKQCVU2Z.js +21 -0
  170. package/dist/chunk-VKQCVU2Z.js.map +1 -0
  171. package/dist/{chunk-QDPZWTN2.js → chunk-W3G5REIU.js} +2 -2
  172. package/dist/{chunk-WEYZTLXN.js → chunk-W3L64Y3J.js} +2 -2
  173. package/dist/{chunk-ONTBCHCP.js → chunk-W66AM5IB.js} +2 -2
  174. package/dist/chunk-W66AM5IB.js.map +1 -0
  175. package/dist/{chunk-A5BDDEXK.js → chunk-WBKZUO5V.js} +26 -43
  176. package/dist/chunk-WBKZUO5V.js.map +1 -0
  177. package/dist/{chunk-CBSY2N76.js → chunk-WLXMTYGO.js} +2 -2
  178. package/dist/{chunk-3U3FBR7O.js → chunk-X3F5YJG2.js} +2 -2
  179. package/dist/{chunk-GQNGCEP5.js → chunk-X45JYIAY.js} +3 -3
  180. package/dist/chunk-X45JYIAY.js.map +1 -0
  181. package/dist/{chunk-6XE5TMPR.js → chunk-X4MVWPHV.js} +3 -3
  182. package/dist/chunk-X4MVWPHV.js.map +1 -0
  183. package/dist/chunk-XZHEOSQ2.js +16 -0
  184. package/dist/chunk-XZHEOSQ2.js.map +1 -0
  185. package/dist/chunk-Y2NBBT6K.js +18 -0
  186. package/dist/chunk-Y2NBBT6K.js.map +1 -0
  187. package/dist/{chunk-DDATL6PU.js → chunk-Y2NJBNX3.js} +8 -8
  188. package/dist/{chunk-DDATL6PU.js.map → chunk-Y2NJBNX3.js.map} +1 -1
  189. package/dist/chunk-YMUFFJCO.js +23 -0
  190. package/dist/chunk-YMUFFJCO.js.map +1 -0
  191. package/dist/{chunk-O7WJ47EF.js → chunk-YWWQM7KP.js} +5 -14
  192. package/dist/chunk-YWWQM7KP.js.map +1 -0
  193. package/dist/{chunk-7O3AYBC7.js → chunk-YZJSJOUI.js} +235 -65
  194. package/dist/chunk-YZJSJOUI.js.map +1 -0
  195. package/dist/{chunk-5WZXVULX.js → chunk-ZTLOXFPB.js} +2 -2
  196. package/dist/chunk-ZTLOXFPB.js.map +1 -0
  197. package/dist/classified/index.js +5 -5
  198. package/dist/{classified-marker-WQVIJAW2.js → classified-marker-7PKJBFCG.js} +3 -3
  199. package/dist/collection-facade-6I5K5IOD.js +45 -0
  200. package/dist/{computed-DSMPXG2Y.js → computed-AB45PS4V.js} +3 -3
  201. package/dist/consent/index.js +13 -9
  202. package/dist/consent/index.js.map +1 -1
  203. package/dist/cover/index.js +37 -0
  204. package/dist/crdt/index.js +4 -0
  205. package/dist/crdt/index.js.map +1 -1
  206. package/dist/custody/index.js +26 -0
  207. package/dist/{dead-filter-2A7IAJ3D.js → dead-filter-FL3WIUZS.js} +2 -2
  208. package/dist/{chunk-GYYCX5K6.js → delegation-QXZ4MPPH.js} +12 -6
  209. package/dist/{chunk-GYYCX5K6.js.map → delegation-QXZ4MPPH.js.map} +1 -1
  210. package/dist/derivations/index.js +12 -12
  211. package/dist/derive-VGS7FKE3.js +22 -0
  212. package/dist/directory/index.js +46 -0
  213. package/dist/dispatch-EGGC7UD7.js +72 -0
  214. package/dist/dispatch-EGGC7UD7.js.map +1 -0
  215. package/dist/dispatch-GDSTUTVP.js +194 -0
  216. package/dist/dispatch-GDSTUTVP.js.map +1 -0
  217. package/dist/{enclave-7MW6ZGXW.js → enclave-SPEEDYZ4.js} +11 -11
  218. package/dist/executor-IEBLTBCW.js +9 -0
  219. package/dist/executor-OTF556PL.js +28 -0
  220. package/dist/executor-RM7XWQDU.js +9 -0
  221. package/dist/export-accessible-P42BQ4PB.js +23 -0
  222. package/dist/extract-partition-TCYXRWBN.js +38 -0
  223. package/dist/{fanout-sidecar-WDYQ77VB.js → fanout-sidecar-SSMRSC2L.js} +9 -9
  224. package/dist/find-OZMTRIK3.js +11 -0
  225. package/dist/forget/index.js +11 -11
  226. package/dist/forget/index.js.map +1 -1
  227. package/dist/guards/index.js +6 -6
  228. package/dist/history/index.js +16 -12
  229. package/dist/history/index.js.map +1 -1
  230. package/dist/i18n/index.js +16 -14
  231. package/dist/i18n/index.js.map +1 -1
  232. package/dist/index.d.ts +42 -42
  233. package/dist/index.js +330 -2106
  234. package/dist/index.js.map +1 -1
  235. package/dist/indexing/index.js +4 -2
  236. package/dist/indexing/index.js.map +1 -1
  237. package/dist/introspection/index.js +28 -0
  238. package/dist/issue-FCKN5O6C.js +19 -0
  239. package/dist/kernel/best-effort-revert.d.ts +9 -1
  240. package/dist/kernel/collection-config.d.ts +12 -70
  241. package/dist/kernel/collection.d.ts +11 -23
  242. package/dist/kernel/enclave/crypto.d.ts +8 -8
  243. package/dist/kernel/enclave/index.d.ts +3 -3
  244. package/dist/kernel/errors.d.ts +41 -22
  245. package/dist/kernel/lazy.d.ts +30 -0
  246. package/dist/kernel/noydb.d.ts +81 -124
  247. package/dist/kernel/query/builder.d.ts +10 -10
  248. package/dist/kernel/query/index.d.ts +0 -6
  249. package/dist/kernel/query/scan-builder.d.ts +4 -4
  250. package/dist/kernel/sync-policy.d.ts +76 -4
  251. package/dist/kernel/types.d.ts +146 -86
  252. package/dist/kernel/validation.d.ts +33 -33
  253. package/dist/kernel/vault.d.ts +41 -229
  254. package/dist/kernel/via/dispatch.d.ts +7 -0
  255. package/dist/{ledger-33BC4TR7.js → ledger-6XBEVWYP.js} +11 -11
  256. package/dist/liberate-CYUOS4NC.js +24 -0
  257. package/dist/{link-set-QK22TDEX.js → link-set-NK6L2A5S.js} +11 -11
  258. package/dist/materialized-views/index.js +22 -19
  259. package/dist/money/index.js +31 -0
  260. package/dist/noydb-FZZIMLCI.js +81 -0
  261. package/dist/overlay-views/index.js +4 -4
  262. package/dist/periods/index.js +15 -13
  263. package/dist/pod/index.js +26 -12
  264. package/dist/{policy-NAHB2TUT.js → policy/index.js} +6 -6
  265. package/dist/port/with/archive-strategy.d.ts +31 -0
  266. package/dist/port/with/blob-strategy.d.ts +15 -5
  267. package/dist/port/with/collection-options.d.ts +171 -0
  268. package/dist/port/with/collection-registries.d.ts +62 -0
  269. package/dist/port/with/strategies.d.ts +134 -0
  270. package/dist/port/with/team-strategy.d.ts +3 -2
  271. package/dist/portability/index.js +8 -8
  272. package/dist/{post-register-JVTMJ6OW.js → post-register-VFIYISUK.js} +6 -6
  273. package/dist/query/index.js +6 -46
  274. package/dist/{aggregate → reduce}/index.js +24 -17
  275. package/dist/reduce/index.js.map +1 -0
  276. package/dist/register-UKU2J6MN.js +25 -0
  277. package/dist/registry-2VCWVHZS.js +19 -0
  278. package/dist/registry-7Y5WF7GS.js +9 -0
  279. package/dist/{registry-VW6P6A3J.js → registry-BWD2ED52.js} +2 -2
  280. package/dist/registry-DTWRJLSF.js +18 -0
  281. package/dist/request-withdrawal-JYDZ5XY2.js +31 -0
  282. package/dist/{reveal-LGBOM2BO.js → reveal-CL2BCW6A.js} +6 -6
  283. package/dist/revoke-2TSAD2T4.js +24 -0
  284. package/dist/satellites/index.js +1 -1
  285. package/dist/schema-update/index.js +26 -0
  286. package/dist/sealed-record/index.js +12 -12
  287. package/dist/search/index.js +36 -0
  288. package/dist/{seed-PWK45HDF.js → seed-NUVJEL7T.js} +12 -12
  289. package/dist/seed-NUVJEL7T.js.map +1 -0
  290. package/dist/sequence/index.js +30 -0
  291. package/dist/session/index.js +13 -9
  292. package/dist/session/index.js.map +1 -1
  293. package/dist/shadow/index.js +6 -2
  294. package/dist/shadow/index.js.map +1 -1
  295. package/dist/signer-K7Z2EOMG.js +25 -0
  296. package/dist/snapshots/index.js +14 -10
  297. package/dist/snapshots/index.js.map +1 -1
  298. package/dist/{stale-FJED42DM.js → stale-A4J26OIC.js} +11 -10
  299. package/dist/storage-YVHZUISN.js +23 -0
  300. package/dist/store/index.js +22 -0
  301. package/dist/{store-coordination-provider-APODTV4W.js → store-coordination-provider-EWRZL7XF.js} +3 -3
  302. package/dist/sync/index.js +15 -12
  303. package/dist/sync/index.js.map +1 -1
  304. package/dist/team/index.js +33 -48
  305. package/dist/tiers/index.js +10 -10
  306. package/dist/to/index.js +1 -1
  307. package/dist/{tx → transactions}/index.js +7 -3
  308. package/dist/transactions/index.js.map +1 -0
  309. package/dist/util/index.js +1 -1
  310. package/dist/{verify-26Y5GNMK.js → verify-7XWHJWUN.js} +5 -5
  311. package/dist/via/blob/active.d.ts +23 -7
  312. package/dist/via/blob/binding.d.ts +1 -1
  313. package/dist/via/blob/index.d.ts +7 -1
  314. package/dist/via/i18n/core.d.ts +1 -1
  315. package/dist/via/i18n/index.d.ts +2 -0
  316. package/dist/via/money/money-reducer.d.ts +2 -2
  317. package/dist/walk-P5D6CF4V.js +21 -0
  318. package/dist/with-audit/consent/index.d.ts +2 -0
  319. package/dist/with-audit/forget/active.d.ts +3 -3
  320. package/dist/with-audit/forget/index.d.ts +6 -5
  321. package/dist/with-audit/forget/strategy.d.ts +3 -3
  322. package/dist/with-audit/guards/executor.d.ts +4 -4
  323. package/dist/with-audit/guards/immutable-guard.d.ts +3 -3
  324. package/dist/with-audit/guards/index.d.ts +1 -1
  325. package/dist/with-audit/guards/registry.d.ts +3 -3
  326. package/dist/with-audit/guards/transition-guard.d.ts +3 -3
  327. package/dist/with-audit/guards/types.d.ts +10 -10
  328. package/dist/with-audit/guards/with-guard.d.ts +2 -2
  329. package/dist/with-audit/periods/index.d.ts +2 -0
  330. package/dist/with-audit/periods/vault-facade.d.ts +6 -0
  331. package/dist/with-audit/portability/export-accessible.d.ts +4 -4
  332. package/dist/with-audit/portability/request-withdrawal.d.ts +3 -3
  333. package/dist/with-audit/portability/withdraw-accessible.d.ts +2 -2
  334. package/dist/with-audit/sealed-record/strategy.d.ts +1 -1
  335. package/dist/with-audit/tiers/index.d.ts +1 -0
  336. package/dist/with-cargo/adopt-partition.d.ts +7 -7
  337. package/dist/{legacy/kernel.d.ts → with-cargo/floor.d.ts} +4 -4
  338. package/dist/with-cargo/index.d.ts +9 -2
  339. package/dist/with-commit/crdt/index.d.ts +2 -0
  340. package/dist/with-commit/history/index.d.ts +2 -0
  341. package/dist/with-commit/history/time-machine.d.ts +2 -2
  342. package/dist/with-commit/numbering/descriptor.d.ts +16 -6
  343. package/dist/with-commit/tx/active.d.ts +5 -5
  344. package/dist/with-commit/tx/index.d.ts +2 -2
  345. package/dist/with-commit/tx/invariants.d.ts +1 -1
  346. package/dist/with-commit/tx/strategy.d.ts +3 -3
  347. package/dist/with-fork/shadow/index.d.ts +2 -0
  348. package/dist/with-fork/snapshots/active.d.ts +2 -2
  349. package/dist/with-fork/snapshots/index.d.ts +3 -1
  350. package/dist/with-fork/snapshots/noydb-facade.d.ts +3 -3
  351. package/dist/with-fork/snapshots/strategy.d.ts +2 -2
  352. package/dist/with-formula/derivations/dispatch.d.ts +98 -0
  353. package/dist/with-formula/derivations/executor.d.ts +2 -2
  354. package/dist/with-formula/derivations/index.d.ts +4 -1
  355. package/dist/with-formula/derivations/registry.d.ts +3 -3
  356. package/dist/with-formula/derivations/stale.d.ts +2 -2
  357. package/dist/with-formula/derivations/types.d.ts +3 -3
  358. package/dist/with-formula/derivations/with-derivation.d.ts +2 -2
  359. package/dist/with-formula/derivations/with-rollup.d.ts +15 -8
  360. package/dist/with-formula/materialized-views/dependency-analyzer.d.ts +3 -3
  361. package/dist/with-formula/materialized-views/dispatch.d.ts +75 -0
  362. package/dist/with-formula/materialized-views/index.d.ts +2 -1
  363. package/dist/with-formula/materialized-views/registry.d.ts +4 -4
  364. package/dist/with-formula/materialized-views/types.d.ts +11 -11
  365. package/dist/with-formula/materialized-views/with-materialized-view.d.ts +2 -2
  366. package/dist/with-formula/overlay-views/index.d.ts +1 -1
  367. package/dist/with-formula/overlay-views/registry.d.ts +4 -4
  368. package/dist/with-formula/overlay-views/types.d.ts +3 -3
  369. package/dist/with-formula/overlay-views/virtual-collection.d.ts +2 -2
  370. package/dist/with-formula/overlay-views/with-overlayed-view.d.ts +2 -2
  371. package/dist/with-lookup/indexing/active.d.ts +5 -5
  372. package/dist/with-lookup/indexing/index.d.ts +3 -1
  373. package/dist/with-lookup/indexing/strategy.d.ts +3 -3
  374. package/dist/with-lookup/{aggregate → reduce}/active.d.ts +8 -8
  375. package/dist/with-lookup/{aggregate → reduce}/groupby.d.ts +21 -21
  376. package/dist/with-lookup/reduce/index.d.ts +34 -0
  377. package/dist/with-lookup/{aggregate → reduce}/reducers.d.ts +11 -12
  378. package/dist/with-lookup/{aggregate/aggregation.d.ts → reduce/reduction.d.ts} +21 -21
  379. package/dist/with-lookup/{aggregate → reduce}/strategy.d.ts +15 -15
  380. package/dist/with-lookup/search/build-docs.d.ts +7 -2
  381. package/dist/with-lookup/search/index.d.ts +1 -1
  382. package/dist/with-party/broker/index.d.ts +1 -0
  383. package/dist/with-party/broker/seed.d.ts +1 -1
  384. package/dist/with-party/custody/liberate.d.ts +3 -3
  385. package/dist/with-party/directory/cover/index.d.ts +1 -5
  386. package/dist/with-party/directory/cover/schema.d.ts +0 -6
  387. package/dist/with-party/directory/cover/storage.d.ts +0 -8
  388. package/dist/with-party/directory/cover/types.d.ts +0 -21
  389. package/dist/with-party/directory/index.d.ts +2 -0
  390. package/dist/with-party/session/dev-unlock.d.ts +2 -2
  391. package/dist/with-party/session/index.d.ts +2 -0
  392. package/dist/with-party/session/session.d.ts +2 -2
  393. package/dist/with-party/team/deed.d.ts +8 -8
  394. package/dist/with-party/team/index.d.ts +4 -7
  395. package/dist/with-party/team/keyring.d.ts +83 -35
  396. package/dist/with-party/team/{managed-passphrase.d.ts → managed-secret.d.ts} +27 -27
  397. package/dist/with-party/team/noydb-facade.d.ts +43 -42
  398. package/dist/with-party/team/peer-recover.d.ts +13 -13
  399. package/dist/with-party/team/recovery.d.ts +5 -5
  400. package/dist/with-party/team/rotate-recover.d.ts +30 -30
  401. package/dist/with-pod/bundle.d.ts +30 -32
  402. package/dist/with-pod/format.d.ts +2 -2
  403. package/dist/with-pod/index.d.ts +2 -1
  404. package/dist/with-shape/blobs/blob-compaction.d.ts +43 -3
  405. package/dist/with-shape/blobs/blob-pinning.d.ts +112 -0
  406. package/dist/with-shape/blobs/blob-set.d.ts +86 -2
  407. package/dist/with-shape/introspection/index.d.ts +6 -0
  408. package/dist/with-shape/schema-update/index.d.ts +1 -0
  409. package/dist/with-store/index.d.ts +3 -2
  410. package/dist/with-store/route-store.d.ts +1 -1
  411. package/dist/{with-party/team/sync-active.d.ts → with-sync/active.d.ts} +1 -1
  412. package/dist/{with-party/team/sync-credentials.d.ts → with-sync/credentials.d.ts} +3 -3
  413. package/dist/{with-party/team/sync.d.ts → with-sync/engine.d.ts} +5 -5
  414. package/dist/{with-party/sync → with-sync}/index.d.ts +4 -2
  415. package/dist/{with-party/team/sync-period-scope.d.ts → with-sync/period-scope.d.ts} +2 -2
  416. package/dist/{with-party/team → with-sync}/presence.d.ts +2 -2
  417. package/dist/{with-party/team/sync-strategy.d.ts → with-sync/strategy.d.ts} +6 -6
  418. package/dist/{with-party/team/sync-transaction.d.ts → with-sync/transaction.d.ts} +3 -3
  419. package/dist/withdraw-accessible-A2PWF5MZ.js +28 -0
  420. package/package.json +45 -36
  421. package/dist/aggregate/index.js.map +0 -1
  422. package/dist/api-IQFZJSQO.js +0 -20
  423. package/dist/as/index.js +0 -30
  424. package/dist/at/index.js +0 -1
  425. package/dist/bundle/index.js +0 -105
  426. package/dist/by/index.js +0 -11
  427. package/dist/chunk-2IALOXZS.js.map +0 -1
  428. package/dist/chunk-2MUZ3LY6.js +0 -205
  429. package/dist/chunk-2MUZ3LY6.js.map +0 -1
  430. package/dist/chunk-2P7EGNKF.js.map +0 -1
  431. package/dist/chunk-3PF6CRDT.js.map +0 -1
  432. package/dist/chunk-3SV2EPMN.js.map +0 -1
  433. package/dist/chunk-4BRCIYFG.js.map +0 -1
  434. package/dist/chunk-5OI6BR4H.js.map +0 -1
  435. package/dist/chunk-5WZXVULX.js.map +0 -1
  436. package/dist/chunk-6NQMXTYC.js.map +0 -1
  437. package/dist/chunk-6XE5TMPR.js.map +0 -1
  438. package/dist/chunk-7CI6CP7Z.js.map +0 -1
  439. package/dist/chunk-7O3AYBC7.js.map +0 -1
  440. package/dist/chunk-A5BDDEXK.js.map +0 -1
  441. package/dist/chunk-A7JJLVGH.js.map +0 -1
  442. package/dist/chunk-BGK233EA.js.map +0 -1
  443. package/dist/chunk-BMQWTQZ4.js.map +0 -1
  444. package/dist/chunk-BOKIKJJB.js.map +0 -1
  445. package/dist/chunk-CCOP5XDW.js.map +0 -1
  446. package/dist/chunk-CHFZDSE4.js +0 -1
  447. package/dist/chunk-DDHM2TLX.js.map +0 -1
  448. package/dist/chunk-GQNGCEP5.js.map +0 -1
  449. package/dist/chunk-GVZQ4NAM.js +0 -14
  450. package/dist/chunk-GVZQ4NAM.js.map +0 -1
  451. package/dist/chunk-GWKAVKHI.js.map +0 -1
  452. package/dist/chunk-HOONYZY2.js.map +0 -1
  453. package/dist/chunk-HPUWOVR7.js.map +0 -1
  454. package/dist/chunk-ISMV2QVG.js.map +0 -1
  455. package/dist/chunk-JBBSYJHP.js.map +0 -1
  456. package/dist/chunk-JRBNCUNT.js.map +0 -1
  457. package/dist/chunk-JZWWLLVY.js +0 -99
  458. package/dist/chunk-JZWWLLVY.js.map +0 -1
  459. package/dist/chunk-KUPKLSX5.js.map +0 -1
  460. package/dist/chunk-KWVJAOIN.js.map +0 -1
  461. package/dist/chunk-LB6OH6K2.js.map +0 -1
  462. package/dist/chunk-LB7FD65R.js +0 -163
  463. package/dist/chunk-LB7FD65R.js.map +0 -1
  464. package/dist/chunk-LOF2W3JU.js.map +0 -1
  465. package/dist/chunk-LSASLXGC.js.map +0 -1
  466. package/dist/chunk-O7WJ47EF.js.map +0 -1
  467. package/dist/chunk-ONTBCHCP.js.map +0 -1
  468. package/dist/chunk-PT37L5DV.js +0 -255
  469. package/dist/chunk-PT37L5DV.js.map +0 -1
  470. package/dist/chunk-RDXW3OBQ.js.map +0 -1
  471. package/dist/chunk-RND5ZPNP.js.map +0 -1
  472. package/dist/chunk-S3BFKGET.js.map +0 -1
  473. package/dist/chunk-SMGEYW6G.js.map +0 -1
  474. package/dist/chunk-T36RFODW.js.map +0 -1
  475. package/dist/chunk-TAGR72IR.js.map +0 -1
  476. package/dist/chunk-UNCOC7QH.js.map +0 -1
  477. package/dist/chunk-VCBHAE4R.js.map +0 -1
  478. package/dist/chunk-VEALDPHW.js.map +0 -1
  479. package/dist/chunk-VQNJ7UZL.js.map +0 -1
  480. package/dist/chunk-W2OG6JOL.js.map +0 -1
  481. package/dist/chunk-WNGGJWF7.js.map +0 -1
  482. package/dist/chunk-YJV2UZ7H.js.map +0 -1
  483. package/dist/chunk-YMUGBZN2.js.map +0 -1
  484. package/dist/chunk-YTGDPCPW.js.map +0 -1
  485. package/dist/chunk-ZEQWCBCM.js.map +0 -1
  486. package/dist/collection-facade-MJXSC3Q6.js +0 -44
  487. package/dist/cover-B5SJDHR3.js +0 -52
  488. package/dist/delegation-A5A75KQO.js +0 -25
  489. package/dist/derive-HJSMH7RA.js +0 -21
  490. package/dist/executor-AXE4XMLT.js +0 -26
  491. package/dist/executor-KGNLJD6M.js +0 -9
  492. package/dist/executor-XGIS6CR7.js +0 -9
  493. package/dist/export-accessible-VVBJM3MM.js +0 -23
  494. package/dist/extract-partition-LGGBTQET.js +0 -38
  495. package/dist/find-YS6XWB46.js +0 -11
  496. package/dist/in/index.js +0 -1
  497. package/dist/issue-LSR5PS6H.js +0 -19
  498. package/dist/legacy/bundle.d.ts +0 -30
  499. package/dist/liberate-GBSAYLPJ.js +0 -24
  500. package/dist/noydb-MTUBMWFE.js +0 -70
  501. package/dist/on/index.js +0 -17
  502. package/dist/port/as/index.d.ts +0 -26
  503. package/dist/port/at/index.d.ts +0 -18
  504. package/dist/port/in/index.d.ts +0 -21
  505. package/dist/port/on/index.d.ts +0 -27
  506. package/dist/port/ui/index.d.ts +0 -21
  507. package/dist/port/with/index.d.ts +0 -18
  508. package/dist/register-X462DNDM.js +0 -23
  509. package/dist/registry-3XMCT7MQ.js +0 -18
  510. package/dist/registry-KHD2I3M2.js +0 -19
  511. package/dist/registry-NPOWJHC4.js +0 -9
  512. package/dist/request-withdrawal-U7K3JTMY.js +0 -31
  513. package/dist/revoke-OCWDSWRQ.js +0 -24
  514. package/dist/seed-PWK45HDF.js.map +0 -1
  515. package/dist/signer-3YFBLZRN.js +0 -25
  516. package/dist/storage-W3GURVFU.js +0 -23
  517. package/dist/tx/index.js.map +0 -1
  518. package/dist/ui/index.js +0 -1
  519. package/dist/with/index.js +0 -31
  520. package/dist/with-lookup/aggregate/index.d.ts +0 -23
  521. package/dist/withdraw-accessible-Y3B2535F.js +0 -28
  522. /package/dist/{api-IQFZJSQO.js.map → api-ZDKTWNBB.js.map} +0 -0
  523. /package/dist/{backup-LU2DTZTI.js.map → backup-2UX7PRM5.js.map} +0 -0
  524. /package/dist/{chunk-IY23KFNZ.js.map → chunk-35NMVJ7I.js.map} +0 -0
  525. /package/dist/{chunk-RTEK4C4I.js.map → chunk-4JQK3L4V.js.map} +0 -0
  526. /package/dist/{chunk-3ELCWU56.js.map → chunk-4ZKNC6A6.js.map} +0 -0
  527. /package/dist/{chunk-VAIS3SOM.js.map → chunk-55LX6FSX.js.map} +0 -0
  528. /package/dist/{chunk-6HPG2YNX.js.map → chunk-5DRROK5W.js.map} +0 -0
  529. /package/dist/{chunk-S6Q37VGE.js.map → chunk-5GQEU4DN.js.map} +0 -0
  530. /package/dist/{chunk-TOPYT5U5.js.map → chunk-63FE525P.js.map} +0 -0
  531. /package/dist/{chunk-JVBO4SZL.js.map → chunk-6UBZ6I5Z.js.map} +0 -0
  532. /package/dist/{chunk-UFEVO57O.js.map → chunk-ABS7FMMD.js.map} +0 -0
  533. /package/dist/{chunk-M75ZZHSR.js.map → chunk-BLBAQRBM.js.map} +0 -0
  534. /package/dist/{chunk-G6EQYAL6.js.map → chunk-CI32CAFS.js.map} +0 -0
  535. /package/dist/{chunk-6B4DYGWO.js.map → chunk-DABACXEF.js.map} +0 -0
  536. /package/dist/{chunk-F6T6L2UK.js.map → chunk-DMH3VZU5.js.map} +0 -0
  537. /package/dist/{chunk-MQ6WOPSY.js.map → chunk-DZ2JXHWK.js.map} +0 -0
  538. /package/dist/{chunk-UBNCR3YK.js.map → chunk-EE7UHLKH.js.map} +0 -0
  539. /package/dist/{chunk-EGCRBCNA.js.map → chunk-EKRSCRNM.js.map} +0 -0
  540. /package/dist/{chunk-IRQY7IR5.js.map → chunk-FRMPM7WG.js.map} +0 -0
  541. /package/dist/{as/index.js.map → chunk-GJW4HX2O.js.map} +0 -0
  542. /package/dist/{chunk-T6GU5PPZ.js.map → chunk-GU3IP74F.js.map} +0 -0
  543. /package/dist/{chunk-SMIRGMS7.js.map → chunk-IXNCZTWA.js.map} +0 -0
  544. /package/dist/{chunk-RM2XV574.js.map → chunk-IXTSNGYY.js.map} +0 -0
  545. /package/dist/{chunk-PK6QCH2C.js.map → chunk-JJZGIE4U.js.map} +0 -0
  546. /package/dist/{chunk-DRWBEDYO.js.map → chunk-JYJFGD2H.js.map} +0 -0
  547. /package/dist/{chunk-KUIB4B5Q.js.map → chunk-LNZKOUJR.js.map} +0 -0
  548. /package/dist/{chunk-IM3QVF53.js.map → chunk-MBKVNOJS.js.map} +0 -0
  549. /package/dist/{chunk-N6WF5PKC.js.map → chunk-NFZGZ4AK.js.map} +0 -0
  550. /package/dist/{chunk-OF75ACKS.js.map → chunk-PEATRIYJ.js.map} +0 -0
  551. /package/dist/{chunk-N55W7KUL.js.map → chunk-QDIH2744.js.map} +0 -0
  552. /package/dist/{chunk-6HY2X62D.js.map → chunk-QJNCB5SK.js.map} +0 -0
  553. /package/dist/{chunk-2PCAVW2I.js.map → chunk-SHEEBRZ4.js.map} +0 -0
  554. /package/dist/{chunk-RA5VTXXG.js.map → chunk-V6UQZA2H.js.map} +0 -0
  555. /package/dist/{chunk-QDPZWTN2.js.map → chunk-W3G5REIU.js.map} +0 -0
  556. /package/dist/{chunk-WEYZTLXN.js.map → chunk-W3L64Y3J.js.map} +0 -0
  557. /package/dist/{chunk-CBSY2N76.js.map → chunk-WLXMTYGO.js.map} +0 -0
  558. /package/dist/{chunk-3U3FBR7O.js.map → chunk-X3F5YJG2.js.map} +0 -0
  559. /package/dist/{classified-marker-WQVIJAW2.js.map → classified-marker-7PKJBFCG.js.map} +0 -0
  560. /package/dist/{at/index.js.map → collection-facade-6I5K5IOD.js.map} +0 -0
  561. /package/dist/{bundle/index.js.map → computed-AB45PS4V.js.map} +0 -0
  562. /package/dist/{by → cover}/index.js.map +0 -0
  563. /package/dist/{in → custody}/index.js.map +0 -0
  564. /package/dist/{dead-filter-2A7IAJ3D.js.map → dead-filter-FL3WIUZS.js.map} +0 -0
  565. /package/dist/{chunk-CHFZDSE4.js.map → derive-VGS7FKE3.js.map} +0 -0
  566. /package/dist/{on → directory}/index.js.map +0 -0
  567. /package/dist/{collection-facade-MJXSC3Q6.js.map → enclave-SPEEDYZ4.js.map} +0 -0
  568. /package/dist/{computed-DSMPXG2Y.js.map → executor-IEBLTBCW.js.map} +0 -0
  569. /package/dist/{cover-B5SJDHR3.js.map → executor-OTF556PL.js.map} +0 -0
  570. /package/dist/{delegation-A5A75KQO.js.map → executor-RM7XWQDU.js.map} +0 -0
  571. /package/dist/{derive-HJSMH7RA.js.map → export-accessible-P42BQ4PB.js.map} +0 -0
  572. /package/dist/{enclave-7MW6ZGXW.js.map → extract-partition-TCYXRWBN.js.map} +0 -0
  573. /package/dist/{fanout-sidecar-WDYQ77VB.js.map → fanout-sidecar-SSMRSC2L.js.map} +0 -0
  574. /package/dist/{executor-AXE4XMLT.js.map → find-OZMTRIK3.js.map} +0 -0
  575. /package/dist/{ui → introspection}/index.js.map +0 -0
  576. /package/dist/{executor-KGNLJD6M.js.map → issue-FCKN5O6C.js.map} +0 -0
  577. /package/dist/{executor-XGIS6CR7.js.map → ledger-6XBEVWYP.js.map} +0 -0
  578. /package/dist/{export-accessible-VVBJM3MM.js.map → liberate-CYUOS4NC.js.map} +0 -0
  579. /package/dist/{extract-partition-LGGBTQET.js.map → link-set-NK6L2A5S.js.map} +0 -0
  580. /package/dist/{with → money}/index.js.map +0 -0
  581. /package/dist/{find-YS6XWB46.js.map → noydb-FZZIMLCI.js.map} +0 -0
  582. /package/dist/{issue-LSR5PS6H.js.map → policy/index.js.map} +0 -0
  583. /package/dist/{post-register-JVTMJ6OW.js.map → post-register-VFIYISUK.js.map} +0 -0
  584. /package/dist/{ledger-33BC4TR7.js.map → register-UKU2J6MN.js.map} +0 -0
  585. /package/dist/{liberate-GBSAYLPJ.js.map → registry-2VCWVHZS.js.map} +0 -0
  586. /package/dist/{link-set-QK22TDEX.js.map → registry-7Y5WF7GS.js.map} +0 -0
  587. /package/dist/{noydb-MTUBMWFE.js.map → registry-BWD2ED52.js.map} +0 -0
  588. /package/dist/{policy-NAHB2TUT.js.map → registry-DTWRJLSF.js.map} +0 -0
  589. /package/dist/{register-X462DNDM.js.map → request-withdrawal-JYDZ5XY2.js.map} +0 -0
  590. /package/dist/{reveal-LGBOM2BO.js.map → reveal-CL2BCW6A.js.map} +0 -0
  591. /package/dist/{registry-3XMCT7MQ.js.map → revoke-2TSAD2T4.js.map} +0 -0
  592. /package/dist/{registry-KHD2I3M2.js.map → schema-update/index.js.map} +0 -0
  593. /package/dist/{registry-NPOWJHC4.js.map → search/index.js.map} +0 -0
  594. /package/dist/{registry-VW6P6A3J.js.map → sequence/index.js.map} +0 -0
  595. /package/dist/{request-withdrawal-U7K3JTMY.js.map → signer-K7Z2EOMG.js.map} +0 -0
  596. /package/dist/{revoke-OCWDSWRQ.js.map → stale-A4J26OIC.js.map} +0 -0
  597. /package/dist/{signer-3YFBLZRN.js.map → storage-YVHZUISN.js.map} +0 -0
  598. /package/dist/{stale-FJED42DM.js.map → store/index.js.map} +0 -0
  599. /package/dist/{store-coordination-provider-APODTV4W.js.map → store-coordination-provider-EWRZL7XF.js.map} +0 -0
  600. /package/dist/{verify-26Y5GNMK.js.map → verify-7XWHJWUN.js.map} +0 -0
  601. /package/dist/{storage-W3GURVFU.js.map → walk-P5D6CF4V.js.map} +0 -0
  602. /package/dist/with-lookup/{aggregate → reduce}/canonical-key.d.ts +0 -0
  603. /package/dist/{with-party → with-sync}/tab-coordination.d.ts +0 -0
  604. /package/dist/{with-party → with-sync}/tab-write-relay.d.ts +0 -0
  605. /package/dist/{withdraw-accessible-Y3B2535F.js.map → withdraw-accessible-A2PWF5MZ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/kernel/types.ts"],"sourcesContent":["/**\n * Core types — the {@link NoydbStore} interface, envelope format, roles, and\n * all configuration shapes consumed by {@link createNoydb}.\n *\n * ## What lives here\n *\n * - **{@link NoydbStore}** — the 6-method contract every backend must implement\n * (`get`, `put`, `delete`, `list`, `loadAll`, `saveAll`).\n * - **{@link EncryptedEnvelope}** — the wire format stored by backends:\n * `{ _noydb, _v, _ts, _iv, _data }`. Backends only ever see this shape.\n * - **{@link Role} / {@link Permission}** — the access-control vocabulary\n * (`owner`, `admin`, `operator`, `viewer`, `client`).\n * - **{@link NoydbOptions}** — the full configuration object passed to\n * {@link createNoydb}.\n *\n * ## Extending the store interface\n *\n * All optional store capabilities (`ping`, `listPage`, `listSince`,\n * `presencePublish`, `presenceSubscribe`, `listVaults`) are additive extensions\n * discovered via `'method' in store`. Implementing them unlocks features but\n * is never required — core always falls back to the 6-method baseline.\n *\n * @module\n */\n\nimport type { StandardSchemaV1 } from './schema.js'\nimport type { DeferredNumberingConfig } from '../with-commit/numbering/descriptor.js'\nimport type { SyncPolicy, ReadinessState } from './sync-policy.js'\nimport type { BlobsStrategy } from '../port/with/blob-strategy.js'\nimport type { ArchiveStrategy } from '../with-fork/archive/index.js'\nimport type { IndexingStrategy } from '../with-lookup/indexing/strategy.js'\nimport type { ReduceStrategy } from '../with-lookup/reduce/strategy.js'\nimport type { ConsentStrategy } from '../with-audit/consent/strategy.js'\nimport type { PeriodsStrategy } from '../with-audit/periods/strategy.js'\nimport type { ShadowStrategy } from '../with-fork/shadow/strategy.js'\nimport type { TransactionsStrategy } from '../with-commit/tx/strategy.js'\nimport type { HistoryStrategy } from '../with-commit/history/strategy.js'\nimport type { ForgetStrategy } from '../with-audit/forget/strategy.js'\nimport type { SnapshotsStrategy } from '../with-fork/snapshots/strategy.js'\nimport type { DerivationSkippedFrozen } from './via/dispatch.js'\nimport type { AttestationStrategy } from '../with-audit/attestation/strategy.js'\nimport type { ClassifiedStrategy } from '../port/with/classified-strategy.js'\nimport type { TiersStrategy } from '../with-audit/tiers/strategy.js'\nimport type { SealedRecordStrategy } from '../with-audit/sealed-record/strategy.js'\nimport type { PortabilityStrategy } from '../with-audit/portability/strategy.js'\nimport type { SequenceStrategy } from '../with-commit/sequence/strategy.js'\nimport type { CustodyStrategy } from '../with-party/custody/strategy.js'\nimport type { TeamStrategy } from '../port/with/team-strategy.js'\nimport type { BrokerStrategy } from '../port/with/broker-strategy.js'\nimport type { LazyStrategy } from '../port/with/lazy-strategy.js'\nimport type { SearchStrategy } from '../with-lookup/search/strategy.js'\nimport type { CargoStrategy } from '../with-cargo/strategy.js'\nimport type { Layer, I18nStrategy } from '../port/with/i18n-strategy.js'\nimport type { SessionStrategy } from '../with-party/session/strategy.js'\nimport type { SyncStrategy } from '../with-sync/strategy.js'\nimport type { GuardStrategyAny } from '../with-audit/guards/types.js'\nimport type { DerivationStrategy } from '../with-formula/derivations/types.js'\nimport type { UnlockedKeyring } from '../with-party/team/keyring.js'\nimport type { SecretPolicy } from './validation.js'\nimport type { CoverSchema } from '../with-party/directory/cover/types.js'\nimport type { MaterializedViewStrategy } from '../with-formula/materialized-views/types.js'\nimport type { OverlayedViewStrategy } from '../with-formula/overlay-views/types.js'\nimport type { SealingKeyProvider, RecipientHint } from '../with-party/team/managed-secret.js'\nimport type { ShamirRecoveryProvider } from '../with-party/team/shamir-recovery-provider.js'\nimport type { ObjectProjection } from '../with-shape/blobs/object-projection.js'\nimport type { CoordinationProvider } from '../port/by/types.js'\nimport type { ScriptWarning } from '../port/with/i18n-strategy.js'\nimport type { ViaDescriptor } from './via/index.js'\nimport type { EnclaveKey } from './enclave/index.js'\n\n/** Format version for encrypted record envelopes. */\nexport const NOYDB_FORMAT_VERSION = 1 as const\n\n/** Format version for keyring files. */\nexport const NOYDB_KEYRING_VERSION = 1 as const\n\n/** Format version for backup files. */\nexport const NOYDB_BACKUP_VERSION = 1 as const\n\n/** Format version for sync metadata. */\nexport const NOYDB_SYNC_VERSION = 1 as const\n\n// ─── Roles & Permissions ───────────────────────────────────────────────\n\n/**\n * Access role assigned to a user within a vault.\n *\n * Roles control both the operations a user can perform and which DEKs\n * they receive in their keyring:\n *\n * | Role | Collections | Can grant/revoke | Can export |\n * |-------------|-----------------|:----------------:|:----------:|\n * | `owner` | all (rw) | Yes (all roles) | Yes |\n * | `admin` | all (rw) | Yes (≤ admin) | Yes |\n * | `custodian` | all (rw) | No (see below) | Yes |\n * | `operator` | explicit (rw) | No | ACL-scoped |\n * | `viewer` | all (ro) | No | Yes |\n * | `client` | explicit (ro) | No | ACL-scoped |\n *\n * **`custodian` (FR-6 sovereign custody).** Operationally admin-rank —\n * rw + access on every collection, receives all collection DEKs on grant\n * — but is *provably non-owning*: it CANNOT grant, revoke, rotate keys,\n * destructively withdraw/sever, or extract-and-sever a partition (rotate is\n * blocked in `rotateKeys`, sever in `withdrawAccessibleData`, and extract in\n * `extractPartition`). Only the (sealed Deed) **owner** may\n * mint or remove a custodian; an admin cannot. This is the inalienability\n * floor — a custodian can run the vault day-to-day yet never escalate to\n * the owner credential.\n */\nexport type Role = 'owner' | 'admin' | 'custodian' | 'operator' | 'viewer' | 'client'\n\n/**\n * Read-write or read-only access on a collection.\n * Stored per-collection in the user's keyring.\n */\nexport type Permission = 'rw' | 'ro'\n\n/**\n * Map of collection name → permission level for a user's keyring entry.\n * `'*'` is the wildcard collection matching all collections in the vault.\n */\nexport type Permissions = Record<string, Permission>\n\n// ─── Encrypted Envelope ────────────────────────────────────────────────\n\n/** The encrypted wrapper stored by stores. Stores only ever see this. */\nexport interface EncryptedEnvelope {\n readonly _noydb: typeof NOYDB_FORMAT_VERSION\n readonly _v: number\n readonly _ts: string\n readonly _iv: string\n readonly _data: string\n /** User who created this version (unencrypted metadata). */\n readonly _by?: string\n /**\n * Opaque provenance source id — which party/registry wrote this version.\n * Unencrypted; present only when the collection opts into `provenance: true`\n * and a `source` is supplied to `put()`. Off by default (zero cost).\n */\n readonly _source?: string\n /** ISO-8601 timestamp the provenance source was recorded. Present alongside `_source`. */\n readonly _sourceTs?: string\n /**\n * Hierarchical access tier. Omitted → tier 0.\n *\n * Unencrypted on purpose — the store reads it to route the envelope\n * to the right DEK slot without having to try-decrypt against every\n * tier. Only leaks the tier of each record, not any value\n * equivalence.\n */\n readonly _tier?: number\n /**\n * User id who last elevated this record. Used by\n * `demote()` to gate the reverse operation: only the original\n * elevator or an owner can demote a record back down. Cleared on\n * every successful demote so a later re-elevate requires the new\n * actor to own the demotion right.\n */\n readonly _elevatedBy?: string\n /**\n * Deterministic-encryption index. Map of field name →\n * base64 deterministic ciphertext. Present only when the collection\n * declares `deterministicFields` and the feature is acknowledged. The\n * field names are unencrypted (they're the index keys); the values\n * are AES-GCM ciphertext with an HKDF-derived deterministic IV.\n *\n * Enables blind equality search (`collection.findByDet(field,\n * value)`) without decrypting every record. Leaks equality as a known\n * side channel.\n */\n readonly _det?: Record<string, string>\n /**\n * Structural group-encryption. Map of sensitive field name →\n * per-field sealed ciphertext in `iv:data` form (same shape as a `_det`\n * slot). Present only when the collection declares `sensitive` fields and\n * at least one is present on the record. Each field is encrypted under its\n * own HKDF-derived per-field key (`deriveSealedFieldKey`, domain-separated\n * by `<collection>/sealed/<field>`), and is kept OUT of the open `_data`\n * blob — so a reader who can open `_data` still cannot see sealed fields\n * without re-deriving each field key. With no sensitive fields declared the\n * map is absent and `_data` is unchanged (byte-identical to legacy output).\n */\n readonly _sealed?: Record<string, string>\n /**\n * Verify-digest slots (classified stage 2). Map of digest-only field name →\n * AES-256-GCM `iv:data` blob sealed under the HKDF(CEK) vdig slot key with\n * AAD ['noydb-classify-vdig', collection, recordId, field]. The store sees\n * only ciphertext; only the enclave verify path can read the digest. At most\n * one of `_sealed[field]` / `_vdig[field]` exists per field (I4).\n */\n readonly _vdig?: Record<string, string>\n /**\n * Equatable blind-index tags (classified slice 2b). Map of digest-only field\n * name → base64 33-byte tag (1-byte cost/version discriminator ‖ 32-byte keyed\n * MAC), CURRENT VALUE ONLY (the _vdig ring is never indexed). This is the ONLY\n * store-visible classified artifact: a keyed MAC, comparable without a key\n * ceremony, with NO inline cryptographic integrity by construction. Invariant:\n * _bidx[field] present ⇒ _vdig[field] present. Confirm-by-verify (findByDigest)\n * makes any read-side orphan/splice unreturnable.\n */\n readonly _bidx?: Record<string, string>\n /**\n * Per-record content-encryption key (CEK), base64 AES-KW-wrapped under\n * the collection (or tier) DEK. Present only on records written by a\n * collection opened with `perRecordKeys: true`. When present, the body\n * (`_iv`/`_data`) is encrypted under the unwrapped CEK rather than the\n * collection DEK directly.\n *\n * Presence is the format discriminant: `_cek` absent → legacy body\n * keyed off the collection DEK (read unchanged); `_cek` present →\n * unwrap under the collection DEK, then decrypt the body under the CEK.\n *\n * The CEK is stable across every version of a record (insert mints it;\n * updates and history snapshots reuse it), so all `_history` envelopes\n * for a record carry the same `_cek`. This is the foundation for\n * per-record erasure and record-scoped sealing.\n *\n * `_det` slots are deliberately NOT keyed off the CEK — they remain\n * keyed to the collection DEK so blind-equality search keeps working\n * across records.\n */\n readonly _cek?: string\n /**\n * Debug-plaintext marker. Present only on records written by a vault opened\n * with `debugPlaintext: true` (which requires `encrypt: false`). When set,\n * the record's own fields are inlined as top-level keys on this envelope\n * (beside the reserved `_`-prefixed metadata) and `_data` is empty — so\n * native store tooling (jq, S3 console) reads the record directly. The read\n * path reconstructs the record from the non-`_` keys; the marker makes a\n * debug envelope self-describing, so a classic plaintext reader handles it too.\n */\n readonly _debug?: typeof NOYDB_FORMAT_VERSION\n /**\n * #589: this envelope is a delete marker (ordinary `collection.delete()` under\n * sync). Empty `_data`, no `_cek`, but version-ordered — a higher-`_v` re-create\n * resurrects the id. Distinct from a forget crypto-shred tombstone, which is\n * terminal. Reads treat it as absent.\n */\n readonly _del?: true\n}\n\n/** Spine policy for one digest-only classified field — the enclave-consumable\n * projection of a ClassifiedFieldSpec (the enclave never imports with-*). */\nexport interface VdigFieldPolicy {\n readonly normalize: 'password' | 'secret-answer'\n /** Ring size for reuse refusal; 0 = no ring. Cap 8 (spec Q4). */\n readonly notLastN: number\n readonly rotateDays?: number\n /** default false — refused unless the double door is open (R8) */\n readonly equatable: boolean\n}\n\n/**\n * The persisted classified-fields config marker (C-A / R10). Reuses the\n * stage-2 persisted-schema record; this is the shape of the marker stored\n * there.\n */\nexport interface ClassifiedMarker {\n /** field names declared digest-only (have _vdig); non-empty ⇒ writes need the classified codec */\n readonly digestOnly: readonly string[]\n /** field names additionally declared equatable (have _bidx when covered) */\n readonly equatable: readonly string[]\n /**\n * Lifetime epoch (#597) — same shape/intent as `PairingMarker.epoch`\n * (`with-shape/satellites/types.ts`): an opaque, stable-per-collection-\n * lifetime stamp minted the first time this marker is persisted and\n * carried forward unchanged by an IDENTICAL re-persist for the SAME\n * collection (the equality fast path). NOTE: unlike `PairingMarker` (whose\n * R-S9 refuses divergent redeclares), a classified marker IS rewritten\n * wholesale on a genuine reconfiguration (changed digestOnly/equatable\n * set) — so the epoch re-stamps to the fresh value then (see\n * `config-drift.ts`'s `markerForFields`). Optional: markers persisted\n * before this field existed have none. Deliberately excluded from the\n * classified-marker equality check in\n * `with-shape/persisted-schemas/register.ts`. ADDITIVE ONLY today: no\n * delete-collection API exists yet, so a stale marker on a reused name is\n * unreachable; the epoch-MISMATCH rejection this would enable is a\n * deferred follow-up once name reuse is possible — whoever wires it must\n * first make reconfiguration carry the prior epoch forward.\n */\n readonly epoch?: string\n}\n\n/** Verdict-only egress of the enclave oracle (spec §3). */\nexport interface ClassifiedVerdict {\n readonly ok: boolean\n /** I1: present ONLY when ok === true — never computed for a false verdict. */\n readonly mustRotate?: true\n}\n\n/**\n * Opaque access gate for a sealed (`sensitive`) field returned by a public\n * read (the access layer). The handle carries only the per-field\n * **ciphertext** — the plaintext is never materialised into the working-set\n * cache. Call {@link Sealed.reveal} to decrypt the value on demand.\n *\n * A handle is intentionally NOT usable as `V`: it serialises to a non-leaking\n * marker (`JSON.stringify` / structured logging emit `'[sealed]'`, never the\n * value) and exposes no synchronous accessor.\n */\nexport interface Sealed<V> {\n /** Discriminant — always `true`, lets callers narrow a field to a handle. */\n readonly sealed: true\n /** Decrypt and return the underlying value. */\n reveal(): Promise<V>\n}\n\n/**\n * The shape a public read returns for a collection that declares `sensitive`\n * fields `S`: every sealed field becomes an opaque {@link Sealed} handle while\n * the rest of the record is unchanged. The `[S] extends [never]` guard collapses\n * `SealedView<T, never>` to exactly `T`, so collections with no sensitive fields\n * are unaffected — a plain `Omit<T, never>` is *not* a faithful identity for\n * generic intersection record types (it can degrade intersection-only members to\n * `unknown`), which would break consumers like the derivation/MV `_derivedFrom` /\n * `_materializedFrom` reads.\n */\nexport type SealedView<T, S extends keyof T> = [S] extends [never]\n ? T\n : Omit<T, S> & {\n readonly [K in S]: Sealed<T[K]>\n }\n\n/**\n * The type of a field-name argument to the query/scan DSL (`where`, `orderBy`,\n * …) for a collection whose sealed (`sensitive`) fields are `S`.\n *\n * Guarded so the common case is unchanged: with **no** sensitive fields\n * (`S = never`) it is exactly `string` — collections that don't opt into\n * `sensitive` keep today's permissive DSL, zero churn. Once a field is\n * declared `sensitive`, the DSL narrows to the non-sensitive field names, so\n * `where('ssn', …)` becomes a compile error. TypeScript cannot subtract a\n * literal from `string`, so refusing a sensitive name necessarily means\n * narrowing to the known field-name union — this is intentional and only\n * affects collections that opted in.\n *\n * When `Q` (the indexed-field set) is given, `where()` is additionally\n * restricted to `Q` minus any sensitive fields — the escape hatch for\n * non-indexed filters is `scan()`. `Q = never` (the default) preserves the\n * existing 2-param behaviour exactly (zero churn).\n */\nexport type QueryField<T, S extends keyof T = never, Q extends keyof T & string = never> =\n [Q] extends [never]\n ? ([S] extends [never] ? string : Exclude<keyof T & string, S>)\n : Exclude<Q, S>\n\n/**\n * The type of a field-name reference in a collection's index-declaration\n * options (`indexes`, `deterministicFields`, `textIndexes`). Same guarded\n * narrowing as {@link QueryField}: permissive `string` until a field is\n * declared `sensitive`, then the sensitive names are refused (a plaintext\n * secondary index over a sealed field defeats non-residency). Kept distinct\n * from `QueryField` so the two DSL surfaces can diverge later without coupling.\n *\n * When `Q` (the indexed-field set) is given, the `indexes` option is\n * additionally restricted to `Q` minus any sensitive fields — declaring `Q`\n * but listing a different field in `indexes` becomes a compile error.\n * `Q = never` (the default) preserves the existing 2-param behaviour.\n */\nexport type IndexFieldName<T, S extends keyof T = never, Q extends keyof T & string = never> =\n [Q] extends [never]\n ? ([S] extends [never] ? string : Exclude<keyof T & string, S>)\n : Exclude<Q, S>\n\n/**\n * Generic form of the runtime `IndexDef` (see `indexing/eager-indexes.ts`)\n * parameterised by the allowed field-name set `F`. Used to refuse `sensitive`\n * fields in the `indexes` collection option at compile time while leaving the\n * runtime `IndexDef` (string-based) untouched. `IndexDefFor<string>` is\n * structurally identical to `IndexDef`, which is why `vault.collection` can cast\n * the narrowed public option to `IndexDef[]` at the runtime boundary (through\n * `unknown`, solely to drop the `readonly`).\n * **Keep this in sync with `IndexDef`** — if `IndexDef` gains a new union member,\n * add it here too, or that boundary cast will silently admit shapes the runtime\n * machinery does not narrow.\n */\nexport type IndexDefFor<F extends string> =\n | F\n | { readonly fields: readonly F[]; readonly unique?: boolean }\n | readonly F[]\n\n/**\n * The type of the `sensitive` collection option, conditional on whether the\n * caller opted into compile-time refusal via an explicit second generic.\n * With no 2nd generic (`S = never`) it accepts any field array — runtime\n * sealing only, no compile refusal, non-breaking. With `S` given, it is\n * `readonly S[]`, which ties the runtime array to the declared sensitive\n * union so the two cannot drift.\n */\nexport type SensitiveOpt<T, S extends keyof T> = [S] extends [never]\n ? readonly (keyof T & string)[]\n : readonly S[]\n\n/**\n * The type of the `moneyFields` collection option, conditional on whether the\n * caller opted into compile-time money-field typing via the 4th generic `M`.\n * Typed against the opaque {@link ViaDescriptor} marker rather than the\n * concrete `MoneyDescriptor` — the kernel never inspects a Via feature's\n * descriptor shape, only its declaring service does.\n * A `money()` descriptor structurally satisfies `ViaDescriptor` (it carries\n * `_viaBrand: 'money'`), so this stays publicly assignable from `money()`\n * call sites. With no `M` (`M = never`) it accepts any\n * `Record<string, ViaDescriptor>` — runtime money only, no compile-level\n * narrowing, non-breaking. With `M` given, it is `Record<M, ViaDescriptor>`,\n * tying the runtime map to the declared money-field union so the two cannot\n * drift.\n */\nexport type MoneyFieldsOpt<T, M extends keyof T & string = never> =\n [M] extends [never] ? Record<string, ViaDescriptor> : Record<M, ViaDescriptor>\n\n/**\n * Concrete {@link Sealed} handle. Holds the reveal closure (which captures the\n * field's ciphertext blob and the unseal routine) in a private field, so it is\n * invisible to `JSON.stringify`, `util.inspect`, and `Object.keys`. `toJSON`\n * returns the marker `'[sealed]'` — a handle can never leak its value through\n * serialisation or logging because the plaintext is not stored on it at all.\n */\nexport class SealedHandle<V> implements Sealed<V> {\n readonly sealed = true as const\n readonly #reveal: () => Promise<V>\n\n constructor(reveal: () => Promise<V>) {\n this.#reveal = reveal\n }\n\n reveal(): Promise<V> {\n return this.#reveal()\n }\n\n /** Non-leaking serialisation marker — never the underlying value. */\n toJSON(): string {\n return '[sealed]'\n }\n}\n\n/**\n * Handover-capable provider. Implemented additionally by asymmetric/granted\n * providers (cloud-KMS asymmetric, Azure RSA Key Vault, AWS KMS with grant).\n * Self-only providers (macOS Keychain, env-var, WebAuthn-PRF) do NOT\n * implement this — the §11.2 capability matrix lives in the type system.\n *\n * Per foundation §11.4. A function that requires recipient-target sealing\n * takes `RecipientSealer`, not `SealingKeyProvider` — the compiler rejects\n * passing a self-only provider at the spec site.\n */\nexport interface RecipientSealer {\n readonly id: string\n /** Produce hint material a sender uses to seal-for-this-recipient. */\n publishRecipientHint(): Promise<RecipientHint>\n /**\n * Seal plaintext for the recipient described by `hint`. Returns opaque\n * bytes — same contract as `SealingKeyProvider.seal()`. The bundle\n * layer base64-encodes the bytes into `SealedAutoUnlockEntry.sealed`\n * without inspecting them.\n */\n sealForRecipient(plaintext: Uint8Array, hint: RecipientHint): Promise<Uint8Array>\n}\n\n/**\n * Thin delivery envelope persisted at\n * `_sealed_cek/<collection>/<id>/<pid>`. The grantor writes one per\n * (record, recipient host) pair. `payload` is the base64 of the bytes returned\n * by {@link RecipientSealer.sealForRecipient} over a UTF-8\n * `JSON.stringify({@link SealedCekBinding})`.\n *\n * `expiresAt` is duplicated here for a cheap pre-unseal reject, but is NOT\n * authoritative — the binding inside `payload` carries the expiry the host\n * verifies after unsealing, so a tampered delivery envelope cannot extend a\n * grant.\n */\nexport interface SealedCekDeliveryEnvelope {\n /** Envelope schema version. */\n readonly v: 1\n /** Magic marker for forensics + format detection. */\n readonly _noydb_sealed_cek: 1\n /** Recipient host provider id; matches the sealer's `.id` / hint `pid`. */\n readonly pid: string\n /** base64 of the sealed {@link SealedCekBinding} bytes. */\n readonly payload: string\n /** Fast-path expiry hint (ISO 8601). Authoritative copy is inside `payload`. */\n readonly expiresAt: string\n}\n\n/**\n * The plaintext struct sealed for the recipient host. After the host unseals\n * `SealedCekDeliveryEnvelope.payload` it parses this and MUST verify:\n * - `collection` + `id` match the record envelope it is decrypting, and\n * - `expiresAt` has not passed (authoritative expiry check).\n *\n * `cek` is the base64 of the raw 32-byte AES-256-GCM record CEK.\n */\nexport interface SealedCekBinding {\n /** Collection the CEK belongs to. */\n readonly collection: string\n /** Record id the CEK belongs to. */\n readonly id: string\n /** base64 of the raw AES-256-GCM CEK bytes. */\n readonly cek: string\n /** Authoritative expiry (ISO 8601). */\n readonly expiresAt: string\n}\n\n/**\n * Placeholder returned by `getAtTier()` in `'ghost'` mode when a\n * record is at a tier the caller cannot decrypt. Record existence is\n * advertised — the id and tier are visible — but contents are\n * withheld. `canElevateFrom` lists user ids authorized to elevate\n * access for this caller when known; absent when the workflow is\n * not configured.\n */\nexport interface GhostRecord {\n readonly _ghost: true\n readonly _tier: number\n readonly canElevateFrom?: readonly string[]\n}\n\n/** Control what lower-tier reads see above their clearance. */\nexport type TierMode = 'invisibility' | 'ghost'\n\n/**\n * Event emitted when a record at a tier above the caller's inherent\n * clearance is read or written successfully (via elevation or\n * delegation). Always written to the ledger; subscribers get a\n * real-time feed.\n */\nexport interface CrossTierAccessEvent {\n readonly actor: string\n readonly collection: string\n readonly id: string\n readonly tier: number\n /** How the caller gained tier access: they elevated it, or a delegation is active. */\n readonly authorization: 'elevation' | 'delegation' | 'inherent'\n readonly op: 'get' | 'put' | 'elevate' | 'demote'\n readonly ts: string\n /**\n * When `authorization === 'elevation'`, the audit reason string the\n * caller passed to `vault.elevate(...)`. Empty for inherent /\n * delegation paths.\n */\n readonly reason?: string\n /**\n * When `authorization === 'elevation'`, the tier the caller's\n * keyring effectively held BEFORE elevation. Useful for audit\n * dashboards distinguishing \"operator elevating to 2\" from\n * \"inherent tier-2 write.\"\n */\n readonly elevatedFrom?: number\n}\n\n/**\n * A single deterministic-ciphertext index slot on an envelope. Stored\n * as `iv:data` (both base64, colon-separated) so a single string per\n * field keeps the envelope compact.\n */\nexport type DeterministicCipher = string\n\n// ─── Vault Snapshot ──────────────────────────────────────────────\n\n/** All records across all collections for a compartment. */\nexport type VaultSnapshot = Record<string, Record<string, EncryptedEnvelope>>\n\n/**\n * Result of a single page fetch via the optional `listPage` adapter extension.\n *\n * `items` carries the actual encrypted envelopes (not just ids) so the\n * caller can decrypt and emit a single record without an extra `get()`\n * round-trip per id. `nextCursor` is `null` on the final page.\n */\nexport interface ListPageResult {\n /** Encrypted envelopes for this page, in adapter-defined order. */\n items: Array<{ id: string; envelope: EncryptedEnvelope }>\n /** Opaque cursor for the next page, or `null` if this was the last page. */\n nextCursor: string | null\n}\n\n// ─── Store Interface ───────────────────────────────────────────────────\n\nexport interface NoydbStore {\n /**\n * Optional human-readable store name (e.g. 'memory', 'file', 'dynamo').\n * Used in diagnostic messages and the listPage fallback warning. Stores\n * are encouraged to set this so logs are clearer about which backend is\n * involved when something goes wrong.\n */\n name?: string\n\n /**\n * Optional declared store capabilities (CAS atomicity, native tx, blob\n * size limits, auth). Consumers that require a capability — e.g.\n * `vault.sequence().next()` needs `casAtomic` — read it here.\n */\n capabilities?: StoreCapabilities\n\n /** Get a single record. Returns null if not found. */\n get(vault: string, collection: string, id: string): Promise<EncryptedEnvelope | null>\n\n /** Put a record. Throws ConflictError if expectedVersion doesn't match. */\n put(\n vault: string,\n collection: string,\n id: string,\n envelope: EncryptedEnvelope,\n expectedVersion?: number,\n ): Promise<void>\n\n /** Delete a record. */\n delete(vault: string, collection: string, id: string): Promise<void>\n\n /** List all record IDs in a collection. */\n list(vault: string, collection: string): Promise<string[]>\n\n /** Load all records for a vault (initial hydration). */\n loadAll(vault: string): Promise<VaultSnapshot>\n\n /** Save all records for a vault (bulk write / restore). */\n saveAll(vault: string, data: VaultSnapshot): Promise<void>\n\n /** Optional connectivity check for sync engine. */\n ping?(): Promise<boolean>\n\n /**\n * The store's authoritative time as a bounded-uncertainty interval.\n * Present iff `capabilities.serverWriteTime` is true. Monotonic\n * non-decreasing across calls on a single store.\n */\n getStoreTime?(): Promise<StoreTime>\n\n /**\n * Optional: list record IDs in a collection that have `_ts` after `since`.\n * Used by partial sync (`pull({ modifiedSince })`). Stores that omit this\n * fall back to a full `loadAll` + client-side timestamp filter.\n */\n listSince?(vault: string, collection: string, since: string): Promise<string[]>\n\n /**\n * Optional pagination extension. Stores that implement `listPage` get\n * the streaming `Collection.scan()` fast path; stores that don't are\n * silently fallen back to a full `loadAll()` + slice (with a one-time\n * console.warn).\n *\n * `cursor` is opaque to the core — each store encodes its own paging\n * state (DynamoDB: base64 LastEvaluatedKey JSON; S3: ContinuationToken;\n * memory/file/browser: numeric offset of a sorted id list). Pass\n * `undefined` to start from the beginning.\n *\n * `limit` is a soft upper bound on `items.length`. Stores MAY return\n * fewer items even when more exist (e.g. if the underlying store has\n * its own page size cap), and MUST signal \"no more pages\" by returning\n * `nextCursor: null`.\n *\n * The 6-method core contract is unchanged — this is an additive\n * extension discovered via `'listPage' in adapter`.\n */\n listPage?(\n vault: string,\n collection: string,\n cursor?: string,\n limit?: number,\n ): Promise<ListPageResult>\n\n /**\n * Optional pub/sub for real-time presence.\n * Publish an encrypted payload to a presence channel.\n * Falls back to storage-based polling when absent.\n */\n presencePublish?(channel: string, payload: string): Promise<void>\n\n /**\n * Optional pub/sub for real-time presence.\n * Subscribe to a presence channel. Returns an unsubscribe function.\n * Falls back to storage-based polling when absent.\n */\n presenceSubscribe?(channel: string, callback: (payload: string) => void): () => void\n\n /**\n * Optional cross-vault enumeration extension.\n *\n * Returns the names of every top-level vault the store\n * currently stores. Used by `Noydb.listAccessibleVaults()` to\n * enumerate the universe of vaults before filtering down to\n * the ones the calling principal can actually unwrap.\n *\n * **Why this is optional:** the storage shape of compartments\n * differs across backends. Memory and file stores store\n * vaults as top-level keys / directories and can enumerate\n * them in O(1) calls. DynamoDB stores everything in a single table\n * keyed by `(compartment#collection, id)` — enumerating compartments\n * requires either a Scan (expensive, eventually consistent, leaks\n * ciphertext metadata) or a dedicated GSI that the consumer\n * provisioned. S3 needs a prefix list (cheap if enabled, ACL-sensitive\n * otherwise). Browser localStorage can scan keys by prefix.\n *\n * Stores that cannot implement `listVaults` cheaply or\n * cleanly should omit it. Core surfaces a `StoreCapabilityError`\n * with a clear message when a caller invokes\n * `listAccessibleVaults()` against a store that doesn't\n * provide this method, so consumers know to either upgrade their\n * store, provide a candidate list explicitly to `queryAcross()`,\n * or fall back to maintaining the compartment index out of band.\n *\n * **Privacy note:** `listVaults` returns *every* compartment\n * the store has, not just the ones the caller can access. The\n * existence-leak filtering (returning only compartments whose\n * keyring the caller can unwrap) happens in core, not in the\n * store. The store is trusted to know its own contents — that\n * is not a leak in the threat model. The leak the API guards\n * against is the *return value* of `listAccessibleVaults()`\n * exposing existence to a downstream observer who only sees that\n * function's output.\n *\n * The 6-method core contract is unchanged — this is an additive\n * extension discovered via `'listVaults' in store`.\n */\n listVaults?(): Promise<string[]>\n\n /**\n * Optional: generate a presigned URL for direct client download.\n * Only meaningful for object stores (S3, GCS) that support URL signing.\n * Returns a time-limited URL that fetches the encrypted envelope directly.\n * The caller must decrypt client-side (the URL returns ciphertext).\n */\n presignUrl?(vault: string, collection: string, id: string, expiresInSeconds?: number): Promise<string>\n\n /**\n * Optional: estimate current storage usage.\n * Returns `{ usedBytes, quotaBytes }` or null if the store cannot estimate.\n * Used by quota-aware routing to detect overflow conditions.\n */\n estimateUsage?(): Promise<{ usedBytes: number; quotaBytes: number } | null>\n\n /**\n * Optional multi-record atomic write.\n *\n * When present, `db.transaction(async (tx) => { ... })` uses this to\n * commit every staged op in one storage-layer transaction — either\n * all ops land or none do, regardless of which records they touch.\n * Every `TxOp.expectedVersion` (when set) must be honored atomically\n * alongside the write; any violation throws `ConflictError` and the\n * whole batch fails.\n *\n * Stores that omit this fall through to the hub's per-record OCC\n * fallback: pre-flight CAS check, then sequential `put`/`delete`\n * with best-effort unwind on mid-batch failure (see\n * `runTransaction` for the exact semantics and crash window).\n *\n * Native implementations: `to-memory` (single Map mutation),\n * `to-dynamo` (`TransactWriteItems`), `to-browser-idb` (one\n * `readwrite` transaction). File / S3 cannot implement this\n * atomically and should omit the method.\n */\n tx?(ops: readonly TxOp[]): Promise<void>\n}\n\n/**\n * A single staged operation inside a `db.transaction(fn)` commit. The\n * hub assembles `TxOp[]` from the user's `tx.collection().put/delete`\n * calls, encrypts any `record` values into `envelope`, and hands the\n * array to `NoydbStore.tx()` when the store supports atomic batch\n * writes. Stores that implement `tx()` MUST honor every\n * `expectedVersion` atomically against the stored envelope version.\n */\nexport interface TxOp {\n readonly type: 'put' | 'delete'\n readonly vault: string\n readonly collection: string\n readonly id: string\n /** Populated for `type: 'put'` — the encrypted envelope to write. */\n readonly envelope?: EncryptedEnvelope\n /** Optional per-record CAS. Mismatch must throw `ConflictError`. */\n readonly expectedVersion?: number\n}\n\n// ─── Store Factory Helper ──────────────────────────────────────────────\n\n/** Type-safe helper for creating store factories. */\nexport function createStore<TOptions>(\n factory: (options: TOptions) => NoydbStore,\n): (options: TOptions) => NoydbStore {\n return factory\n}\n\n// ─── Keyring ───────────────────────────────────────────────────────────\n\n/**\n * Interchange formats `@noy-db/as-*` packages can produce. `'*'` is a\n * wildcard granting every current + future plaintext format.\n */\nexport type ExportFormat =\n | 'xlsx'\n | 'csv'\n | 'json'\n | 'ndjson'\n | 'xml'\n | 'sql'\n | 'pdf'\n | 'blob'\n | 'zip'\n | '*'\n\n/**\n * Owner-granted export capability on a keyring.\n *\n * Two independent dimensions:\n *\n * - `plaintext` — per-format allowlist for record formatters + blob\n * extractors that emit plaintext bytes (`as-xlsx`, `as-csv`,\n * `as-blob`, `as-zip`, …). **Defaults to empty** for every role;\n * the owner/admin must positively grant per-format (or `'*'`).\n * - `bundle` — boolean for `.noydb` encrypted container export\n * (`as-noydb`). **Default policy: on for owner/admin, off for\n * operator/viewer/client** — applied when the field is absent or\n * undefined (see `hasExportCapability`).\n */\nexport interface ExportCapability {\n readonly plaintext?: readonly ExportFormat[]\n readonly bundle?: boolean\n}\n\n/**\n * Owner-granted import capability on a keyring (sibling of\n * `ExportCapability`, issue ).\n *\n * Two independent dimensions:\n *\n * - `plaintext` — per-format allowlist for `as-*` readers that ingest\n * plaintext bytes (`as-csv`, `as-json`, `as-ndjson`, `as-zip`, …).\n * Defaults to empty for every role; the owner/admin must positively\n * grant per-format (or `'*'`).\n * - `bundle` — boolean gate for `.noydb` bundle import. **Defaults to\n * `false` for every role**, including owner/admin. Import is more\n * dangerous than export (corrupts vs leaks), so the policy is\n * default-closed across the board — the owner explicitly opts a\n * keyring in via `db.grant({ importCapability: { bundle: true } })`.\n */\nexport interface ImportCapability {\n readonly plaintext?: readonly ExportFormat[]\n readonly bundle?: boolean\n}\n\n/**\n * Forward-declared on-disk shape for `VaultPolicy` — the actual policy\n * model is declared further down in this file (#9), see {@link VaultPolicy}.\n * Declared here as an `unknown`-typed map (rather than `VaultPolicy` itself)\n * so the `KeyringFile.policy` field can still round-trip foreign/older\n * documents that don't strictly satisfy the current shape.\n *\n * @internal\n */\nexport type VaultPolicyOnDisk = Record<string, unknown>\n\n/**\n * Recovery profile enrolled at vault creation.\n *\n * - `paper` — `on-recovery` codes (the standard end-to-end profile).\n * - `shamir` / `multi-channel` / `admin-mediated` — API surface ships;\n * per-profile dispatch lands in follow-up issues. Calling\n * `db.recoverSecret` against these throws\n * {@link RecoveryProfileNotImplementedError}.\n */\nexport type RecoveryEnrollment =\n | {\n readonly profile: 'paper'\n /** Number of single-use codes to print at enrollment. */\n readonly codes: number\n }\n | {\n readonly profile: 'shamir'\n readonly k: number\n readonly n: number\n readonly trustees: ReadonlyArray<string>\n }\n | {\n readonly profile: 'multi-channel'\n readonly email?: string\n readonly pin?: boolean\n readonly paperCodes?: number\n }\n | {\n readonly profile: 'admin-mediated'\n readonly grantorUserId: string\n }\n\n/**\n * One tier-2 authenticator slot inside a keyring file. Each slot\n * independently wraps the SAME KEK under a method-specific derived key\n * (LUKS pattern). Adding or removing a slot is a constant-time keyring\n * write — no DEK re-keying required.\n *\n * @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/session-tiers.md → Tier 2 — Authenticate (multi-slot)\n */\n/**\n * Shared fields across all authenticator slot variants. The variant\n * (`KeyringAuthenticatorWrappingKEK` vs `KeyringAuthenticatorWrappingDEKs`)\n * carries the actual wrapped material; everything below is identity +\n * metadata only.\n */\ninterface KeyringAuthenticatorBase {\n /** Caller-chosen identifier — e.g. `'webauthn-yubikey-blue'`, `'oidc-google'`, `'password'`. */\n readonly id: string\n /** Method family — selects which `@noy-db/on-*` package handles unlock. */\n readonly method: 'webauthn' | 'oidc' | 'password'\n /** ISO-8601 timestamp at which the slot was added. */\n readonly enrolled_at: string\n /**\n * Which session tier ENROLLED this slot. Tier 1 enrolls a fresh slot;\n * tier 2 may add a sibling slot when the active policy permits.\n */\n readonly enrolled_via_tier: 1 | 2\n /**\n * Method-specific metadata: WebAuthn cred id, OIDC issuer/sub, PBKDF2\n * salt for `on-password`, etc. The schema is open by design — the\n * `@noy-db/on-*` package owns the contents.\n */\n readonly meta: Record<string, unknown>\n}\n\n/**\n * Slot that wraps the KEK directly under a method-derived AES-KW key.\n * Used by ceremonies where the on-* package can produce/recover an\n * extractable KEK from its own credential — WebAuthn (PRF-derived\n * wrapping key) and split-key OIDC.\n *\n * `wrapKind` is optional/absent on older slots — those\n * legacy slots are treated as wrap-KEK by default at unlock time.\n */\nexport interface KeyringAuthenticatorWrappingKEK extends KeyringAuthenticatorBase {\n readonly wrapKind?: 'kek'\n /** Base64 wrapped-KEK ciphertext under the method-derived key. */\n readonly wrapped_kek: string\n /** XOR guard — wrap-KEK slots must NOT carry wrap-DEKs material. */\n readonly wrapped_deks?: never\n /** XOR guard — wrap-KEK slots must NOT carry wrap-DEKs material. */\n readonly iv?: never\n}\n\n/**\n * Slot that wraps the DEK set (not the KEK) under a method-derived\n * AES-GCM key — sidesteps the non-extractable-KEK constraint by\n * encrypting the serialized `{ deks: { collection: rawDekBase64 } }`\n * directly. Mirrors the format used by `mintPaperRecoveryEntry`\n * (`PaperRecoveryEntry`) and `@noy-db/on-pin`'s `PinResumeState` —\n * the unified wrap-DEKs primitive across tier-0 / tier-2 / tier-3.\n *\n * Trade-off: a slot of this kind reconstructs `UnlockedKeyring` with\n * `kek: null` after unlock. That is semantically correct for tier-2\n * (sensitive ops like `enrollAuthenticator` / `rotateSecret`\n * require a tier-1 unlock anyway) and matches how `@noy-db/on-pin`\n * already behaves at tier 3.\n *\n * @see `mintPaperRecoveryEntry` in `team/recovery.ts` — same shape on\n * a different on-disk path (`_meta/recovery-paper`).\n */\nexport interface KeyringAuthenticatorWrappingDEKs extends KeyringAuthenticatorBase {\n readonly wrapKind: 'deks'\n /** Base64 AES-GCM ciphertext of `{ deks: { collection: base64rawDek } }`. */\n readonly wrapped_deks: string\n /** Base64 AES-GCM IV used for the `wrapped_deks` ciphertext. */\n readonly iv: string\n /** XOR guard — wrap-DEKs slots must NOT carry wrap-KEK material. */\n readonly wrapped_kek?: never\n}\n\n/**\n * Discriminated union over the two wrap-format variants. Reads from\n * disk should always go through this type so the variant is preserved.\n *\n * Discriminator: `wrapKind`. Absent → wrap-KEK (legacy / WebAuthn /\n * OIDC). Present and `'deks'` → wrap-DEKs (password / future on-* that\n * want to sidestep extractable-KEK).\n *\n * The type-level XOR enforces \"exactly one of `wrapped_kek` /\n * `wrapped_deks` is present\" — a structural guarantee that the runtime\n * dispatch is safe.\n */\nexport type KeyringAuthenticator =\n | KeyringAuthenticatorWrappingKEK\n | KeyringAuthenticatorWrappingDEKs\n\nexport interface KeyringFile {\n readonly _noydb_keyring: typeof NOYDB_KEYRING_VERSION\n readonly user_id: string\n readonly display_name: string\n readonly role: Role\n readonly permissions: Permissions\n readonly deks: Record<string, string>\n readonly salt: string\n readonly created_at: string\n readonly granted_by: string\n /**\n * Secret canary — base64 AES-KW-wrapped form of a known constant\n * 256-bit value, wrapped under the keyring's KEK.\n *\n * Optional: older keyrings load with no canary and fall back to\n * the multi-DEK corruption heuristic. Newer keyrings\n * carry one and let `loadKeyring` distinguish wrong-secret\n * from corruption even when ALL DEKs (including a single-DEK keyring's\n * sole DEK) are corrupted.\n *\n * AES-KW is deterministic — every write site mints fresh on each\n * persist; same KEK + same constant input always produces the same\n * ciphertext, so this round-trips without state.\n */\n readonly canary?: string\n /**\n * Tier-2 authenticator slots (multi-slot keyring extension).\n * Optional / append-only: keyring files written before the\n * extension load with an empty list. Each slot independently wraps\n * the same KEK; any one of them unlocks.\n *\n * @see KeyringAuthenticator\n */\n readonly authenticators?: readonly KeyringAuthenticator[]\n /**\n * Per-keyring policy override (reserved). The on-disk format\n * accepts the field for forward compatibility with the Option C\n * merge engine deferred to a later release; v1.0 reads only the\n * vault-level `_meta/policy` document, so this field is parsed and\n * round-tripped but never enforced.\n */\n readonly policy?: VaultPolicyOnDisk\n /**\n * Optional — authorization spec capability bits. Absent on keyrings written\n * before the RFC implementation. Loading falls back to role-based\n * defaults (owner/admin get bundle-on, everyone else off).\n */\n readonly export_capability?: ExportCapability\n /**\n * Optional bundle-slot expiry. ISO-8601 timestamp; past\n * the cutoff `loadKeyring` throws `KeyringExpiredError` before any\n * DEK unwrap is attempted. Useful for time-boxed audit access:\n * \"this slot works for 30 days then becomes opaque to its holder.\"\n *\n * Absent on live keyrings written via `db.grant()` — the field is\n * meaningful for `BundleRecipient` slots produced by\n * `writePod({ recipients: [...] })`. Setting it on a live\n * keyring is allowed but unusual.\n */\n readonly expires_at?: string\n /**\n * Optional — issue import-capability bits. Absent on keyrings\n * written before landed. Loading falls back to default-closed\n * for every role and every format.\n */\n readonly import_capability?: ImportCapability\n /**\n * hierarchical access clearance. Absent → 0 (advisory;\n * the real check is whether the DEK map carries a `collection#tier`\n * entry for the requested tier). Owners and admins default to the\n * highest tier they have DEKs for at grant time.\n */\n readonly clearance?: number\n}\n\n// ─── Backup ────────────────────────────────────────────────────────────\n\nexport interface VaultBackup {\n readonly _noydb_backup: typeof NOYDB_BACKUP_VERSION\n readonly _compartment: string\n readonly _exported_at: string\n readonly _exported_by: string\n readonly keyrings: Record<string, KeyringFile>\n readonly collections: VaultSnapshot\n /**\n * Internal collections (`_ledger`, `_ledger_deltas`, `_history`, `_sync`, …)\n * captured alongside the data collections. Optional for backwards\n * compat with backups, which only stored data collections —\n * loading a backup leaves the ledger empty (and `verifyBackupIntegrity`\n * skips the chain check, surfacing only a console warning).\n */\n readonly _internal?: VaultSnapshot\n /**\n * Verifiable-backup metadata. Embeds the ledger head at\n * dump time so `load()` can cross-check that the loaded chain matches\n * exactly what was exported. A backup whose chain has been tampered\n * with — either by modifying ledger entries or by modifying data\n * envelopes that the chain references — fails this check.\n *\n * Optional for backwards compat with backups; missing means\n * \"legacy backup, load with a warning, no integrity check\".\n */\n readonly ledgerHead?: {\n /** Hex sha256 of the canonical JSON of the last ledger entry. */\n readonly hash: string\n /** Sequential index of the last ledger entry. */\n readonly index: number\n /** ISO timestamp captured at dump time. */\n readonly ts: string\n }\n}\n\n// ─── Export ────────────────────────────────────────────────────────────\n\n/**\n * Options for `Vault.exportStream()` and `Vault.exportJSON()`.\n *\n * The defaults match the most common consumer pattern: one chunk per\n * collection, no ledger metadata. Per-record streaming and ledger-head\n * inclusion are opt-in because both add structure most consumers don't\n * need.\n */\nexport interface ExportStreamOptions {\n /**\n * `'collection'` (default) yields one chunk per collection with all\n * records bundled in `chunk.records`. `'record'` yields one chunk per\n * record, useful for arbitrarily large collections that should never\n * be materialized as a single array.\n */\n readonly granularity?: 'collection' | 'record'\n\n /**\n * When `true`, every chunk includes the current compartment ledger\n * head under `chunk.ledgerHead`. The value is identical across every\n * chunk in a single export (one ledger per compartment). Forward-\n * compatible with future partition work where the head would become\n * per-partition. Default: `false`.\n */\n readonly withLedgerHead?: boolean\n /**\n * Export locale (BCP 47, e.g. `'th'`). When set, records are read at this\n * locale through the **`export` layer**: `i18nText` fields collapse to\n * the locale string (honoring each field's `export`-layer `onMissing` policy)\n * and `dictKey`/`staticDict` `<field>Label`s are resolved — a single-locale\n * export. The raw `dictionaries` snapshot is then redundant and omitted. This\n * applies to BOTH `exportStream()` and `exportJSON()`.\n *\n * Default: `undefined` — raw `{locale}` maps + the `_dictionaries` snapshot\n * (a full, all-locale backup; format packages apply their own locale strategy).\n */\n readonly resolveLabels?: string\n}\n\n/**\n * One chunk yielded by `Vault.exportStream()`.\n *\n * `granularity: 'collection'` yields one chunk per collection with the\n * full record array in `records`. `granularity: 'record'` yields one\n * chunk per record with `records` containing exactly one element — the\n * `schema` and `refs` metadata is repeated on every chunk so consumers\n * doing per-record streaming don't have to thread state across yields.\n */\nexport interface ExportChunk<T = unknown> {\n /** Collection name (no leading underscore — internal collections are filtered out). */\n readonly collection: string\n\n /**\n * Standard Schema validator attached to the collection at `collection()`\n * construction time, or `null` if no schema was provided. Surfaced so\n * downstream serializers (`@noy-db/as-*` packages, custom\n * exporters) can produce schema-aware output (typed CSV headers, XSD\n * generation, etc.) without poking at collection internals.\n */\n readonly schema: StandardSchemaV1<unknown, T> | null\n\n /**\n * Foreign-key references declared on the collection via the `refs`\n * option, as the `{ field → { target, mode } }` map produced by\n * `RefRegistry.getOutbound`. Empty object when no refs were declared.\n */\n readonly refs: Record<string, { readonly target: string; readonly mode: 'strict' | 'warn' | 'cascade' }>\n\n /**\n * Decrypted, ACL-scoped, schema-validated records. Length 1 in\n * `granularity: 'record'` mode, full collection in `granularity: 'collection'`\n * mode. Records are returned by reference from the collection's eager\n * cache where applicable — consumers must treat them as immutable.\n */\n readonly records: T[]\n\n /**\n * Dictionary snapshots for every `dictKey` field declared on this\n * collection. Captured once at stream-start and held\n * constant across all chunks within the same export — a rename\n * mid-export does not change the snapshot. `undefined` when the\n * collection has no `dictKeyFields`.\n *\n * Shape: `{ [fieldName]: { [stableKey]: { [locale]: label } } }`\n *\n * @example\n * ```ts\n * chunk.dictionaries?.status?.paid?.th // → 'ชำระแล้ว'\n * ```\n */\n readonly dictionaries?: Record<\n string, // field name\n Record<string, Record<string, string>> // stable key → locale → label\n >\n\n /**\n * Vault ledger head at export time. Present only when\n * `exportStream({ withLedgerHead: true })` was called. Identical\n * across every chunk in the same export — included on every chunk\n * for forward-compatibility with future per-partition ledgers, where\n * the value will differ per chunk.\n */\n readonly ledgerHead?: {\n readonly hash: string\n readonly index: number\n readonly ts: string\n }\n}\n\n// ─── Sync ──────────────────────────────────────────────────────────────\n\nexport interface DirtyEntry {\n readonly vault: string\n readonly collection: string\n readonly id: string\n readonly action: 'put' | 'delete'\n readonly version: number\n readonly timestamp: string\n}\n\nexport interface SyncMetadata {\n readonly _noydb_sync: typeof NOYDB_SYNC_VERSION\n readonly last_push: string | null\n readonly last_pull: string | null\n readonly dirty: DirtyEntry[]\n}\n\nexport interface Conflict {\n readonly vault: string\n readonly collection: string\n readonly id: string\n readonly local: EncryptedEnvelope\n readonly remote: EncryptedEnvelope\n readonly localVersion: number\n readonly remoteVersion: number\n /**\n * Present only when the collection uses `conflictPolicy: 'manual'`.\n * Call `resolve(winner)` to commit the winning envelope, or\n * `resolve(null)` to defer (conflict stays queued for the next sync).\n * Called synchronously inside the `sync:conflict` event handler.\n */\n readonly resolve?: (winner: EncryptedEnvelope | null) => void\n}\n\n/**\n * #590: sync suppressed a live envelope because a crypto-shred tombstone is\n * terminal for its record id. Reported on push/pull results (`erasures`) and\n * via the `'sync:erasure'` event; conflict resolvers are never consulted for\n * tombstone pairs.\n */\nexport interface ErasureEnforcement {\n readonly vault: string\n readonly collection: string\n readonly id: string\n /** The winning tombstone (as stored after enforcement). */\n readonly tombstone: EncryptedEnvelope\n /** The live envelope that lost: a suppressed dirty local edit, or the remote copy destroyed by re-assertion. */\n readonly suppressed: EncryptedEnvelope\n readonly direction: 'pull' | 'push'\n}\n\n/**\n * A same-device cross-tab write conflict: another tab overwrote a\n * document this tab had written, having diverged from an older base. Records\n * are decrypted (cross-tab handlers reconcile in plaintext). `base` is the\n * common ancestor from history, or null when history is unavailable.\n */\nexport interface WriteConflict {\n readonly vault: string\n readonly collection: string\n readonly docId: string\n readonly local: unknown\n readonly remote: unknown\n readonly base: unknown\n readonly localVersion: number\n readonly remoteVersion: number\n readonly baseVersion: number\n}\n\nexport type ConflictStrategy =\n | 'local-wins'\n | 'remote-wins'\n | 'version'\n | ((conflict: Conflict) => 'local' | 'remote')\n\n/**\n * Collection-level conflict policy.\n * Overrides the db-level `conflict` option for the specific collection.\n *\n * - `'last-writer-wins'` — higher `_ts` wins (timestamp LWW).\n * - `'first-writer-wins'` — lower `_v` wins (earlier version is preserved).\n * - `'manual'` — emits `sync:conflict` with a `resolve` callback. Call\n * `resolve(winner)` synchronously to commit or `resolve(null)` to defer.\n * - Custom fn — synchronous `(local: T, remote: T) => T`. Must be pure.\n *\n * **Delete-vs-edit caveat:** `'last-writer-wins'`, `'first-writer-wins'`,\n * and `'manual'` compare/hand over raw envelopes, so an edit CAN win over\n * a delete marker (a later `_ts`, an earlier `_v`, or the app's own\n * `resolve()` choice). A custom fn, and the CRDT merge modes `'lww-map'`/\n * `'rga'` (`crdtStrategy`), CANNOT: their shared resolver wrapper decrypts\n * both sides first and short-circuits to whichever side is the\n * shredded/tombstoned one *before* the merge function (or CRDT merge)\n * ever runs — delete unconditionally wins. CRDT mode `'yjs'` is the\n * exception among CRDT modes: it never decrypts and falls back to a\n * plain higher-`_v`-wins compare, so an edit can beat a delete there too.\n */\nexport type ConflictPolicy<T> =\n | 'last-writer-wins'\n | 'first-writer-wins'\n | 'manual'\n | ((local: T, remote: T) => T)\n\n/**\n * Envelope-level resolver registered per collection with the SyncEngine.\n * Receives the `id` of the conflicting record and both envelopes.\n * Returns the winning envelope, or `null` to defer resolution.\n * @internal\n */\nexport type CollectionConflictResolver = (\n id: string,\n local: EncryptedEnvelope,\n remote: EncryptedEnvelope,\n) => Promise<EncryptedEnvelope | null>\n\n/** Options for targeted push operations. */\nexport interface PushOptions {\n /** Only push records belonging to these collections. Omit to push all dirty. */\n collections?: string[]\n}\n\n/** Options for targeted pull operations. */\nexport interface PullOptions {\n /** Only pull these collections. Omit to pull all. */\n collections?: string[]\n /**\n * Only pull records with `_ts` strictly after this ISO timestamp.\n * Stores that implement `listSince` use it directly; others fall back\n * to a full scan with client-side filtering.\n */\n modifiedSince?: string\n /**\n * #807 — period-scoped pull (thin-client bootstrap). `{ current: true }`\n * pulls only records at-or-after the latest closed period's boundary;\n * an array of closed-period names backfills exactly those periods'\n * windows. Membership is by envelope write-time `_ts` (the freeze/\n * archive store-tier law — the engine never sees business dates).\n *\n * Never filtered regardless of this option: the `_periods` summaries +\n * companions (always pulled first — the navigation index), delete\n * markers/tombstones (the #589/#590 convergence law), and reserved\n * lookup collections. Composes with `collections` as an intersection.\n * Requires the periods service (`periodsStrategy: withPeriods()`) to\n * resolve windows once the vault holds `_periods` records. Push is\n * never period-filtered.\n */\n periods?: string[] | { current: true }\n}\n\nexport interface PushResult {\n readonly pushed: number\n readonly conflicts: Conflict[]\n readonly errors: Error[]\n /** #590: tombstone enforcements applied during this run (never resolver-visible). */\n readonly erasures?: ErasureEnforcement[]\n}\n\nexport interface PullResult {\n readonly pulled: number\n readonly conflicts: Conflict[]\n readonly errors: Error[]\n /** #590: tombstone enforcements applied during this run (never resolver-visible). */\n readonly erasures?: ErasureEnforcement[]\n /**\n * #807: present on period-scoped pulls only — per-phase KPI counters\n * (`summaries` = the `_periods` navigation index + companions; `records`\n * = everything after it). `records`/`bytes` count envelopes applied to\n * the local store; `bytes` approximates ciphertext payload size\n * (`_data` + `_iv` length), for bounding a first sync's download budget.\n */\n readonly phases?: {\n readonly summaries: { readonly records: number; readonly bytes: number }\n readonly records: { readonly records: number; readonly bytes: number }\n }\n}\n\n/** Result of a sync transaction commit. */\nexport interface SyncTransactionResult {\n readonly status: 'committed' | 'conflict'\n readonly pushed: number\n readonly conflicts: Conflict[]\n /** #590: staged writes suppressed by tombstone enforcement during commit. */\n readonly erasures?: ErasureEnforcement[]\n}\n\nexport interface SyncStatus {\n readonly dirty: number\n readonly lastPush: string | null\n readonly lastPull: string | null\n readonly online: boolean\n /**\n * Per-collection readiness under a `'phased'` pull policy (#809). **Absent**\n * for every other policy, and a collection the sequence never names is absent\n * from the map — `undefined` means *\"no claim made\"*, never a reason to gate\n * a UI. Only `'live'` asserts that a miss from `get()` is a real absence.\n */\n readonly readiness?: ReadonlyMap<string, ReadinessState>\n /** 1-based position in the phased sequence; `null` once it has drained. */\n readonly phase?: { readonly index: number; readonly total: number } | null\n}\n\n// ─── Sync Target ─────────────────────────────────────────\n\nexport type SyncTargetRole = 'sync-peer' | 'backup' | 'archive'\n\n/**\n * A sync target with role and optional per-target policy.\n *\n * | Role | Direction | Conflict resolution | Typical use |\n * |-------------|---------------|---------------------|--------------------------|\n * | `sync-peer` | Bidirectional | ConflictStrategy | DynamoDB live sync |\n * | `backup` | Push-only | N/A (receives merged)| S3 dump, Google Drive |\n * | `archive` | Push-only | N/A | IPFS, Git tags, S3 Lock |\n */\nexport interface SyncTarget {\n /** The store to sync with. */\n readonly store: NoydbStore\n /** Role determines sync direction and conflict handling. */\n readonly role: SyncTargetRole\n /**\n * Per-target sync policy. Inherits the store-category default when absent —\n * but only a *declared* policy starts automation (#897), so a target with no\n * `policy` and no instance-level `syncPolicy` syncs on explicit calls only.\n */\n readonly policy?: SyncPolicy\n /** Human-readable label for DevTools and audit logs. */\n readonly label?: string\n}\n\n// ─── Events ────────────────────────────────────────────────────────────\n\nexport interface ChangeEvent {\n readonly vault: string\n readonly collection: string\n readonly id: string\n readonly action: 'put' | 'delete'\n}\n\nexport interface NoydbEventMap {\n 'change': ChangeEvent\n 'error': Error\n /**\n * Same-instance signal that this vault's schema-fence state changed.\n * For UI integration. Cross-client coordination goes\n * through the store, not this event.\n */\n 'schema:fence-changed': { vault: string; currentSchemaVersion: number; fenceState: 'normal' | 'draining' | 'migrating' | 'complete' }\n 'sync:push': PushResult\n 'sync:pull': PullResult\n 'sync:erasure': ErasureEnforcement\n 'sync:conflict': Conflict\n 'write:conflict': WriteConflict\n 'sync:online': void\n 'sync:offline': void\n 'sync:backup-error': { vault: string; target: string; error: Error }\n 'history:save': { vault: string; collection: string; id: string; version: number }\n 'history:prune': { vault: string; collection: string; id: string; pruned: number }\n /**\n * A non-fatal i18n script violation under `onScriptViolation: 'warn' | 'filter'`.\n * 'warn' stored the value as-is; 'filter' stripped disallowed characters\n * (the event is the only signal the stored data was mutated). 'reject'\n * throws `ScriptViolationError` and emits nothing.\n */\n 'i18n:script-violation': {\n vault: string\n collection: string\n id: string\n mode: 'warn' | 'filter'\n warning: ScriptWarning\n }\n /**\n * Emitted when a persisted-index side-car put/delete fails after the\n * main record write already succeeded. The main record is durable; the\n * index mirror may have drifted. Operators reconcile via\n * `collection.reconcileIndex(field)`.\n */\n 'index:write-partial': {\n vault: string\n collection: string\n id: string\n action: 'put' | 'delete'\n error: Error\n }\n /**\n * emitted by `Collection.ensurePersistedIndexesLoaded()`\n * once per field on first lazy-mode query when\n * `reconcileOnOpen: 'auto' | 'dry-run'` is configured. `applied` is\n * `0` in `'dry-run'` mode. `skipped` is reserved for a future\n * drift-stamp optimization that short-circuits the reconcile when\n * the mirror version matches what's on disk — currently always\n * `false` (the full reconcile runs every session).\n */\n 'index:reconciled': {\n vault: string\n collection: string\n field: string\n missing: readonly string[]\n stale: readonly string[]\n applied: number\n skipped: boolean\n }\n /**\n * #638 Task 5 — a dispatch-driven derivation/rollup/MV output write targeted a row whose\n * period is closed. The write is SKIPPED (the historical value stands); the SOURCE write\n * that triggered the recompute still succeeded. See `kernel/via/dispatch.ts#putDerivedOutput`.\n * `source.id` may be a non-record sentinel (e.g. `'refreshView'`) for manual bulk-refresh-\n * triggered skips, not a real source record id.\n */\n 'derivation:skipped-frozen': DerivationSkippedFrozen\n /**\n * #654 — an ordinary-delete lookup-ref `cascade`/`nullify` propagation edge whose compare-key\n * could not be resolved from the backing row (matrix custom-key row unreadable — corruption\n * class). The delete itself proceeds (only `restrict` edges fail closed, via\n * `RestrictRefUnresolvableError`); this edge's propagation is skipped and reported here instead\n * of silently dropped — the ordinary-delete counterpart of the forget path's\n * `ForgetResult.lookupReferencesResidue` channel. `residue` entries are `backing:key:\n * collection.field`, one per un-propagated edge (see `VaultLinks.applyLookupRefsPropagation`).\n */\n 'lookup:propagation-residue': { vault: string; dimension: string; key: string; residue: readonly string[] }\n /**\n * #640 rider (#644 item 3) — the sync/cutover/restore dispatch wave's per-id recompute failed\n * (a genuine decrypt failure, a derive()/executor bug, a schema violation on the output, ...).\n * ADDITIVE to the existing `console.warn` in `runGraphDispatchWave` — never replaces it, so no\n * listener-dependent silence. One event per failed (collection, id); the wave still isolates\n * the failure to just that one record. See `kernel/via/dispatch.ts#runGraphDispatchWave`.\n */\n 'derivation:wave-error': { collection: string; id: string; error: unknown }\n}\n\n// ─── Grant / Revoke ────────────────────────────────────────────────────\n\nexport interface GrantOptions {\n readonly userId: string\n readonly displayName: string\n readonly role: Role\n readonly secret: string\n readonly permissions?: Permissions\n /**\n * Optional `@noy-db/as-*` export capability. Omit or\n * leave undefined to apply role-based defaults (see\n * `hasExportCapability` and `ExportCapability`).\n */\n readonly exportCapability?: ExportCapability\n /**\n * Optional `@noy-db/as-*` import capability (issue ). Omit or\n * leave undefined for default-closed semantics — no plaintext format\n * is grantable until positively listed; bundle import is denied.\n */\n readonly importCapability?: ImportCapability\n /**\n * Skip phrase-format strength validation (issue #7). Defaults to\n * false — `grant()` rejects phrases that don't meet the configured\n * `SecretPolicy`. Test fixtures and CLI scripts pass `true`.\n */\n readonly allowWeakSecret?: boolean\n /**\n * Initial user-envelope payload for the new principal. Sealed under\n * the same vault DEK (the reserved `_users` collection's DEK) and\n * persisted alongside the keyring during grant.\n *\n * **Bootstrap-only.** Once the new user activates and writes their\n * own envelope, the own-only write rule kicks in — admins cannot\n * edit a teammate's envelope after activation. Use this field for\n * pre-fill at invite time (e.g. \"displayName: Bob, locale: en-US\")\n * and let the user take over from there.\n *\n * Hub does not introspect the payload; it is JSON-serialized and\n * encrypted opaquely. Apps own the schema.\n *\n * @see docs/superpowers/specs/2026-05-05-user-envelope-design.md → Lifecycle\n */\n readonly initialProfile?: unknown\n}\n\n/**\n * Caller payload for `db.updateUser`. Mutate one or more\n * identity fields on an existing keyring without rotating any keys.\n *\n * `role`, `displayName`, and `permissions` live in the plaintext header\n * of `_keyring/<userId>` (the sync engine reads them without keys).\n * Mutating them is a JSON header swap — no DEK rewrap, no KEK\n * required, no authenticator slots touched. Tier-2 slots and recovery\n * enrollments survive unchanged. Last-write-wins through the existing\n * keyring put (same concurrency story as `db.grant` / `db.revoke`).\n *\n * Top-level fields are partial-merge: absent fields are not modified.\n * `null` on `displayName` clears the field (stored as the empty string;\n * UI consumers typically render the empty case by falling back to the\n * user id). `undefined` / absent leaves the field untouched. Mirrors\n * the `null`-as-clear convention `UserApi.updateMe` uses.\n *\n * `permissions`, however, is a **full replacement** at the map level —\n * passing `{ invoices: 'rw' }` REPLACES the entire permissions map,\n * silently dropping any other entries. To partially update, read the\n * current keyring and merge: `permissions: { ...current, invoices: 'rw' }`.\n * To clear all permissions, pass `permissions: {}` explicitly.\n *\n * Role-elevation guard: the same hierarchy as `db.grant`. Admins can\n * change `admin` / `operator` / `viewer` / `client` to and from each\n * other; admins cannot promote to or demote from `owner`. Owners can\n * do anything. Non-admin callers (operator/viewer/client) cannot call\n * `db.updateUser` at all — for self-displayName changes, use\n * `vault.user.updateMe` (the user-envelope API).\n */\nexport interface UpdateUserOptions {\n readonly userId: string\n readonly role?: Role\n readonly displayName?: string | null\n readonly permissions?: Permissions\n}\n\nexport interface RevokeOptions {\n readonly userId: string\n readonly rotateKeys?: boolean\n\n /**\n * Cascade behavior when the revoked user is an admin who has granted\n * other admins.\n *\n * - `'strict'` (default) — recursively revoke every admin that the\n * target (transitively) granted. The cascade walks the\n * `granted_by` field on each keyring file and stops at non-admin\n * leaves. All affected collections are accumulated and rotated in\n * a single pass at the end, so cascade cost is O(records in\n * affected collections), not O(records × cascade depth).\n *\n * - `'warn'` — leave the descendant admins in place but emit a\n * `console.warn` listing them. Useful for diagnostic dry runs and\n * for environments where the operator wants to clean up the\n * delegation tree manually.\n *\n * No effect when the target is not an admin (operators, viewers, and\n * clients cannot grant other users, so they have no delegation\n * subtree to cascade through). Defaults to `'strict'`.\n */\n readonly cascade?: 'strict' | 'warn'\n}\n\n// ─── Cross-vault queries ──────────────────────────────\n\n/**\n * One entry returned by `Noydb.listAccessibleVaults()`. Carries\n * the compartment id and the role the calling principal holds in it,\n * so the consumer can decide how to fan out without re-checking\n * permissions per vault.\n */\nexport interface AccessibleVault {\n readonly id: string\n readonly role: Role\n}\n\n/**\n * Options for `Noydb.listAccessibleVaults()`.\n */\nexport interface ListAccessibleVaultsOptions {\n /**\n * Minimum role the caller must hold to include a vault in the\n * result. Vaults where the caller's role is strictly *below*\n * this threshold are silently excluded. Defaults to `'client'`,\n * which means \"every vault I can unwrap is returned.\" Set to\n * `'admin'` for \"vaults where I can grant/revoke,\" or\n * `'owner'` for \"vaults I own.\"\n *\n * The privilege ordering used:\n * `client (1) < viewer (2) < operator (3) < admin (4) < owner (5)`\n *\n * Note: `viewer` and `client` are conceptually peers in the ACL\n * (neither can grant), but `viewer` has read-all access while\n * `client` has only explicit-collection read. The numeric order\n * reflects \"how much can this principal see,\" not \"how much can\n * this principal modify.\"\n */\n readonly minRole?: Role\n}\n\n/**\n * Options for `Noydb.queryAcross()`.\n */\nexport interface QueryAcrossOptions {\n /**\n * Maximum number of compartments to process in parallel. Defaults\n * to `1` (sequential) — conservative because the per-compartment\n * callback typically does its own I/O and an unbounded fan-out can\n * exhaust adapter connections (DynamoDB throughput, S3 socket\n * limits, browser fetch concurrency).\n *\n * Set to `4` or `8` for cloud-backed compartments where parallelism\n * is the whole point of fanning out. Set to `1` (default) for local\n * adapters where the disk I/O serializes anyway.\n */\n readonly concurrency?: number\n /**\n * Open shards non-creatingly — a missing grant throws instead of\n * self-provisioning. Default: `true` (create iff the vault has no\n * `_keyring/*`). Pass `false` for strict open-existing semantics\n * (e.g. federation read fan-out where shards are pre-provisioned\n * and an absent grant should fail closed).\n */\n readonly create?: boolean\n}\n\n/**\n * One entry in the array returned by `Noydb.queryAcross()`. Either\n * `result` is set (callback succeeded for this compartment) or\n * `error` is set (callback threw, or compartment failed to open).\n *\n * Per-compartment errors do **not** abort the overall fan-out — every\n * compartment is given a chance to run its callback, and the\n * partition between success and failure is exposed in the return\n * value. Consumers that want fail-fast semantics can check\n * `r.error !== undefined` and short-circuit themselves.\n */\nexport type QueryAcrossResult<T> =\n | { readonly vault: string; readonly result: T; readonly error?: undefined }\n | { readonly vault: string; readonly result?: undefined; readonly error: Error }\n\n// ─── User Info ─────────────────────────────────────────────────────────\n\nexport interface UserInfo {\n readonly userId: string\n readonly displayName: string\n readonly role: Role\n readonly permissions: Permissions\n readonly createdAt: string\n readonly grantedBy: string\n}\n\n// ─── Session ───────────────────────────────────────────────\n\n/**\n * Operations that a session policy can require re-authentication for.\n * Passed as the `requireReAuthFor` array in `SessionPolicy`.\n */\nexport type ReAuthOperation = 'export' | 'grant' | 'revoke' | 'rotate' | 'changeSecret'\n\n/**\n * Session policy controlling lifetime, re-auth requirements, and\n * background-lock behavior.\n *\n * All timeout values are in milliseconds. `undefined` means \"no limit.\"\n * The policy is evaluated lazily — it does not start timers itself;\n * enforcement happens at the Noydb call site.\n */\nexport interface SessionPolicy {\n /**\n * Idle timeout in ms. If no NOYDB operation is performed for this\n * duration, the session is revoked on the next operation attempt\n * (which will throw `SessionExpiredError`). The idle clock resets\n * on every successful operation.\n *\n * Default: `undefined` (no idle timeout).\n */\n readonly idleTimeoutMs?: number\n\n /**\n * Absolute timeout in ms from session creation. After this duration\n * the session is unconditionally revoked regardless of activity.\n *\n * Default: `undefined` (no absolute timeout).\n */\n readonly absoluteTimeoutMs?: number\n\n /**\n * Operations that require the user to re-authenticate (re-enter their\n * secret or perform a fresh WebAuthn assertion) before proceeding,\n * even if the session is still alive.\n *\n * Common pattern: `requireReAuthFor: ['export', 'grant']` — allow\n * read/write operations in the background but demand a fresh credential\n * for high-risk mutations.\n *\n * Default: `[]` (no extra re-auth requirements).\n */\n readonly requireReAuthFor?: readonly ReAuthOperation[]\n\n /**\n * If `true`, the session is revoked when the page goes to the background\n * (visibilitychange event, `document.hidden === true`). Useful for\n * high-sensitivity deployments where leaving the tab is treated as\n * a session boundary.\n *\n * No-op in non-browser environments (Node.js, workers without document).\n * Default: `false`.\n */\n readonly lockOnBackground?: boolean\n}\n\n// ─── i18n / Locale ─────────────────────────────────────\n\n/**\n * Locale-aware read options. Pass to `Collection.get()`, `list()`,\n * `query()`, and `scan()` to trigger per-record locale resolution for\n * `dictKey` and `i18nText` fields.\n *\n * - **`locale: 'raw'`** — skip resolution for `i18nText` fields and\n * return the full `{ [locale]: string }` map. Dict key fields still\n * return the stable key (no `<field>Label` added).\n * - **`fallback`** — single locale code or ordered list. Use `'any'` as\n * the last element to fall back to any present translation.\n *\n * When neither the call-level locale nor the compartment's default locale\n * is set, reading a record with `i18nText` fields throws\n * `LocaleNotSpecifiedError`.\n */\nexport interface LocaleReadOptions {\n /**\n * The target locale code (e.g. `'th'`), or `'raw'` to return the full\n * language map without resolution.\n */\n readonly locale?: string\n /**\n * Fallback locale or ordered fallback chain. Use `'any'` as the last\n * element to fall back to any present translation.\n */\n readonly fallback?: string | readonly string[]\n /**\n * @internal — the resolution layer this read belongs to (`'read'` by\n * default). Threaded by layer-tagged read facades (guard / derivation)\n * so `applyI18nLocale` and dictKey `resolvePolicy` select that layer's\n * `onMissing` policy instead of the `'read'` policy. Not part of the\n * public read API — callers select policy via the field's `onMissing`\n * map, not by setting this.\n */\n readonly _layer?: Layer\n}\n\n// ─── plaintextTranslator hook ──────────────────────────────\n\n/**\n * Context passed to the consumer-supplied `plaintextTranslator` function.\n * The hook receives the source text plus enough metadata to route it to the\n * right translation service and record what it did.\n */\nexport interface PlaintextTranslatorContext {\n /** The plaintext string to translate. */\n readonly text: string\n /** BCP 47 source locale (the locale the text is written in). */\n readonly from: string\n /** BCP 47 target locale to translate into. */\n readonly to: string\n /** The schema field name that triggered the translation. */\n readonly field: string\n /** The collection the record is being put into. */\n readonly collection: string\n}\n\n/**\n * A consumer-supplied async function that translates a single string\n * from one locale to another. noy-db ships no built-in translator.\n *\n * **Security:** this function receives plaintext. The consumer is\n * responsible for the data policy of whatever service it calls. See\n * `NOYDB_SPEC.md § Zero-Knowledge Storage` and the `plaintextTranslator`\n * JSDoc on `NoydbOptions` for the full invariant statement.\n */\nexport type PlaintextTranslatorFn = (\n ctx: PlaintextTranslatorContext,\n) => Promise<string>\n\n/**\n * One entry in the in-process translator audit log. Cleared when\n * `db.close()` is called — same lifetime as the KEK and DEKs.\n *\n * Deliberately omits any content hash or translated-text fingerprint\n * to prevent correlation attacks on the audit trail.\n */\nexport interface TranslatorAuditEntry {\n readonly type: 'translator-invocation'\n /** Schema field name that was translated. */\n readonly field: string\n /** Collection the record belongs to. */\n readonly collection: string\n /** Source locale. */\n readonly fromLocale: string\n /** Target locale. */\n readonly toLocale: string\n /**\n * Consumer-provided translator name from\n * `NoydbOptions.plaintextTranslatorName`. Defaults to `'anonymous'`\n * when not supplied.\n */\n readonly translatorName: string\n /** ISO 8601 timestamp of the invocation. */\n readonly timestamp: string\n /**\n * `true` when the result was served from the in-process cache rather\n * than by calling the translator function. Present only on cache hits\n * so the absence of the field also communicates a cache miss.\n */\n readonly cached?: true\n}\n\n// ─── Presence ─────────────────────────────────────────────\n\n/**\n * A presence peer entry. `lastSeen` is an ISO timestamp set by core on each\n * `update()` call. Stale entries (lastSeen older than `staleMs`) are filtered\n * before delivering to the subscriber callback.\n */\nexport interface PresencePeer<P> {\n readonly userId: string\n readonly payload: P\n readonly lastSeen: string\n}\n\n// ─── CRDT ─────────────────────────────────────────────────\n\n/** Per-collection CRDT mode. */\nexport type CrdtMode = 'lww-map' | 'rga' | 'yjs'\n\n// Hoisted from with-commit/crdt/crdt.ts (C3 — #667: breaks the\n// types.ts ↔ crdt.ts cycle by making crdt.ts's re-export of these\n// three types leaf-ward only). crdt.ts re-exports them from here so\n// existing importers of that module are unaffected.\n\n/**\n * Per-field last-write-wins registers.\n * Each field carries its latest value and the ISO timestamp of the last write.\n * Merge: for each field, keep the entry with the lexicographically higher `ts`.\n */\nexport interface LwwMapState {\n readonly _crdt: 'lww-map'\n readonly fields: Record<string, { readonly v: unknown; readonly ts: string }>\n}\n\n/**\n * Simplified Replicated Growable Array.\n * Items are assigned stable NID (noy-db id) strings on first insertion.\n * Deleted items are tracked as tombstones so concurrent removals commute.\n *\n * The resolved snapshot is the ordered list of non-tombstoned `v` values.\n */\nexport interface RgaState {\n readonly _crdt: 'rga'\n readonly items: ReadonlyArray<{ readonly nid: string; readonly v: unknown }>\n readonly tombstones: readonly string[]\n}\n\n/**\n * Yjs binary state marker. `update` is base64(Y.encodeStateAsUpdate()).\n * Core stores and retrieves the blob opaquely. `@noy-db/yjs` is responsible\n * for encoding, decoding, and merging via `Y.mergeUpdates`.\n * Core falls back to last-write-wins (higher `_v`) for conflict resolution.\n */\nexport interface YjsState {\n readonly _crdt: 'yjs'\n /** base64-encoded Y.encodeStateAsUpdate() bytes. */\n readonly update: string\n}\n\nexport type CrdtState = LwwMapState | RgaState | YjsState\n\n/**\n * Seam interface. `@internal`.\n *\n * @internal\n */\nexport interface CrdtStrategy {\n buildLwwMapState(\n record: Record<string, unknown>,\n previous: LwwMapState | undefined,\n now: string,\n ): LwwMapState\n buildRgaState(\n items: readonly unknown[],\n previous: RgaState | undefined,\n idGen: () => string,\n ): RgaState\n mergeCrdtStates(local: CrdtState, remote: CrdtState): CrdtState\n resolveCrdtSnapshot(state: CrdtState): unknown\n}\n\n// ─── Blob / Attachment Store ────────────────────────\n\n/**\n * Second store shape for blob-store backends (Drive, WebDAV, Git, iCloud)\n * that operate on whole-vault bundles rather than per-record KV.\n *\n * Implement `readBundle` / `writeBundle` instead of the six-method KV\n * contract. Use `wrapBundleStore()` from `@noy-db/hub` to convert to a\n * `NoydbStore` that the rest of the API consumes transparently.\n *\n * Named `NoydbPodStore` (not `NoydbBundleAdapter`) for consistency\n * with the hub / to-* / in-* rename. Concrete implementations ship\n * in `@noy-db/to-*` packages starting in.\n */\nexport interface NoydbPodStore {\n /** Discriminant for engine auto-detection of store shape. */\n readonly kind: 'bundle'\n /** Human-readable name for diagnostics (e.g. `'drive'`, `'webdav'`). */\n readonly name?: string\n /**\n * Read the entire vault as raw bytes. Returns `null` if no bundle exists\n * yet (first open of a brand-new vault).\n */\n readBundle(vaultId: string): Promise<{ bytes: Uint8Array; version: string } | null>\n /**\n * Write the entire vault as raw bytes. `expectedVersion` is the version\n * token from the last `readBundle` (or `null` for a first write).\n * Implementations MUST reject the write if the stored version has advanced\n * past `expectedVersion` — throw `PodVersionConflictError`.\n * Returns the new version token on success.\n */\n writeBundle(\n vaultId: string,\n bytes: Uint8Array,\n expectedVersion: string | null,\n ): Promise<{ version: string }>\n /** Delete a vault bundle. Idempotent — no-op if the bundle does not exist. */\n deleteBundle(vaultId: string): Promise<void>\n /** List all vault bundles managed by this store. */\n listBundles(): Promise<Array<{ vaultId: string; version: string; size: number }>>\n}\n\n/** @deprecated Use `NoydbPodStore`. */\nexport type NoydbBundleStore = NoydbPodStore\n\n/**\n * Content-addressed blob object stored in the vault-level blob index.\n * Identified by HMAC-SHA-256(blobDEK, plaintext) — opaque to the store.\n *\n * Shared across all collections within a vault for deduplication: two\n * records that attach identical byte content reference the same `eTag`\n * and share a single set of encrypted chunks in `_blob_chunks`.\n */\nexport interface BlobObject {\n /** HMAC-SHA-256 hex of the original plaintext bytes, keyed by `_blob` DEK. */\n readonly eTag: string\n /** Original uncompressed size in bytes. */\n readonly size: number\n /** Compressed size in bytes (the payload that is actually encrypted and chunked). */\n readonly compressedSize: number\n /** Compression algorithm applied before encryption. */\n readonly compression: 'gzip' | 'none'\n /** Raw chunk size in bytes used at write time. Readers MUST use this value. */\n readonly chunkSize: number\n /** Total number of chunks written. Reader expects exactly this many. */\n readonly chunkCount: number\n /** MIME type if provided or auto-detected at upload time. */\n readonly mimeType?: string\n /** ISO timestamp of first upload. */\n readonly createdAt: string\n /** Live reference count — slots + published versions pointing to this blob. */\n readonly refCount: number\n /**\n * Base64 AES-KW-wrapped per-blob **content CEK** (wrapped under the `_blob`\n * DEK). Present on erasable-collection blobs (`perRecordKeys`): the chunks\n * are encrypted under this content CEK rather than directly under the `_blob`\n * DEK, so deleting this BlobObject at `refCount → 0` crypto-shreds the chunks\n * (they become permanently undecryptable). Absent → legacy blob, chunks\n * decrypt directly under the `_blob` DEK (read unchanged). See\n * docs/superpowers/specs/2026-06-13-per-blob-cek-design.md.\n */\n readonly _cek?: string\n /**\n * Transient migration marker. Present only while a legacy\n * blob is being migrated to a content CEK: it holds the wrapped content CEK\n * BEFORE the chunks have been re-encrypted under it. Readers **ignore**\n * `_cekPending` (they key off `_cek`), so the blob stays readable under the\n * `_blob` DEK during migration AND the content CEK survives a crash → a\n * re-run resumes and promotes `_cekPending` → `_cek`. Never set on a settled blob.\n */\n readonly _cekPending?: string\n /**\n * Hint indicating which store holds the chunk data.\n * Used by `routeStore` size-tiered routing: `'default'` for small blobs\n * stored inline (e.g. DynamoDB), `'blobs'` for large blobs in the overflow\n * store (e.g. S3). Absent when no routing is configured.\n */\n readonly storeHint?: 'default' | 'blobs'\n /**\n * Bounded ring (K=8 — an AUDIT-VISIBLE concurrency bound, not an\n * implementation detail) of the most recent op-stamp identities applied to\n * this object's `refCount`. Appended in the SAME CAS write as the\n * `refCount` change it stamps (`BlobSet`'s `casUpdateRefCountStamped`),\n * never a separate write — no crash window between the two. Oldest entry\n * is evicted once the ring exceeds K entries.\n *\n * The blob durability journal (#753, spec §7 C2/C4) uses membership here\n * as its test-and-set: a stamped mutator re-reads this ring on every CAS\n * attempt (including retries) BEFORE computing its delta — stamp already\n * present → that mutation already landed → skip re-applying it. This is\n * what makes a crash-resumed refCount decrement/increment exactly-once\n * rather than at-least-once.\n *\n * Two acceptances, by design — SHRED only (see the #746 whole-branch\n * review correction below for rehome):\n * - **Eviction beyond K, for SHRED.** More than 8 distinct in-flight\n * stamped operations racing the SAME object between reads is far\n * outside any expected co-ownership fan-out; a 9th racer whose stamp\n * gets evicted before it re-reads can only double-apply an idempotent\n * CAS delta — never silently lose one (the DECREMENT delta is bounded\n * by the marker's authoritative captured `hold.n`, applied as ONE CAS\n * per eTag — never per-row — so eviction has nothing row-scoped to\n * re-apply against). Not a data-loss risk for shred, just a documented\n * concurrency bound.\n * - **Stale stamps on a retained object.** A `retainedShared` object (one\n * reference released, others still live) keeps whatever stamp its last\n * CAS write appended even after that operation's marker is gone —\n * harmless bookkeeping, not crypto material (unlike `_cek`), that sits\n * inert until the object's next CAS write evicts or overwrites it.\n *\n * **#746 whole-branch review correction — this ring is NOT the sole\n * idempotency source for REHOME.** Rehome's destination `+1`s are\n * ROW-SCOPED (`${opId}:${slotName}` / `${opId}:${versionKey}`, one stamp\n * PER CONTRIBUTING ROW, not one per eTag). Within a SINGLE op this ring is\n * sufficient — rows are processed sequentially and each row's referencing\n * update lands before the next row's `+1`, so a row whose stamp is later\n * evicted is already seen as \"moved\" (skipped) on resume, never\n * re-incremented. The genuine over-count is **concurrent independent ops**\n * (distinct `opId`s) converging on one shared destination: ≥8 of them can\n * evict a crashed op's row-stamp before it resumes, and a naive ring-only\n * resume would then double-apply that row's `+1` — a real, silent,\n * permanent-leak over-count, not merely eviction-tolerant like shred's.\n * `BlobIntent.appliedStamps` (`blob-intent.ts`) is rehome's ring-INDEPENDENT\n * backstop: an unbounded, per-op, per-record log of confirmed row-stamps,\n * consulted BEFORE this ring on every resume (see\n * `BlobSet.applyStampedIncrement`). This ring stays the fast first-line\n * check; `appliedStamps` makes correctness independent of ring eviction\n * **except** in one intrinsic non-atomic window: the destination `+1`/ring\n * write (A) and the `appliedStamps` append (B) are separate object writes,\n * A before B (deliberately — B-first could under-count and crypto-shred a\n * still-referenced object, i.e. data loss, strictly worse than a\n * retained-too-long leak). A crash BETWEEN A and B followed by ≥8 concurrent\n * evictions before resume can still over-count. This window is intrinsic\n * (rehome, unlike shred, cannot pre-capture destinations at mint time) and\n * fail-safe-directed; it is a documented residual (see the arc changeset).\n */\n readonly lastOps?: readonly string[]\n}\n\n/**\n * Slot record — mutable metadata linking a named slot on a record\n * to a `BlobObject` via its eTag.\n *\n * Multiple slots (even across different records) may reference the same\n * `eTag` — the underlying chunks are shared. Updating metadata creates\n * a new envelope version (`_v++`) while the blob data is unchanged.\n */\nexport interface SlotRecord {\n /**\n * Reference to the `BlobObject` in `_blob_index` (chunk-based blobs).\n * Empty string (`''`) for an `external` slot, whose bytes live in the\n * `ObjectProjection` rather than `_blob_chunks` — read `external` instead.\n */\n readonly eTag: string\n /**\n * External-projection reference. Present when the blob field is declared\n * `external`: the raw bytes live in the vault's `ObjectProjection` at `key`\n * (unencrypted), not in `_blob_chunks`. This slot record (in the encrypted\n * collection) remains the catalog entry — the anchoring invariant.\n */\n readonly external?: {\n readonly key: string\n readonly contentType?: string\n readonly public?: boolean\n /** Opaque-token backlink stamped on the object (when `backlink:'opaque-token'`). */\n readonly backlink?: string\n /**\n * Secondary metadata store synced from the object / its processing pipeline\n * (e.g. video `duration`, image `width`/`height`, arbitrary metatags).\n * Populated via `BlobSet.setExternalMeta()` — typically an AWS-side callback.\n */\n readonly meta?: Record<string, unknown>\n }\n /** User-visible filename for the slot. */\n readonly filename: string\n /** Original uncompressed size in bytes (denormalized from `BlobObject`). */\n readonly size: number\n /** MIME type. Takes precedence over the MIME type stored in `BlobObject`. */\n readonly mimeType?: string\n /** ISO timestamp of the upload that set this slot. */\n readonly uploadedAt: string\n /** User ID of the uploader, if available. */\n readonly uploadedBy?: string\n /**\n * Internal rehome-journal bookkeeping (#746 spec §7 review, carried\n * finding (b)) — NEVER part of the public `list()`/`SlotInfo` contract\n * (`BlobSet.list()` filters it out explicitly). Set, in the SAME CAS\n * write that points this slot at its new (rehomed) eTag, to the OLD eTag\n * still awaiting its refCount release: `putUnderDEK`'s slot-CAS and its\n * old-eTag release are two separate writes, and a crash between them\n * would otherwise lose the only record of which object still needs\n * releasing (the slot map itself has already moved past it) — a\n * permanent stranded-refcount leak. Cleared once the release lands.\n * Only ever set under a marker-governed (stamped) rehome; absent on\n * every ordinary `put()`.\n */\n readonly pendingRelease?: string\n}\n\n/** Result of `BlobSet.list()` — slot record plus its named slot key. */\nexport interface SlotInfo extends SlotRecord {\n /** The slot name (key in the record's slot map). */\n readonly name: string\n /**\n * DEVICE-LOCAL (#808): slot is pinned for offline on THIS device. Read from\n * the `withBlobs()` pin registry at list time — never stored in (or synced\n * through) the vault store; present only when `true`. Pinned slots are\n * exempt from `vault.compact()` eviction and the cache-budget pass.\n */\n readonly pinned?: boolean\n /**\n * DEVICE-LOCAL (#808): ISO timestamp of the last local read of this slot on\n * this device — the LRU input for `vault.compact({ cacheBudget })`. Absent\n * when the slot was never read here (the budget pass falls back to\n * `uploadedAt`).\n */\n readonly lastAccessAt?: string\n /**\n * DEVICE-LOCAL (#808): byte size of the local encrypted side-cache copy of\n * an `external` slot on this device (see `BlobPinEntry.cipher`). Absent for\n * internal slots and for external slots with no local copy.\n */\n readonly cachedBytes?: number\n}\n\n/**\n * Explicitly published version snapshot — an independent reference to a\n * blob at a specific point in time.\n */\nexport interface VersionRecord {\n /** User-defined label (e.g. `'issued-2025-01'`, `'amendment-2025-02'`). */\n readonly label: string\n /** eTag of the blob snapshot at publish time — independent of the current slot. */\n readonly eTag: string\n /** ISO timestamp when the version was published. */\n readonly publishedAt: string\n /** User ID of the publisher, if available. */\n readonly publishedBy?: string\n}\n\n/** Options for `BlobSet.put()`. */\nexport interface BlobPutOptions {\n /** MIME type hint. If omitted, auto-detected from magic bytes. */\n mimeType?: string\n /**\n * Raw chunk size in bytes. Priority: this value > store.maxBlobBytes > 256 KB.\n */\n chunkSize?: number\n /**\n * Whether to gzip-compress bytes before encrypting. Default: `true`.\n * Auto-set to `false` for pre-compressed MIME types (JPEG, PNG, ZIP, etc.).\n */\n compress?: boolean\n /** User ID to record as `uploadedBy`. Defaults to the Noydb session user. */\n uploadedBy?: string\n /**\n * User-visible filename to store on the slot. Defaults to the slot name.\n * Differs from the slot name when the caller wants a display/download name\n * (e.g. slot `attachment` holding `invoice-2024.pdf`); this is the value\n * that the L1 lexical index tokenizes for blob fields.\n */\n filename?: string\n}\n\n/** Options for `BlobSet.response()` and `BlobSet.responseVersion()`. */\nexport interface BlobResponseOptions {\n /**\n * When `true`, sets `Content-Disposition: inline; filename=\"...\"` so\n * the browser renders the file in the tab. Default (`false`) sets\n * `attachment; filename=\"...\"` which triggers a download.\n */\n inline?: boolean\n /** Override the filename in the Content-Disposition header. */\n filename?: string\n}\n\n// ─── Store Capabilities ─────────────────────────────\n\nexport type StoreAuthKind =\n | 'none'\n | 'filesystem'\n | 'api-key'\n | 'iam'\n | 'oauth'\n | 'kerberos'\n | 'browser-origin'\n\nexport interface StoreAuth {\n kind: StoreAuthKind | StoreAuthKind[]\n required: boolean\n flow: 'static' | 'oauth' | 'kerberos' | 'implicit'\n}\n\n/** Vendor-neutral short-lived store credentials. `kind` is the credential-PAYLOAD\n * discriminator — orthogonal to StoreAuthKind ('iam'|'api-key'|…), which is unchanged. */\nexport type StoreCredentials =\n | { readonly kind: 'aws'\n readonly accessKeyId: string\n readonly secretAccessKey: string\n readonly sessionToken?: string\n readonly expiresAt?: string } // ISO 8601\n | { readonly kind: 'token' // postgres/turso/supabase/webdav/bearer — a LATER slice\n readonly token: string\n readonly expiresAt?: string }\n | { readonly kind: 'password' // connection-auth stores: to-postgres/to-mysql user+password; to-smb NTLM via `domain`\n readonly username: string\n readonly password: string\n readonly domain?: string // NTLM domain (to-smb); postgres/mysql omit it\n readonly expiresAt?: string } // ISO 8601 — cloud IAM auth tokens are password-shaped and expire\n\n/** Refresh hook a store calls when it has no credentials or they are near expiry. */\nexport type StoreCredentialSource = () => Promise<StoreCredentials>\n\n/**\n * The store's authoritative clock as a bounded-uncertainty interval\n * (Spanner TrueTime model). True time is provably within [earliest, latest];\n * `latest - earliest` is the clock-uncertainty bound ε. Used by deferred\n * numbering to order records by store-commit-time and to commit-wait. Never\n * the client wall clock.\n */\nexport interface StoreTime {\n readonly earliest: number\n readonly latest: number\n}\n\nexport interface StoreCapabilities {\n /**\n * true — the store's expectedVersion check and write are atomic at the\n * storage layer. Two concurrent puts with the same expectedVersion will\n * produce exactly one success and one ConflictError.\n * false — check and write are separate operations with a race window.\n */\n casAtomic: boolean\n /**\n * true — the store exposes an authoritative {@link NoydbStore.getStoreTime}\n * clock and records are ordered by store-commit-time. Required for\n * `withDeferredNumbering`. Absent/false — the store cannot back deferred\n * numbering (use CAS `sequence().next()` or per-series).\n */\n serverWriteTime?: boolean\n /**\n * Advisory geographic region this store serves (e.g. `'eu'`, `'us'`).\n * Purely declarative — no behavior change for stores that omit it. The\n * federation data-residency guard compares this against a\n * `sharding.regionOf(record)` to refuse non-compliant shard placement.\n */\n region?: string\n auth: StoreAuth\n /**\n * true — the store implements {@link NoydbStore.tx} and commits\n * every op atomically at the storage layer. The hub's\n * `db.transaction(fn)` will delegate to `tx(ops)` and surface a\n * single pass/fail outcome. false (or absent) — no native\n * multi-record atomicity; the hub falls back to per-record OCC\n * with best-effort unwind on partial failure.\n */\n txAtomic?: boolean\n /**\n * Maximum raw bytes per blob chunk record.\n * `undefined` — no limit (S3, file, IDB); blob stored as single chunk.\n * `256 * 1024` — DynamoDB (400 KB item limit minus envelope overhead).\n * `5 * 1024 * 1024` — localStorage quota safety.\n */\n maxBlobBytes?: number\n /**\n * true — the store is a tiered router (`routeStore`) with a cold route,\n * so `compact(vault, { before })` can relocate records hot → cold and\n * reads fall through to cold. `vault.archivePeriod()` requires this.\n */\n coldArchival?: boolean\n}\n\n// ─── Factory Options ───────────────────────────────────────────────────\n\nexport interface NoydbOptions {\n /** The ciphertext store. Optional — defaults to the built-in `memoryStore()` (non-persistent). */\n readonly store?: NoydbStore\n /**\n * tree-shake seam — optional blob strategy. Pass `withBlobs()`\n * from `@noy-db/hub/blobs` to enable `collection.blob(id)` storage.\n * When omitted, hub's blob machinery stays out of the bundle (ESM\n * tree-shaking) and `collection.blob(id)` throws with a pointer at\n * the subpath. `BlobsStrategy` is `@internal` — users only construct\n * it via the subpath factory.\n *\n * @internal\n */\n readonly blobsStrategy?: BlobsStrategy\n /**\n * Cold-storage archival target. `withArchive({ store })` designates a\n * second store that holds archived record envelopes. Enables\n * `vault.archive()` / `vault.restore()` / `vault.listArchived()`.\n */\n readonly archiveStrategy?: ArchiveStrategy\n /**\n * tree-shake seam — optional indexing strategy. Pass\n * `withIndexing()` from `@noy-db/hub/indexing` to enable eager-mode\n * `==/in` fast-paths, lazy-mode `.lazyQuery()`, rebuild/reconcile,\n * and auto-reconcile. When omitted, indexing code never reaches the\n * bundle; `.lazyQuery()` throws with a pointer at the subpath, and\n * eager-mode collections fall back to linear scans regardless of\n * `indexes: [...]` declarations. `IndexingStrategy` is `@internal` —\n * users only construct it via the subpath factory.\n *\n * @internal\n */\n readonly indexingStrategy?: IndexingStrategy\n /**\n * tree-shake seam — optional aggregate strategy. Pass\n * `withReduce()` from `@noy-db/hub/reduce` to enable\n * `.aggregate()` and `.groupBy()` on Query. When omitted, those\n * methods throw with a pointer at the subpath; the ~886 LOC of\n * Reduction + GroupedQuery machinery never reaches the bundle.\n * Streaming `scan().aggregate()` works independently of this\n * strategy — it doesn't use the `Reduction` class.\n *\n * @internal\n */\n readonly reduceStrategy?: ReduceStrategy\n /**\n * tree-shake seam — optional CRDT strategy. Required when\n * any collection is declared with `crdt: 'lww-map' | 'rga' | 'yjs'`;\n * otherwise the first put/sync-merge hitting the CRDT path throws.\n * When omitted, ~221 LOC of LWW-Map / RGA / merge helpers never\n * reach the bundle.\n *\n * @internal\n */\n readonly crdtStrategy?: CrdtStrategy\n /**\n * tree-shake seam — strategy for the collection-level hierarchical-tier\n * operations. Pass `withTiers()` from `@noy-db/hub/tiers` to enable\n * `putAtTier`/`getAtTier`/`listAtTier`/`elevate`/`demote` on collections\n * declared with `{ tiers: [...] }`. When omitted, all five throw\n * `TiersNotEnabledError` and the tier read/write/re-key engine never\n * reaches the bundle.\n *\n * @internal\n */\n readonly tiersStrategy?: TiersStrategy\n /**\n * tree-shake seam — optional consent-audit strategy. Pass\n * `withConsent()` from `@noy-db/hub/consent` to enable per-op audit\n * writes into `_consent_audit` when a consent scope is active.\n * When omitted, `vault.consentAudit()` returns `[]` and writes are\n * no-ops; the consent module's ~194 LOC never reaches the bundle.\n *\n * @internal\n */\n readonly consentStrategy?: ConsentStrategy\n /**\n * tree-shake seam — optional periods strategy. Pass\n * `withPeriods()` from `@noy-db/hub/periods` to enable\n * `vault.closePeriod()` / `.openPeriod()` / write-guard on closed\n * periods. When omitted, `vault.listPeriods()` returns `[]` and\n * the write-guard is a no-op; the ~363 LOC of period validation +\n * ledger appending stay out of the bundle.\n *\n * @internal\n */\n readonly periodsStrategy?: PeriodsStrategy\n /**\n * tree-shake seam — optional VaultFrame strategy. Pass\n * `withShadow()` from `@noy-db/hub/shadow` to enable\n * `vault.frame()`. Without it, calling `vault.frame()` throws.\n *\n * @internal\n */\n readonly shadowStrategy?: ShadowStrategy\n /**\n * tree-shake seam — optional multi-record transactions. Pass\n * `withTransactions()` from `@noy-db/hub/transactions` to enable\n * `db.transaction(fn)`. Without it, calling the method throws.\n *\n * @internal\n */\n readonly transactionsStrategy?: TransactionsStrategy\n /**\n * tree-shake seam — optional history + ledger + time-machine.\n * Pass `withHistory()` from `@noy-db/hub/history` to enable\n * per-record version snapshots, the hash-chained audit ledger, JSON\n * Patch deltas, `vault.ledger()`, `vault.at()`, and the\n * `collection.history()` / `getVersion()` / `revert()` / `diff()` /\n * `clearHistory()` / `pruneRecordHistory()` read APIs. When omitted,\n * snapshots/prune/clear are silent no-ops, the read APIs throw with\n * a pointer at the subpath, and ~1,880 LOC stay out of the bundle.\n *\n * @internal\n */\n readonly historyStrategy?: HistoryStrategy\n /**\n * GDPR right-to-erasure. Pass `withForget({ subjects })`\n * from `@noy-db/hub/forget` to declare which collections carry erasable\n * subject data and the record field naming the data subject. Enables\n * `vault.forget(subjectId)` crypto-shred (rewrite-to-tombstone of the live\n * record + every history version → body permanently undecryptable, single\n * `op:'forget'` ledger entry, chain still verifies). Each declared\n * collection is forced to `perRecordKeys: true`. When omitted (the\n * `NO_FORGET` default), `vault.forget()` throws\n * `ForgetStrategyNotConfiguredError` and no subject-index write hooks run.\n * Requires `historyStrategy` (the ledger) for the erasure-proof entry.\n */\n readonly forgetStrategy?: ForgetStrategy\n /**\n * tree-shake seam — optional i18n strategy. Pass `withI18n()`\n * from `@noy-db/hub/i18n` to enable `i18nText`/`dictKey` field\n * resolution on reads, `i18nText` validation on writes, and\n * `vault.dictionary(name)`. When omitted, locale resolution is the\n * identity (raw values returned), the validators throw with a\n * pointer to the subpath, and ~854 LOC of dictionary + locale\n * machinery stay out of the bundle.\n *\n * @internal\n */\n readonly i18nStrategy?: I18nStrategy\n /**\n * tree-shake seam — optional session-policy strategy. Pass\n * `withSession()` from `@noy-db/hub/session` to enable\n * `sessionPolicy` validation, `PolicyEnforcer` lifecycle (idle /\n * absolute timeouts, lockOnBackground), and global session-token\n * revocation. When omitted, setting `sessionPolicy` throws at\n * `createNoydb()` time, and ~495 LOC of policy + token machinery\n * stay out of the bundle.\n *\n * @internal\n */\n readonly sessionStrategy?: SessionStrategy\n /**\n * tree-shake seam — optional sync engine + presence strategy.\n * Pass `withSync()` from `@noy-db/hub/sync` to enable\n * `db.push()` / `pull()` / replication, `db.transaction(vault)`\n * for sync-aware transactions, and `collection.presence()`. When\n * omitted, configuring `sync` / calling these surfaces throws with\n * a pointer at the subpath, and ~856 LOC of replication + presence\n * machinery stay out of the bundle. Keyring stays core; grant/\n * revoke/magic-link/delegation tree-shake via direct imports.\n *\n * @internal\n */\n readonly syncStrategy?: SyncStrategy\n /**\n * Tree-shake seam — optional snapshot-lifecycle service. Pass\n * `withSnapshots({ store })` from `@noy-db/hub/snapshots` to enable\n * `db.snapshot()`, `db.listSnapshots()`, and `db.restoreSnapshot()`.\n * When omitted, all three methods throw with a pointer at the subpath.\n */\n readonly snapshotsStrategy?: SnapshotsStrategy\n /**\n * Tree-shake seam — optional attestation capability. Pass\n * `withAttestation()` from `@noy-db/hub/attestation` to enable\n * `vault.issueAttestation()`, `vault.getDocumentSigningPublicKey()`,\n * `vault.revokeAttestation()`, `vault.unrevokeAttestation()`,\n * `vault.getRevokedDocIds()`, and `vault.publishRevocationList()`. When\n * omitted, all six throw `AttestationNotEnabledError` and the issue/revoke/\n * signer engines are tree-shaken out.\n */\n readonly attestationStrategy?: AttestationStrategy\n /**\n * Tree-shake seam — optional classified-field capability. Pass\n * `withClassified()` from `@noy-db/hub/classified` to enable\n * `collection.reveal()`. When omitted, `reveal()` throws\n * `ClassifiedNotEnabledError` and the reveal engine is tree-shaken out.\n */\n readonly classifiedStrategy?: ClassifiedStrategy\n /**\n * Tree-shake seam — optional sealed-record (grantor-side) capability. Pass\n * `withSealedRecord()` from `@noy-db/hub/sealed-record` to enable\n * `vault.sealRecordToHost()`, `vault.revokeSealedRecord()`, and\n * `vault.rotateRecordCek()`. When omitted, all three throw\n * `SealedRecordNotEnabledError` and the record-keys grantor engine is reached\n * only via opt-in. The host-side `openSealedRecord` opener stays ungated.\n */\n readonly sealedRecordStrategy?: SealedRecordStrategy\n /**\n * Tree-shake seam — optional portability (data-sovereignty) capability. Pass\n * `withPortability()` from `@noy-db/hub/portability` to enable the\n * `vault.user.*` export/withdrawal surface (`exportMyAccessibleData`,\n * `unilateralWithdrawal`, `requestWithdrawal`, `listWithdrawalRequests`,\n * `approveWithdrawal`, `rejectWithdrawal`). When omitted, all six throw\n * `PortabilityNotEnabledError` and the export/withdraw/request engines are\n * reached only via opt-in.\n */\n readonly portabilityStrategy?: PortabilityStrategy\n /**\n * Tree-shake seam — optional atomic-sequence capability. Pass\n * `withSequence()` from `@noy-db/hub` to enable `vault.sequence(name)`\n * (`.next()` / `.peek()` / `.seedTo()`). When omitted, `vault.sequence()`\n * throws `SequenceNotEnabledError` and the CAS `SequenceStore` engine is\n * reached only via opt-in. Deferred-numbering series (`numbering:\n * [withDeferredNumbering(...)]`) are a separate capability and stay live.\n */\n readonly sequenceStrategy?: SequenceStrategy\n /**\n * Tree-shake seam — optional sovereign-custody (FR-6) capability. Pass\n * `withCustody()` from `@noy-db/hub` to enable minting / removing a\n * `custodian` (`db.grantCustodian` / `db.revokeCustodian` and the\n * `vault.custody.*` facade) plus the `vault.custody.liberate()` ceremony.\n * When omitted, those throw `CustodyNotEnabledError` and the liberate engine\n * is reached only via opt-in. The lower-level `liberateVault` free function\n * stays ungated (it has no createNoydb instance to gate against).\n */\n readonly custodyStrategy?: CustodyStrategy\n /**\n * Tree-shake seam — optional multi-user team capability (#267\n * keyring-grant → team split). Pass `withTeam()` from `@noy-db/hub/team`\n * to enable `db.grant` / `db.revoke` / `db.rotate`. When omitted, those\n * throw `TeamNotEnabledError` and the keyring grant/revoke/rotate engines\n * are reached only via opt-in — the always-on floor is single-user.\n * Single-user primitives (owner keyring, unlock, `listUsers`,\n * `updateUser`, secret rotate/recover) stay ungated, as does the\n * `createDeedOwner` free function (no createNoydb instance to gate\n * against).\n */\n readonly teamStrategy?: TeamStrategy\n /**\n * Tree-shake seam — optional credential-broker capability (#479). Pass\n * `brokerStrategy: withBroker(config)` from `@noy-db/hub/broker` to\n * enable `vault.broker()` (`.enroll()` / `.rotate()` /\n * `.credentialSource(profile?)`). When omitted, `vault.broker()` throws\n * `BrokerNotEnabledError` and the seed lifecycle + network/cache engine\n * are reached only via opt-in.\n */\n readonly brokerStrategy?: BrokerStrategy\n /**\n * Opt-in seam — the `lazy` service (#267). Pass `withLazy()` from\n * `@noy-db/hub/lazy` to explicitly enable lazy mode's bounded-LRU\n * working set for collections declared with `prefetch: false`. When\n * omitted, `prefetch: false` still works via the deprecated implicit\n * back-compat path (identical behavior, one-time deprecation warn);\n * the implicit path will be removed at 1.0.\n */\n readonly lazyStrategy?: LazyStrategy\n /**\n * Tree-shake seam — optional search / retrieval capability. Pass\n * `withSearch()` from `@noy-db/hub` to enable a collection's `search`\n * / `retrieve` / `similarTo` / `warmIndex` / `flushIndex` methods and the\n * put()-time embedding-vector compute for collections declaring `embeddings`.\n * When omitted, those throw `SearchNotEnabledError` and the search/retrieval\n * engine is reached only via opt-in. Embedding compute is paired with search\n * (a vector no gated retrieval could read would be dead weight).\n */\n readonly searchStrategy?: SearchStrategy\n /**\n * Tree-shake seam — optional cargo (partition extraction) capability\n * (FR-6/FR-7). Pass `withCargo()` from `@noy-db/hub/cargo` to enable the\n * source-side `extractPartition(vault, …)` free function. When omitted, it\n * throws `CargoNotEnabledError` and the extraction crypto is reached only via\n * opt-in. The recipient-side `adoptPartition` / `decryptExtractedPartition`\n * free functions — and `diffVault` (shared import/merge infra) — operate\n * without a gated source instance and stay ungated.\n */\n readonly cargoStrategy?: CargoStrategy\n /**\n * Optional guard strategies — collection-level write guards. Each\n * handle is the output of `withGuard()` from `@noy-db/hub/guards`.\n * Multiple guards per collection are allowed; they are dispatched\n * in registration order on `collection.put()`.\n */\n readonly guardStrategies?: ReadonlyArray<GuardStrategyAny>\n /**\n * Deferred-numbering series declared via `withDeferredNumbering(...)`.\n * `vault.sequence(series).next({ for })` then assigns gap-free serials at a\n * numbering pass (`vault.runNumberingPass(series)`) instead of via CAS.\n */\n readonly numbering?: ReadonlyArray<DeferredNumberingConfig>\n /**\n * Optional derivation strategies — source-to-output projections that\n * fire on `collection.put()`. Each handle is the output of\n * `withDerivation()` from `@noy-db/hub/derivations`. The vault\n * validates the derivation graph for cycles on `openVault`; a cyclic\n * graph throws `DerivationCycleError`.\n */\n readonly derivationStrategies?: ReadonlyArray<DerivationStrategy>\n /**\n * Optional materialized-view strategies.\n * Each handle returned by `withMaterializedView()` from\n * `@noy-db/hub/materialized-views`. The vault runs unified cycle\n * detection across the MV + derivation graphs at `openVault`; a\n * cyclic graph throws `MaterializedViewCycleError`.\n */\n readonly materializedViewStrategies?: ReadonlyArray<MaterializedViewStrategy>\n /**\n * Optional overlay strategies. Each handle returned by\n * `withOverlayedView()` from `@noy-db/hub/overlay-views`. The vault\n * validates name uniqueness + base concreteness + overlay\n * availability at `openVault`; a clash throws one of the\n * `Overlay*Error` family.\n */\n readonly overlayedViewStrategies?: ReadonlyArray<OverlayedViewStrategy>\n /** Optional remote store(s) for sync. Accepts a single store, a SyncTarget, or an array. */\n readonly sync?: NoydbStore | SyncTarget | SyncTarget[]\n /** User identifier. */\n readonly user: string\n /** Secret for key derivation. Required unless encrypt is false or `getKeyring` is provided. */\n readonly secret?: string\n /**\n * Optional callback that returns an unlocked keyring for a given vault.\n * Use this to plug in WebAuthn / OIDC / Shamir / any unlock path that\n * produces an `UnlockedKeyring` outside the secret model.\n *\n * When set, `secret` MUST NOT also be set — `createNoydb` throws if both\n * are supplied. When neither is set (and `encrypt !== false`), `createNoydb`\n * also throws.\n *\n * The callback is called lazily, on the first operation that needs the\n * keyring for a given vault. Noydb caches the returned keyring per-vault\n * for the lifetime of the instance, so the callback is invoked at most\n * once per `(instance, vault)` pair (assuming the callback resolves\n * successfully). If the callback rejects, the rejection surfaces from the\n * first vault operation that triggered the unlock; subsequent operations\n * will retry the callback.\n *\n * @example\n * ```ts\n * import { createNoydb } from '@noy-db/hub'\n * import { unlockWebAuthn } from '@noy-db/on-webauthn'\n *\n * const enrollment = await loadEnrollment()\n * const db = await createNoydb({\n * store,\n * user: 'alice',\n * getKeyring: (vault) => unlockWebAuthn(enrollment),\n * })\n * ```\n *\n * Note: this callback is responsible for both the \"open existing vault\"\n * and the \"create new vault\" cases. Unlike the secret path, there is\n * no automatic `NoAccessError` → `createOwnerKeyring` fallback, because\n * the callback owner has the UI context to decide which path to run.\n * For first-time bootstrap, use a secret or recovery code, enroll\n * WebAuthn from the unlocked keyring, then swap to `getKeyring` on\n * subsequent sessions.\n */\n readonly getKeyring?: (vault: string) => Promise<UnlockedKeyring>\n /**\n * Secret mode. Default `'standard'`.\n *\n * - `'standard'` — the legacy flow. `secret` supplies the\n * plaintext secret, the user knows it, and the policy gate\n * `rotate-secret` is enabled.\n * - `'managed'` — rubber-hose-resistant mode. Hub generates a\n * 256-bit random secret at first open and seals it under\n * the provided `sealingKey`. The user never sees or types the\n * secret, defeating the $5-wrench attack. Mutually\n * exclusive with `secret` and `getKeyring`.\n *\n * @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/session-tiers.md → Managed-secret mode\n */\n readonly secretMode?: 'standard' | 'managed'\n /**\n * Provider that seals/unseals the auto-generated managed-mode\n * secret. Required when `secretMode === 'managed'`; ignored\n * otherwise. Implementations live in per-platform packages\n * (`@noy-db/seal-macos-keychain`, `@noy-db/seal-wincred`,\n * `@noy-db/seal-libsecret`, `@noy-db/seal-aws-kms`, …).\n */\n readonly sealingKey?: SealingKeyProvider\n /** Required to use `profile: 'shamir'` recovery. Pass\n * `shamirRecoveryProvider()` from `@noy-db/on-shamir`. */\n readonly shamirRecovery?: ShamirRecoveryProvider\n /** Enable encryption. Default: true. */\n readonly encrypt?: boolean\n /**\n * Debug-only: lay plaintext records out as directly-inspectable store\n * objects (record fields inlined beside envelope metadata, `_debug: 1`) so\n * native store tooling can read them without unwrapping `_data`. Requires\n * `encrypt: false` — combining with encryption throws `DebugPlaintextError`\n * at construction. NEVER enable for production or client data.\n */\n readonly debugPlaintext?: boolean\n /**\n * Object projection for direct-serve / external blob fields (`as-*`, e.g.\n * `@noy-db/as-aws-s3`). Blob fields declared `external` route their RAW bytes\n * to this projection as a single native object (servable from S3/CDN) instead\n * of the encrypted-chunk path; the encrypted record/slot stays the catalog.\n * Sees plaintext bytes — outside the zero-knowledge guarantee.\n */\n readonly objectStore?: ObjectProjection\n /** Conflict resolution strategy. Default: 'version'. */\n readonly conflict?: ConflictStrategy\n /**\n * Sync scheduling policy. Controls when push/pull fire.\n * Default inferred from store category: per-record → `on-change`,\n * bundle → `debounce 30s`.\n */\n readonly syncPolicy?: SyncPolicy\n /**\n * Session timeout in ms. Clears keys after inactivity. Default: none.\n * @deprecated Use `sessionPolicy.idleTimeoutMs` instead. This field is\n * still honored for backwards compatibility but `sessionPolicy` takes\n * precedence when both are supplied.\n */\n readonly sessionTimeout?: number\n /**\n * Session policy controlling lifetime, re-auth requirements, and\n * background-lock behavior. When supplied, replaces the\n * legacy `sessionTimeout` field.\n */\n readonly sessionPolicy?: SessionPolicy\n /**\n * Validate secret strength against the phrase format\n * on first-time keyring creation. When\n * `true`, weak phrases throw {@link WeakSecretError} from\n * `createNoydb()` / `db.team.rotateSecret()`. Default: `false` for\n * back-compat; planned to flip to `true` in a future major release.\n */\n readonly validateSecret?: boolean\n /**\n * Vault-level policy gate document. When present, the hub\n * persists the merged policy at `_meta/policy` on first-time vault\n * creation and gates sensitive operations (`db.rotateSecret`,\n * `db.export*`, …) against it. Omitted ⇒ the engine uses\n * {@link PERSONAL_POLICY}. Use {@link STRICT_POLICY} for regulated\n * deployments.\n *\n * The on-disk document is the source of truth — the policy field\n * is only honored at vault creation; subsequent runs read from\n * `_meta/policy`. Use `db.updatePolicy()` to change it deliberately.\n *\n * Imported from `@noy-db/hub` as a type-only reference; the runtime\n * import lives in `policy/index.ts`.\n */\n readonly policy?: VaultPolicy\n /**\n * Mandatory recovery profile enrollment. Vaults with\n * `recover-secret` enabled MUST register at least one profile\n * before being production-ready, otherwise `createNoydb()` throws\n * {@link RecoveryNotEnrolledError}. Set\n * `policy.gates['recover-secret'].enabled = false` to\n * deliberately opt out of recovery (secret loss = data loss).\n *\n * The `'paper'` profile is supported end-to-end. Other\n * profiles ship the API shape and throw\n * {@link RecoveryProfileNotImplementedError} during use.\n */\n readonly recovery?: ReadonlyArray<RecoveryEnrollment>\n /**\n * When `true`, `createNoydb` rejects vaults with no recovery\n * entries persisted (per the spec's mandatory-enrollment\n * requirement). Default `false` for back-compat; planned to\n * flip to `true` in a future major release. Apps in regulated\n * environments should turn this on now.\n */\n readonly requireRecovery?: boolean\n /**\n * What to do when `openVault` finds an existing keyring in the store that\n * cannot be decrypted with the supplied credentials (`InvalidKeyError`).\n *\n * - `'error'` (default) — propagate the error. The app must prompt the user\n * to supply the correct credentials or clear both the data and auth stores.\n * - `'reset'` — delete the stale keyring and re-initialise the vault from\n * scratch using the current credentials. Use this when the data store can\n * become detached from the auth store (e.g. the user cleared the IndexedDB\n * data records but not the keyring row, or a WebAuthn credential was rotated).\n * **All previously encrypted data is unrecoverable after a reset.**\n *\n * Only applies to the secret (`secret`) path. When `getKeyring` is used,\n * the callback is responsible for handling stale-keyring detection itself.\n */\n readonly onInvalidKey?: 'error' | 'reset'\n /**\n * Enable the cover service (`https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/public-envelope.md`).\n * Pass `true` for the default schema (every standard field, 256 KB\n * icon cap, 200-char text cap), or a `CoverSchema` to narrow what\n * the owner can set. Off by default — vaults written by hubs\n * without this option carry no cover, full stop.\n */\n readonly cover?: true | CoverSchema\n /** Audit history configuration. */\n readonly history?: HistoryConfig\n /**\n * Consumer-supplied translation function for `i18nText` fields with\n * `autoTranslate: true`.\n *\n * ⚠ **`plaintextTranslator` receives unencrypted text.** Configuring\n * this hook causes plaintext to leave noy-db's zero-knowledge boundary\n * over whatever channel the consumer's implementation uses. noy-db ships\n * no built-in translator and adds no translator SDKs as dependencies.\n * The consumer chooses and owns the data policy of the external service.\n *\n * Per-field opt-in via `autoTranslate: true` on `i18nText()`. Calling\n * `put()` on a collection with `autoTranslate: true` fields while this\n * option is absent throws `TranslatorNotConfiguredError`.\n *\n * See `NOYDB_SPEC.md § Zero-Knowledge Storage` for the invariant text.\n */\n readonly plaintextTranslator?: PlaintextTranslatorFn\n /**\n * Human-readable name for the translator, recorded in the in-process\n * audit log (e.g. `'deepl-pro-with-dpa'`, `'self-hosted-llama-7b'`).\n * Defaults to `'anonymous'` when not supplied.\n */\n readonly plaintextTranslatorName?: string\n /**\n * Drain-barrier coordination transport for the schema fence.\n * When omitted, the kernel uses a {@link CoordinationProvider} backed by the\n * primary store (`StoreCoordinationProvider`), reproducing today's\n * store-polling fence behavior byte-for-byte. `@noy-db/by-tabs` /\n * `@noy-db/by-peer` inject a real-time push transport here; an external\n * orchestrator (`@klum-db/lobby`) drives it through the `Noydb` handle.\n *\n * @internal\n */\n readonly coordinationStrategy?: CoordinationProvider\n /**\n * Pre-resolved factory for the `vault.user` per-principal user-envelope\n * API. `createNoydb()` always resolves this itself (dynamically\n * importing `with-party/directory/user-envelope/api.js`) before\n * constructing `Noydb` — mirrors the {@link coordinationStrategy}\n * pre-resolve above. There is no supported way to override it; it exists\n * as an options-bag field only so `createNoydb()` can thread the\n * pre-resolved value into the constructor without a second parameter.\n *\n * @internal\n */\n readonly userApiFactory?: UserApiFactory\n /**\n * Pre-resolved factory for the `NoydbPolicy` service (vault-policy\n * read/update/bootstrap + session-policy enforcer wiring).\n * `createNoydb()` always resolves this itself (dynamically importing\n * `with-party/policy/index.js`) before constructing `Noydb` — mirrors the\n * {@link coordinationStrategy} / {@link userApiFactory} pre-resolves\n * above. There is no supported way to override it.\n *\n * @internal\n */\n readonly policyFactory?: NoydbPolicyFactory\n /**\n * Pre-resolved policy-gate engine function (`checkGate`).\n * `createNoydb()` always resolves this itself (dynamically importing\n * `with-party/policy/index.js`) before constructing `Noydb` — same\n * pre-resolve pattern as {@link policyFactory}.\n *\n * @internal\n */\n readonly policyCheckGateFn?: PolicyCheckGateFn\n /**\n * Stable id for the session that owns this instance's writers (one user's\n * writers across vaults). Tags every {@link WriterPresence} the fence\n * watcher reports. Defaults to a fresh ULID per `Noydb` instance.\n *\n * @internal\n */\n readonly sessionId?: string\n}\n\n// ─── History / Audit Trail ─────────────────────────────────────────────\n\n/** History configuration. */\nexport interface HistoryConfig {\n /** Enable history tracking. Default: true. */\n readonly enabled?: boolean\n /** Maximum history entries per record. Oldest pruned on overflow. Default: unlimited. */\n readonly maxVersions?: number\n /**\n * Participate in the vault-wide hash-chained tamper ledger. Default:\n * `true` (every write of this collection appends a ledger entry when\n * `withHistory()` is active). Set `false` to exclude this collection's\n * writes from the chain — its puts/deletes leave no ledger entry,\n * confining tamper-evidence to the collections where it carries weight.\n * Independent of `enabled`, which gates per-record snapshots. Has no\n * effect when `withHistory()` is not active (there is no ledger).\n */\n readonly ledger?: boolean\n}\n\n/** Options for querying history. */\nexport interface HistoryOptions {\n /** Start date (inclusive), ISO 8601. */\n readonly from?: string\n /** End date (inclusive), ISO 8601. */\n readonly to?: string\n /** Maximum entries to return. */\n readonly limit?: number\n}\n\n/** Options for pruning history. */\nexport interface PruneOptions {\n /** Keep only the N most recent versions. */\n readonly keepVersions?: number\n /** Delete versions older than this date, ISO 8601. */\n readonly beforeDate?: string\n}\n\n/** A decrypted history entry. */\nexport interface HistoryEntry<T> {\n readonly version: number\n readonly timestamp: string\n readonly userId: string\n readonly record: T\n}\n\n// ─── Bulk operations ──────────────────────────────────────\n\n/** Per-item options for `Collection.putMany()`. */\nexport interface PutManyItemOptions {\n /**\n * Optimistic-concurrency check: fail this item if the stored version\n * is not `expectedVersion`. Honored only in `atomic: true` mode;\n * ignored in the default best-effort loop.\n */\n readonly expectedVersion?: number\n}\n\n/**\n * Batch-level options for `Collection.putMany()` and `deleteMany()`.\n *\n * `atomic: true` switches the call from best-effort loop\n * to all-or-nothing: a pre-flight CAS check runs first, then every op\n * is executed; any mid-batch failure triggers a best-effort revert.\n * On failure in atomic mode the whole call throws — you won't get a\n * partial `PutManyResult`. On success the result mirrors the default\n * loop's shape.\n */\nexport interface PutManyOptions {\n readonly atomic?: boolean\n}\n\n/** Result of `Collection.putMany()`. */\nexport interface PutManyResult {\n /** `true` iff every entry succeeded. */\n readonly ok: boolean\n /** IDs that were successfully written. */\n readonly success: readonly string[]\n /** Entries that failed, with the error that prevented each write. */\n readonly failures: ReadonlyArray<{ readonly id: string; readonly error: Error }>\n}\n\n/** Result of `Collection.deleteMany()`. Same shape as `PutManyResult`. */\nexport interface DeleteManyResult {\n readonly ok: boolean\n readonly success: readonly string[]\n readonly failures: ReadonlyArray<{ readonly id: string; readonly error: Error }>\n}\n\n// ─── User Envelope (vault.user contract) ───────────────────────────────\n//\n// The per-principal user-envelope service's PUBLIC CONTRACT lives here in\n// the spine; the implementation (`UserApi` / `createUserApi`, storage\n// primitives) lives at `with-party/directory/user-envelope/` and is wired\n// in by `createNoydb()` via the pre-resolved `userApiFactory` option above\n// — the same dynamic-import-then-stash pattern used for the default\n// `CoordinationProvider`.\n//\n// @see docs/superpowers/specs/2026-05-05-user-envelope-design.md\n\n/**\n * Thin reader view of a user envelope. The on-disk shape is the standard\n * {@link EncryptedEnvelope}; this is what callers see after the storage\n * layer has decrypted the payload.\n *\n * Hub commits to the `keyringId` ⇔ `userId` identity and the `_v` / `_ts`\n * envelope metadata. The `data` payload is fully app-defined — hub does\n * not introspect, validate, or reserve any keys inside it.\n */\nexport interface UserEnvelope<T> {\n /** The principal id this envelope belongs to. Equals the keyring `user_id`. */\n readonly keyringId: string\n /** App-owned payload. Opaque to hub. */\n readonly data: T\n /** Optimistic-concurrency version. Increments on every write. */\n readonly _v: number\n /** ISO timestamp of the last write. */\n readonly _ts: string\n}\n\n/**\n * Recursive partial. Used for `updateMe(patch)` so callers can hand in\n * deeply-nested partial shapes and have them deep-merged onto the\n * current envelope.\n */\nexport type DeepPartial<T> = T extends object\n ? { [P in keyof T]?: DeepPartial<T[P]> }\n : T\n\n/**\n * Recursive partial with `null` allowed at every level — used by\n * `updateMe` to express deletion intent in addition to merge.\n *\n * Semantics inside `updateMe`:\n * - `undefined` (or absent key) — skip; source value preserved\n * - `null` — delete the key from the resulting envelope\n * - any other value — overwrite (deep-merge for plain objects,\n * replace for primitives / arrays)\n *\n * Matches lodash `_.merge` behavior on `null` and Firestore's\n * `FieldValue.delete()` semantics. Loosened from `DeepPartial<T>`.\n * Consumers wanting the original \"merge-only\" surface can keep\n * importing `DeepPartial` and avoid passing `null`.\n */\nexport type DeepPartialOrNull<T> = T extends object\n ? { [P in keyof T]?: DeepPartialOrNull<T[P]> | null }\n : T\n\n/** Cancel a previously-registered subscription. */\nexport type Unsubscribe = () => void\n\n/**\n * Optional factor-proof bundle threaded into gated user-envelope\n * operations. Same shape as `Noydb.checkGate(vault, gate, presented)`\n * accepts elsewhere — apps that have already presented a TOTP/email-OTP\n * for this session pass it here to satisfy tightened policies.\n */\nexport interface UserEnvelopePresented {\n readonly factors?: readonly FactorProof[]\n readonly sharedDevice?: boolean\n}\n\n/**\n * Callback used by `UserApi` to validate the active session against a\n * policy gate. Provided by the `Vault` constructor; in production this\n * delegates to `Noydb.checkGate(vault, gate, presented)`. In tests, a\n * no-op stub is fine.\n */\nexport type UserEnvelopeCheckGate = (\n gate:\n | 'edit-own-profile'\n | 'view-team-profiles'\n | 'client-unilateral-withdraw'\n | 'user-request-withdrawal'\n | 'approve-user-withdrawal',\n presented?: UserEnvelopePresented,\n) => Promise<void>\n\n/**\n * Reactive handle returned by `live()`. `current` is the most recently\n * observed value; `subscribe(cb)` fires on subsequent local writes.\n * `stop()` releases the underlying subscription.\n */\nexport interface LiveUserEnvelope<T> {\n current(): UserEnvelope<T> | null\n subscribe(cb: (env: UserEnvelope<T> | null) => void): Unsubscribe\n stop(): void\n}\n\n/**\n * The 2nd positional parameter of a {@link PortabilityStrategy} method\n * (index 1, right after the leading `vault` argument).\n */\ntype PortabilityParam1<K extends keyof PortabilityStrategy> = Parameters<PortabilityStrategy[K]>[1]\n/**\n * The 3rd positional parameter (index 2) — only present on\n * `approveWithdrawal` / `rejectWithdrawal` (requestId is index 1 there).\n */\ntype PortabilityParam2<K extends keyof PortabilityStrategy> = Parameters<PortabilityStrategy[K]>[2]\ntype PortabilityReturn<K extends keyof PortabilityStrategy> = ReturnType<PortabilityStrategy[K]>\n\n/**\n * Public `vault.user.*` API surface — the CONTRACT. The implementation\n * (`UserApi`) lives at `with-party/directory/user-envelope/api.ts` and\n * `implements` this interface; `createNoydb()` wires it in via the\n * pre-resolved {@link UserApiFactory}.\n *\n * Three families:\n * - Write-self: `me` / `updateMe` / `setMe` — always target the writer's\n * own keyringId. **Own-only write rule** is structural — no method\n * exists to write someone else's envelope.\n * - Read-anyone: `get` / `list` — read other principals' envelopes\n * (subject to `view-team-profiles` policy gate).\n * - Reactive: `subscribe` / `live` — in-process event emission on local\n * writes. Cross-instance updates land via the team/sync engine and\n * surface to subscribers when the sync diff replays through this API.\n *\n * @see docs/superpowers/specs/2026-05-05-user-envelope-design.md\n */\nexport interface VaultUserApi {\n requestWithdrawal(opts?: PortabilityParam1<'requestWithdrawal'>): PortabilityReturn<'requestWithdrawal'>\n listWithdrawalRequests(opts?: PortabilityParam1<'listWithdrawalRequests'>): PortabilityReturn<'listWithdrawalRequests'>\n approveWithdrawal(\n requestId: PortabilityParam1<'approveWithdrawal'>,\n opts?: PortabilityParam2<'approveWithdrawal'>,\n ): PortabilityReturn<'approveWithdrawal'>\n rejectWithdrawal(\n requestId: PortabilityParam1<'rejectWithdrawal'>,\n opts?: PortabilityParam2<'rejectWithdrawal'>,\n ): PortabilityReturn<'rejectWithdrawal'>\n unilateralWithdrawal(opts: PortabilityParam1<'withdrawAccessibleData'>): PortabilityReturn<'withdrawAccessibleData'>\n exportMyAccessibleData(opts?: PortabilityParam1<'exportAccessibleData'>): PortabilityReturn<'exportAccessibleData'>\n me<T = unknown>(): Promise<UserEnvelope<T> | null>\n updateMe<T extends object = Record<string, unknown>>(\n patch: DeepPartialOrNull<T>,\n presented?: UserEnvelopePresented,\n ): Promise<UserEnvelope<T>>\n setMe<T = unknown>(payload: T, presented?: UserEnvelopePresented): Promise<UserEnvelope<T>>\n getMyVisibility(): Promise<{ readonly hidden: boolean }>\n setMyVisibility(visibility: { readonly hidden: boolean }): Promise<void>\n get<T = unknown>(keyringId: string, presented?: UserEnvelopePresented): Promise<UserEnvelope<T> | null>\n list<T = unknown>(presented?: UserEnvelopePresented): Promise<UserEnvelope<T>[]>\n subscribe<T = unknown>(keyringId: string, cb: (env: UserEnvelope<T> | null) => void): Unsubscribe\n live<T = unknown>(keyringId: string): LiveUserEnvelope<T>\n}\n\n/**\n * Constructor dependencies for `UserApi` (the {@link VaultUserApi}\n * implementation). Built by `Vault`'s constructor and passed to the\n * pre-resolved {@link UserApiFactory}.\n */\nexport interface UserApiDeps {\n readonly adapter: NoydbStore\n readonly vaultName: string\n /** The writer's own keyringId. Frozen at construction time. */\n readonly writerKeyringId: string\n readonly getDek: () => Promise<EnclaveKey>\n /**\n * Policy-gate validator. When omitted, gates are skipped — useful\n * for low-level tests that exercise the storage layer directly.\n * Production paths always wire the Noydb-backed implementation.\n */\n readonly checkGate?: UserEnvelopeCheckGate\n /**\n * Noydb-backed `exportMyAccessibleData`, injected by the Vault\n * (which holds the keyring + bundle machinery). Omitted in low-level tests.\n */\n readonly exportAccessible?: (opts: PortabilityParam1<'exportAccessibleData'>) => PortabilityReturn<'exportAccessibleData'>\n /**\n * Noydb-backed `unilateralWithdrawal`, injected by the Vault.\n * Destructive — extract + dispose (delete | freeze). Omitted in low-level tests.\n */\n readonly unilateralWithdraw?: (opts: PortabilityParam1<'withdrawAccessibleData'>) => PortabilityReturn<'withdrawAccessibleData'>\n /**\n * Noydb-backed two-party withdrawal ceremony, injected by the\n * Vault. requestWithdraw = requester side; the rest = owner side.\n */\n readonly requestWithdraw?: (opts: PortabilityParam1<'requestWithdrawal'>) => PortabilityReturn<'requestWithdrawal'>\n readonly listWithdrawals?: (opts: PortabilityParam1<'listWithdrawalRequests'>) => PortabilityReturn<'listWithdrawalRequests'>\n readonly approveWithdraw?: (\n requestId: PortabilityParam1<'approveWithdrawal'>,\n opts: PortabilityParam2<'approveWithdrawal'>,\n ) => PortabilityReturn<'approveWithdrawal'>\n readonly rejectWithdraw?: (\n requestId: PortabilityParam1<'rejectWithdrawal'>,\n opts: PortabilityParam2<'rejectWithdrawal'>,\n ) => PortabilityReturn<'rejectWithdrawal'>\n}\n\n/**\n * Factory that builds the `vault.user` API implementation from its\n * dependencies. `createNoydb()` pre-resolves the real implementation\n * (`with-party/directory/user-envelope/api.js#createUserApi`) via a\n * dynamic import before constructing `Noydb`, so `Vault`'s constructor\n * can call it synchronously — the two sync `subscribe`/`live` methods on\n * `VaultUserApi` are why `vault.user` must be built synchronously.\n */\nexport type UserApiFactory = (deps: UserApiDeps) => VaultUserApi\n\n// ─── Policy gates (VaultPolicy contract) ───────────────────────────────\n//\n// Sensitive operations (rotate the secret, enroll an authenticator,\n// export plaintext, grant a user, …) are gated by a typed policy\n// object. The developer supplies a {@link VaultPolicy} at vault\n// creation; the hub merges it onto a built-in preset and persists the\n// merged document at `_meta/policy`.\n//\n// The CONTRACT (this section) lives here in the spine; the engine\n// (`checkGate`/`describeGate`), the presets (`PERSONAL_POLICY` /\n// `STRICT_POLICY`), storage (`loadVaultPolicy`/`saveVaultPolicy`), and the\n// `NoydbPolicy` facade implementation live at `with-party/policy/` and are\n// wired in by `createNoydb()` via the pre-resolved {@link NoydbPolicyFactory}\n// / {@link PolicyCheckGateFn} options above — the same\n// dynamic-import-then-stash pattern used for the default\n// `CoordinationProvider` / `UserApiFactory`.\n//\n// @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/session-tiers.md → Policy gates DSL\n\n/**\n * A single factor surface — the proof an actor presents at gate time.\n *\n * | Kind | Source | Off-device? |\n * |---|---|---|\n * | `totp` | RFC 6238 authenticator app (Google Auth, 1Password) | yes |\n * | `email-otp` | one-time code mailed to the user | yes |\n * | `recovery` | printable Base32 code (`@noy-db/on-recovery`) | yes (paper) |\n * | `shamir` | k-of-n threshold share (`@noy-db/on-shamir`) | yes |\n * | `webauthn-roaming` | hardware key (YubiKey, SoloKey, Titan) | yes (key portable) |\n * | `webauthn-platform` | platform passkey (Touch ID, Face ID, Hello) | no (device-bound) |\n * | `password` | tier-2 password (`@noy-db/on-password`) | no |\n * | `pin` | tier-3 quick-resume PIN (`@noy-db/on-pin`) | no |\n *\n * Off-device kinds (TOTP, email-OTP, recovery, shamir, roaming WebAuthn)\n * are the strongest factor proofs because they require something\n * separate from the device the user just unlocked. Platform / password /\n * PIN are useful for \"fresh proof of *this* user\" but don't bind across\n * devices — policies can require ANY of them or insist on a count of 2\n * to force a mix.\n *\n * `webauthn-platform`, `password`, `pin` — for consumers with no\n * off-device infrastructure (no TOTP, no email-OTP, paper recovery not\n * enrolled) who want to require \"any second factor I have wired\"\n * without losing the freshness guarantee.\n */\nexport type FactorKind =\n | 'totp'\n | 'email-otp'\n | 'recovery'\n | 'shamir'\n | 'webauthn-roaming'\n | 'webauthn-platform'\n | 'password'\n | 'pin'\n\n/**\n * One factor requirement entry. The default is \"any one of the listed\n * factors, fresh within the last 5 minutes\". Bumping `count` requires N\n * distinct fresh proofs; bumping `freshnessMs` widens the acceptance\n * window.\n */\nexport interface FactorRequirement {\n readonly anyOf: ReadonlyArray<FactorKind>\n /** Number of distinct factors required. Default 1. */\n readonly count?: number\n /** How recent each proof must be. Default 5 minutes. */\n readonly freshnessMs?: number\n}\n\n/** Soft signals layered on top of the gate verdict — never block on their own. */\nexport interface WarningRules {\n /** Behavior on shared-device tier-1 ops. `'block'` raises a `PolicyDeniedError`. */\n readonly sharedDevice?: 'warn' | 'block'\n /** Behavior on weak tier-2 (e.g. password-only) for sensitive ops. */\n readonly weakAuthenticator?: 'warn' | 'block'\n}\n\n/**\n * Policy applied to one named gate. `enabled: false` disables the\n * action entirely (useful in managed-secret mode where rotation is\n * impossible by construction).\n */\nexport interface GatePolicy {\n /** Minimum tier the active session must hold. */\n readonly minTier: 1 | 2 | 3\n /** Extra freshness-bound proofs required at gate time. */\n readonly factors?: ReadonlyArray<FactorRequirement>\n readonly warn?: WarningRules\n readonly enabled?: boolean\n}\n\n/**\n * Built-in gate names. App-defined gates live in the `app:*` namespace\n * and use the same engine; the engine treats unknown names with no\n * configured policy as \"no gate\" (no-op).\n */\nexport type BuiltInGateName =\n | 'rotate-secret'\n | 'recover-secret'\n | 'enroll-authenticator'\n | 'remove-authenticator'\n /**\n * Authorize a deliberate paper-recovery-code regeneration —\n * `db.rotateRecovery`. Symmetric to `rotate-secret` for\n * the case where the user remembers their secret but wants a\n * fresh sheet (lost the printout, suspect compromise of the off-site\n * copy). PERSONAL allows tier-1; STRICT requires an off-device\n * factor so a stolen unlocked laptop cannot silently mint a new\n * sheet for an attacker.\n */\n | 'rotate-recovery'\n /**\n * Authorize a meta-only mutation on an existing authenticator slot —\n * `db.updateAuthenticator`. The slot's wrap material, id, and\n * method are immutable through this gate; only the `meta` blob\n * (nicknames, method-specific labels) can change. Anti-slot-swap\n * guard is preserved structurally regardless of this gate's\n * settings.\n */\n | 'update-authenticator'\n | 'rotate-unlock'\n | 'enroll-user'\n | 'revoke-user'\n | 'export-bundle'\n | 'export-plaintext'\n | 'view-user-auth'\n /** Authorize a write to one's own user envelope. */\n | 'edit-own-profile'\n /** Authorize reading other principals' user envelopes. */\n | 'view-team-profiles'\n /**\n * Authorize an atomic peer-recovery — `db.recoverUser`.\n * Distinct from `revoke-user` because peer-recovery is intentional\n * re-issuance of someone's keyring under a temp secret, NOT\n * removal. Allows owner→owner natively (matches the threat model:\n * a co-owner explicitly recovering another co-owner). Ships with a\n * factor-proof default in `STRICT_POLICY` so the issuer must\n * affirmatively prove identity at the moment of recovery.\n */\n | 'peer-recover-user'\n /**\n * Authorize a post-grant identity mutation — `db.updateUser`.\n * Covers `role`, `displayName`, `permissions` changes on an existing\n * keyring. Pure plaintext-header rewrite — no DEKs touched, no KEK\n * required. The role-elevation guard inside the implementation\n * mirrors `db.grant`'s hierarchy (admin cannot promote to owner)\n * regardless of this gate's settings.\n */\n | 'update-user'\n /**\n * Authorize a non-owner's self-service **destructive** withdrawal —\n * `vault.user.unilateralWithdrawal`. The actor exports their\n * own re-keyed copy and then removes (delete-closure) or freezes the\n * source records. Because it both egresses data AND destroys the\n * firm's live copy, it MUST fail closed: undefined in a policy = denied.\n * Hosts opt in explicitly (and typically pin `minTier`/factor proofs).\n */\n | 'client-unilateral-withdraw'\n /**\n * Authorize FILING a two-party withdrawal request —\n * `vault.user.requestWithdrawal`. Non-destructive (writes a\n * pending request only); enabled by default so a read-only client can ask.\n */\n | 'user-request-withdrawal'\n /**\n * Authorize DECIDING a two-party withdrawal request (approve/reject) —\n * `vault.user.approveWithdrawal` / `rejectWithdrawal`. The approve\n * path is destructive (extract-and-dispose under firm authority), so it\n * defaults to a tier-2 floor; owner/admin role is enforced structurally.\n */\n | 'approve-user-withdrawal'\n /**\n * Authorize minting a **custodian** — `db.grantCustodian` (FR-6). The\n * custodian is the de-facto operational authority on a sealed-owner (Deed)\n * vault, so granting one is an ownership-level act: this gate MUST fail\n * closed (undefined in a policy = denied) and owner-only role is enforced\n * structurally. Hosts opt in explicitly, typically pinning factor proofs.\n */\n | 'grant-custodian'\n /**\n * Authorize the audited **Liberate** ceremony — `vault.custody.liberate`\n * (FR-6). The custodian (holding the live DEKs) claims ownership of a\n * sealed-owner vault under a recorded legal basis, minting a NEW owner\n * keyring. Destructive-of-the-old-ownership and irreversible, so it MUST\n * fail closed (undefined = denied); the caller-is-custodian check is\n * enforced structurally in the ceremony.\n */\n | 'liberate-vault'\n\n/** Either a built-in gate name or an `app:*` custom gate. */\nexport type GateName = BuiltInGateName | `app:${string}`\n\n/**\n * Top-level policy object. Persisted at `_meta/policy` once at vault\n * creation. The `secret` block configures the strength rules\n * applied at every secret ingress; `gates` configures\n * the action-level requirements.\n */\nexport interface VaultPolicy {\n readonly secret?: SecretPolicy\n readonly gates: Partial<Record<GateName, GatePolicy>>\n}\n\n/** Concrete proof an actor presents to {@link checkGate}. */\nexport interface FactorProof {\n readonly kind: FactorKind\n /** ISO-8601 timestamp the proof was minted at. Compared against `freshnessMs`. */\n readonly mintedAt?: string\n /** Method-specific payload. The engine treats it as opaque — verification is delegated. */\n readonly payload?: unknown\n}\n\n/**\n * Bundle of factor proofs + session-context flags passed to a gated\n * Noydb method. Used as the optional last parameter of every method\n * that runs through `checkGate`: `db.grant`, `db.revoke`, `db.updateUser`,\n * `db.enrollAuthenticator`, `db.removeAuthenticator`, `db.updateAuthenticator`,\n * `db.enrollWebAuthn`, `db.rotateSecret`, `db.recoverSecret`,\n * `db.recoverUser`, `db.enrollUnlock`, `db.describeUserAuth`,\n * `db.describeAllUsersAuth`.\n *\n * Previously this type was inlined at every call site as\n * `{ factors?: ReadonlyArray<FactorProof>; sharedDevice?: boolean }`\n * and parameter names alternated between `factors` and `presented`.\n * Now exported so consumers can name their helpers and so the param\n * name converges to `factors` everywhere.\n */\nexport interface FactorProofBundle {\n readonly factors?: ReadonlyArray<FactorProof>\n readonly sharedDevice?: boolean\n}\n\n/** Active session tier — what the engine compares against `gate.minTier`. */\nexport type ActiveTier = 1 | 2 | 3\n\n/**\n * Caller-supplied context for the policy engine's `checkGate`/`describeGate`.\n * Structural mirror of `with-party/policy/engine.ts`'s `CheckGateContext` —\n * duplicated here (rather than imported) because the kernel spine may not\n * statically import a with-* service; see {@link PolicyCheckGateFn}.\n */\nexport interface PolicyCheckGateContext {\n /** Tier the active session currently holds. */\n readonly activeTier: ActiveTier\n /** Proofs the actor is presenting for this gate. */\n readonly factors?: ReadonlyArray<FactorProof>\n /**\n * If the host knows the actor is on a shared device, set this to\n * `true` so the engine can apply `warn.sharedDevice` rules. Defaults\n * to `false`.\n */\n readonly sharedDevice?: boolean\n /**\n * Override `now()` for tests. Defaults to `Date.now()`.\n * @internal\n */\n readonly now?: number\n}\n\n/**\n * Structural type of the policy engine's `checkGate` function. The real\n * implementation lives at `with-party/policy/engine.ts#checkGate`;\n * `createNoydb()` pre-resolves it via a dynamic import (mirrors\n * {@link UserApiFactory}) so `Noydb.checkGate` can call it without the\n * spine statically importing the service.\n */\nexport type PolicyCheckGateFn = (\n policy: VaultPolicy,\n gate: GateName,\n context: PolicyCheckGateContext,\n) => Promise<void>\n\n/**\n * Public `NoydbPolicy` surface — the CONTRACT. The implementation\n * (`NoydbPolicy` class) lives at `with-party/policy/noydb-facade.ts`;\n * `createNoydb()` wires it in via the pre-resolved {@link NoydbPolicyFactory}.\n */\nexport interface NoydbPolicyApi {\n /**\n * Touch the policy enforcer for a vault (records activity, resets\n * idle timer). Also touches the legacy session timer. No-op if no enforcer.\n */\n touchPolicy(vault?: string): void\n /**\n * Check that a policy-guarded operation is permitted.\n * Throws `SessionPolicyError` if re-auth is required.\n */\n checkPolicyOperation(vault: string, op: ReAuthOperation): void\n /**\n * Read the active policy for a vault. Loads from `_meta/policy` on\n * first call; subsequent calls hit the in-memory cache. Throws\n * `ValidationError` if the vault has not been opened.\n */\n getPolicy(vault: string): Promise<VaultPolicy>\n /**\n * Replace the policy document at `_meta/policy` and update the\n * in-memory cache. Gated by the `enroll-user` policy (a policy\n * change is fundamentally a privilege-management action).\n */\n updatePolicy(vault: string, override: Partial<VaultPolicy>): Promise<VaultPolicy>\n /** Read or persist the vault policy at `_meta/policy` on first open. */\n bootstrapPolicy(vault: string, opts?: { skipManagedCheck?: boolean }): Promise<void>\n}\n\n/**\n * Constructor dependencies for `NoydbPolicy` (the {@link NoydbPolicyApi}\n * implementation). Everything the policy/session-policy methods touch on\n * the owning `Noydb` instance's `this.*`.\n *\n * The `policyEnforcers` map is typed structurally (rather than importing\n * `PolicyEnforcer` from `with-party/session/session-policy.ts`) so this\n * spine-resident interface never needs a with-* import; the real\n * `PolicyEnforcer` class satisfies this shape.\n */\nexport interface NoydbPolicyDeps {\n /** In-memory vault-policy cache (Noydb-resident; read/written by reference). */\n readonly policyCache: Map<string, VaultPolicy>\n /** Per-vault session-policy enforcers (Noydb-resident; read/written by reference). */\n readonly policyEnforcers: Map<string, { touch(): void; destroy(): void; checkOperation(op: ReAuthOperation): void }>\n /** The ciphertext store. */\n readonly store: NoydbStore\n /** Whether records are encrypted (`options.encrypt !== false`). */\n readonly encrypted: boolean\n /** The configured session policy, or undefined. */\n readonly sessionPolicy: SessionPolicy | undefined\n /** The developer-supplied default policy, or undefined. */\n readonly policyOption: VaultPolicy | undefined\n /** Whether the owning instance has been closed. */\n isClosed(): boolean\n /** Reset the kernel-resident idle/session timer. */\n resetSessionTimer(): void\n /** Managed-recovery enrolment check (kernel-resident; called on bootstrap). */\n assertRecoveryEnrolled(\n vault: string,\n policy: VaultPolicy,\n opts?: { skipManagedCheck?: boolean },\n ): Promise<void>\n /** Evict the keyring + vault caches when a session is revoked. */\n onSessionRevoke(vault: string): void\n}\n\n/**\n * Factory that builds the `NoydbPolicy` service implementation from its\n * dependencies. `createNoydb()` pre-resolves the real implementation\n * (`with-party/policy/noydb-facade.js#createNoydbPolicy`) via a dynamic\n * import before constructing `Noydb`, so the constructor can call it\n * synchronously — mirrors {@link UserApiFactory}.\n */\nexport type NoydbPolicyFactory = (deps: NoydbPolicyDeps) => NoydbPolicyApi\n\n/**\n * Named field-set declarations for a collection's type-level shape (#839).\n *\n * Replaces the positional `Collection<T, S, Q, M>` tail at every surface a\n * consumer touches. The positional form was unreadable and unsafe: `Q` and `M`\n * are both `keyof T & string`, so swapping them type-checked silently, and\n * reaching `M` meant writing placeholders —\n * `collection<Sale, never, never, 'amount' | 'tax'>`.\n *\n * ```ts\n * vault.collection<Sale, { money: 'amount' | 'tax' }>('sales')\n * vault.collection<Invoice, { sensitive: 'ssn'; indexed: 'clientId' }>('invoices')\n * ```\n *\n * Every member is optional; omitting one keeps that axis permissive, which is\n * what the `[X] extends [never]` guards in {@link QueryField} /\n * {@link IndexFieldName} encode. Those guards are NOT removable — they are the\n * difference between \"no indexes declared, so `where()` accepts any field\" and\n * \"indexes declared, so `where()` is restricted to them\" — so they are\n * re-expressed against this shape rather than dropped.\n */\nexport interface CollectionShape<T> {\n /** Fields sealed at rest; reads return {@link Sealed} handles. */\n readonly sensitive?: keyof T & string\n /** Fields carrying a declared secondary index; narrows `where()` / `orderBy()`. */\n readonly indexed?: keyof T & string\n /** Fields carrying a money descriptor. */\n readonly money?: keyof T & string\n}\n\n/** The `sensitive` field set of a {@link CollectionShape}, or `never` if unset. */\nexport type SensitiveOf<T, O> = O extends { sensitive: infer S } ? (S & keyof T & string) : never\n/** The `indexed` field set of a {@link CollectionShape}, or `never` if unset. */\nexport type IndexedOf<T, O> = O extends { indexed: infer Q } ? (Q & keyof T & string) : never\n/** The `money` field set of a {@link CollectionShape}, or `never` if unset. */\nexport type MoneyOf<T, O> = O extends { money: infer M } ? (M & keyof T & string) : never\n"],"mappings":";AAuEO,IAAM,uBAAuB;AAG7B,IAAM,wBAAwB;AAG9B,IAAM,uBAAuB;AAG7B,IAAM,qBAAqB;AAiV3B,IAAM,eAAN,MAA2C;AAAA,EACvC,SAAS;AAAA,EACT;AAAA,EAET,YAAY,QAA0B;AACpC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,SAAqB;AACnB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA;AAAA,EAGA,SAAiB;AACf,WAAO;AAAA,EACT;AACF;AAsVO,SAAS,YACd,SACmC;AACnC,SAAO;AACT;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SealedRecordNotEnabledError
3
- } from "./chunk-W2OG6JOL.js";
3
+ } from "./chunk-QZWTRENR.js";
4
4
 
5
5
  // src/with-audit/sealed-record/strategy.ts
6
6
  var NO_SEALED_RECORD = {
@@ -18,4 +18,4 @@ var NO_SEALED_RECORD = {
18
18
  export {
19
19
  NO_SEALED_RECORD
20
20
  };
21
- //# sourceMappingURL=chunk-RDXW3OBQ.js.map
21
+ //# sourceMappingURL=chunk-QD46HPZG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-audit/sealed-record/strategy.ts"],"sourcesContent":["/**\n * Sealed-record (grantor-side) capability strategy — the three on-demand vault\n * methods the record-scoped CEK sealing surface routes through. The active\n * engine ({@link withSealedRecord}) dynamically imports the `record-keys`\n * grantor cores (keeping them reachable only via opt-in); {@link NO_SEALED_RECORD}\n * throws. The vault always assembles the per-call {@link SealingContext} and\n * delegates here, so an un-opted-in caller hits `NO_SEALED_RECORD`'s throw.\n *\n * Note: the recipient-host opener (`openSealedRecord`) is a pure decrypt\n * function exported from this subpath and is NOT gated — it runs on a remote\n * host with no `createNoydb` instance (the same way the attestation verifier\n * stays ungated). Only the vault-side grantor operations are the capability.\n * @internal\n */\nimport type { SealingContext } from '../../kernel/enclave/index.js'\nimport type { RecipientSealer } from '../../with-party/team/managed-secret.js'\nimport { SealedRecordNotEnabledError } from '../../kernel/errors.js'\n\nexport interface SealedRecordStrategy {\n sealRecordToHost(\n ctx: SealingContext,\n collection: string,\n id: string,\n hostSealer: RecipientSealer,\n opts: { expiresAt: string },\n ): Promise<{ pid: string; envelopeKey: string }>\n revokeSealedRecord(\n ctx: SealingContext,\n collection: string,\n id: string,\n pid: string,\n opts?: { hard?: boolean },\n ): Promise<void>\n rotateRecordCek(ctx: SealingContext, collection: string, id: string): Promise<void>\n}\n\n/**\n * No-op stub — the floor default. Every grantor method throws\n * {@link SealedRecordNotEnabledError}; opt in with\n * `sealedRecordStrategy: withSealedRecord()` in createNoydb. @internal\n */\nexport const NO_SEALED_RECORD: SealedRecordStrategy = {\n async sealRecordToHost() { throw new SealedRecordNotEnabledError() },\n async revokeSealedRecord() { throw new SealedRecordNotEnabledError() },\n async rotateRecordCek() { throw new SealedRecordNotEnabledError() },\n}\n"],"mappings":";;;;;AAyCO,IAAM,mBAAyC;AAAA,EACpD,MAAM,mBAAmB;AAAE,UAAM,IAAI,4BAA4B;AAAA,EAAE;AAAA,EACnE,MAAM,qBAAqB;AAAE,UAAM,IAAI,4BAA4B;AAAA,EAAE;AAAA,EACrE,MAAM,kBAAkB;AAAE,UAAM,IAAI,4BAA4B;AAAA,EAAE;AACpE;","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  chainAnchor,
5
5
  loadPeriods,
6
6
  validatePeriodName
7
- } from "./chunk-ZEQWCBCM.js";
7
+ } from "./chunk-UDHBXYGG.js";
8
8
 
9
9
  // src/with-audit/periods/active.ts
10
10
  function withPeriods() {
@@ -20,4 +20,4 @@ function withPeriods() {
20
20
  export {
21
21
  withPeriods
22
22
  };
23
- //# sourceMappingURL=chunk-N55W7KUL.js.map
23
+ //# sourceMappingURL=chunk-QDIH2744.js.map
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-LP7BEXCT.js";
6
6
  import {
7
7
  bufferToBase64
8
- } from "./chunk-HOONYZY2.js";
8
+ } from "./chunk-AKKRXT23.js";
9
9
 
10
10
  // src/kernel/enclave/classify/bidx.ts
11
11
  var subtle = globalThis.crypto.subtle;
@@ -66,4 +66,4 @@ export {
66
66
  mintBidxTag,
67
67
  computeBidxTarget
68
68
  };
69
- //# sourceMappingURL=chunk-6HY2X62D.js.map
69
+ //# sourceMappingURL=chunk-QJNCB5SK.js.map
@@ -1,21 +1,21 @@
1
1
  import {
2
2
  liveRecordIsElevated
3
- } from "./chunk-DRWBEDYO.js";
3
+ } from "./chunk-JYJFGD2H.js";
4
4
  import {
5
5
  isRewrappedUnder,
6
6
  openEnvelopeJson,
7
7
  rewrapEnvelope
8
- } from "./chunk-DVUZMT2W.js";
8
+ } from "./chunk-G2JUIDFA.js";
9
9
  import {
10
10
  isTombstone,
11
11
  isTombstoneShape
12
- } from "./chunk-WEYZTLXN.js";
12
+ } from "./chunk-W3L64Y3J.js";
13
13
  import {
14
14
  NOYDB_FORMAT_VERSION
15
- } from "./chunk-LSASLXGC.js";
15
+ } from "./chunk-QBAC3GXA.js";
16
16
  import {
17
17
  ReadOnlyAtInstantError
18
- } from "./chunk-W2OG6JOL.js";
18
+ } from "./chunk-QZWTRENR.js";
19
19
 
20
20
  // src/with-commit/history/history.ts
21
21
  var HISTORY_COLLECTION = "_history";
@@ -330,4 +330,4 @@ export {
330
330
  VaultInstant,
331
331
  CollectionInstant
332
332
  };
333
- //# sourceMappingURL=chunk-4BRCIYFG.js.map
333
+ //# sourceMappingURL=chunk-QX76XVQH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-commit/history/history.ts","../src/with-commit/history/time-machine.ts"],"sourcesContent":["import type { NoydbStore, EncryptedEnvelope, HistoryOptions, PruneOptions } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport { isTombstone, isTombstoneShape, rewrapEnvelope, isRewrappedUnder, type EnclaveKey } from '../../kernel/enclave/index.js'\n\n/**\n * History storage convention:\n * Collection: `_history`\n * ID format: `{collection}:{recordId}:{paddedVersion}`\n * Version is zero-padded to 10 digits for lexicographic sorting.\n */\n\nconst HISTORY_COLLECTION = '_history'\nconst VERSION_PAD = 10\n\nfunction historyId(collection: string, recordId: string, version: number): string {\n return `${collection}:${recordId}:${String(version).padStart(VERSION_PAD, '0')}`\n}\n\n// Unused today, kept for future history-id parsing utilities.\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction parseHistoryId(id: string): { collection: string; recordId: string; version: number } | null {\n const lastColon = id.lastIndexOf(':')\n if (lastColon < 0) return null\n const versionStr = id.slice(lastColon + 1)\n const rest = id.slice(0, lastColon)\n const firstColon = rest.indexOf(':')\n if (firstColon < 0) return null\n return {\n collection: rest.slice(0, firstColon),\n recordId: rest.slice(firstColon + 1),\n version: parseInt(versionStr, 10),\n }\n}\n\nfunction matchesPrefix(id: string, collection: string, recordId?: string): boolean {\n if (recordId) {\n return id.startsWith(`${collection}:${recordId}:`)\n }\n return id.startsWith(`${collection}:`)\n}\n\n/** Save a history entry (a complete encrypted envelope snapshot). */\nexport async function saveHistory(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string,\n envelope: EncryptedEnvelope,\n): Promise<void> {\n const id = historyId(collection, recordId, envelope._v)\n await adapter.put(vault, HISTORY_COLLECTION, id, envelope)\n}\n\n/** Get history entries for a record, sorted newest-first. */\nexport async function getHistory(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string,\n options?: HistoryOptions,\n): Promise<EncryptedEnvelope[]> {\n const allIds = await adapter.list(vault, HISTORY_COLLECTION)\n const matchingIds = allIds\n .filter(id => matchesPrefix(id, collection, recordId))\n .sort()\n .reverse() // newest first\n\n const entries: EncryptedEnvelope[] = []\n\n for (const id of matchingIds) {\n const envelope = await adapter.get(vault, HISTORY_COLLECTION, id)\n if (!envelope) continue\n\n // Apply time filters\n if (options?.from && envelope._ts < options.from) continue\n if (options?.to && envelope._ts > options.to) continue\n\n entries.push(envelope)\n\n if (options?.limit && entries.length >= options.limit) break\n }\n\n return entries\n}\n\n/** Get a specific version's envelope from history. */\nexport async function getVersionEnvelope(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string,\n version: number,\n): Promise<EncryptedEnvelope | null> {\n const id = historyId(collection, recordId, version)\n return adapter.get(vault, HISTORY_COLLECTION, id)\n}\n\n/** Prune history entries. Returns the number of entries deleted. */\nexport async function pruneHistory(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string | undefined,\n options: PruneOptions,\n): Promise<number> {\n const allIds = await adapter.list(vault, HISTORY_COLLECTION)\n const matchingIds = allIds\n .filter(id => recordId ? matchesPrefix(id, collection, recordId) : matchesPrefix(id, collection))\n .sort()\n\n let toDelete: string[] = []\n\n if (options.keepVersions !== undefined) {\n // Keep only the N most recent, delete the rest\n const keep = options.keepVersions\n if (matchingIds.length > keep) {\n toDelete = matchingIds.slice(0, matchingIds.length - keep)\n }\n }\n\n if (options.beforeDate) {\n // Delete entries older than the specified date\n for (const id of matchingIds) {\n if (toDelete.includes(id)) continue\n const envelope = await adapter.get(vault, HISTORY_COLLECTION, id)\n if (envelope && envelope._ts < options.beforeDate) {\n toDelete.push(id)\n }\n }\n }\n\n // Deduplicate\n const uniqueDeletes = [...new Set(toDelete)]\n\n for (const id of uniqueDeletes) {\n await adapter.delete(vault, HISTORY_COLLECTION, id)\n }\n\n return uniqueDeletes.length\n}\n\n/** Clear all history for a vault, optionally scoped to a collection or record. */\nexport async function clearHistory(\n adapter: NoydbStore,\n vault: string,\n collection?: string,\n recordId?: string,\n): Promise<number> {\n const allIds = await adapter.list(vault, HISTORY_COLLECTION)\n let toDelete: string[]\n\n if (collection && recordId) {\n toDelete = allIds.filter(id => matchesPrefix(id, collection, recordId))\n } else if (collection) {\n toDelete = allIds.filter(id => matchesPrefix(id, collection))\n } else {\n toDelete = allIds\n }\n\n for (const id of toDelete) {\n await adapter.delete(vault, HISTORY_COLLECTION, id)\n }\n\n return toDelete.length\n}\n\n/**\n * Crypto-shred every `_history` version of a record. Each non-tombstone\n * history envelope is OVERWRITTEN in place with a tombstone\n * `{ _noydb, _v, _ts: now, _by: actor, _iv: '', _data: '' }` — dropping\n * `_iv`/`_data`/`_cek`/`_det`, so the prior ciphertext (and the wrapped CEK\n * that could decrypt it) is gone everywhere this store reaches. The version\n * counter (`_v`) is preserved so the audit trail still shows \"N versions\n * existed and were erased.\"\n *\n * Overwrite — NOT delete — so the history key itself survives as proof the\n * version existed. Already-tombstoned versions (re-run / idempotent forget)\n * are left untouched and not counted.\n *\n * Returns the number of history versions newly tombstoned.\n */\nexport async function tombstoneHistory(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string,\n actor: string,\n encrypted: boolean,\n): Promise<number> {\n const allIds = await adapter.list(vault, HISTORY_COLLECTION)\n const matchingIds = allIds.filter(id => matchesPrefix(id, collection, recordId))\n\n const now = new Date().toISOString()\n let count = 0\n for (const id of matchingIds) {\n const env = await adapter.get(vault, HISTORY_COLLECTION, id)\n if (!env) continue\n // Already a tombstone (no body and no wrapped CEK)? Skip — idempotent.\n if (isTombstone(env, encrypted)) continue\n const tombstone: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: env._v,\n _ts: now,\n _iv: '',\n _data: '',\n ...(actor ? { _by: actor } : {}),\n }\n await adapter.put(vault, HISTORY_COLLECTION, id, tombstone)\n count++\n }\n return count\n}\n\n/**\n * Re-key every `_history` snapshot of a record from `fromDek` to `toDek`.\n * Mirrors what `rewrapBodyToDek` already does for a record's LIVE body on a\n * tier move (elevate/demote/putAtTier) — each `_history` envelope also\n * carries its own `_cek`, wrapped under the collection's tier-0 DEK at write\n * time (`record-codec.ts`), so a tier move that rewraps only the live\n * envelope leaves prior versions decryptable at rest under the tier the\n * record left. This is defense-in-depth *beneath* the read-gate\n * (`history()`/`getVersion()` already return empty for an elevated record;\n * this protects the ciphertext even if that gate is bypassed).\n *\n * Rewraps content in place — unlike `tombstoneHistory`, it does NOT blank\n * `_iv`/`_data`/`_cek` — so a subsequent `demote()` restores tier-0\n * readability. Tombstone-shaped entries (a forgotten/shredded version —\n * blanked `_data`, no `_cek`) are skipped: there is no key material left to\n * rewrap.\n *\n * **Legacy fallback.** A snapshot written before this fix stays wrapped\n * under the tier-0 DEK even after its live record has since moved tiers, so\n * a rewrap attempted with a tier-N `fromDek` fails to unwrap/decrypt. When\n * the caller supplies `tier0Dek`, a failed rewrap is retried once with\n * `tier0Dek` as `fromDek` (the only other key a pre-fix snapshot can be\n * wrapped under — history is written only by tier-0 `put()`). The output is\n * always wrapped under `toDek` regardless of which `fromDek` succeeded. A\n * rewrap that fails under BOTH keys re-throws — that is real corruption, not\n * a tier mismatch, and must not be swallowed.\n *\n * **Crash-atomicity / idempotency (#712 whole-branch-fix-3).** This loop has\n * no transaction around it: a crash after some entries have been rewritten\n * under `toDek` but before the loop finishes leaves the record's history\n * split across two keys. A retry of the SAME call must not re-fail on the\n * entries that already made it — so each entry is probed with\n * `isRewrappedUnder(env, toDek)` FIRST; a match means it's already at the\n * target key and is skipped (put nothing). This makes same-target retries\n * and demote-after-crash fully self-healing. It does NOT close every crash\n * window: a crash that lands SOME entries under `toDek` while the record's\n * NEXT move target differs from this call's `toDek` (an intermediate-tier\n * crash — e.g. elevate 0→1 crashes mid-loop, then the record is moved 1→2)\n * still finds those entries unreadable under either `fromDek` or the\n * tier-0 fallback, since `toDek` is a third key the next call never probes\n * for. That residual window is an accepted, fail-closed limitation (see\n * `.changeset/history-at-rest.md` and the design doc) — availability is\n * lost, never confidentiality.\n */\nexport async function rewrapHistory(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string,\n fromDek: EnclaveKey,\n toDek: EnclaveKey,\n tier0Dek?: EnclaveKey,\n): Promise<void> {\n const allIds = await adapter.list(vault, HISTORY_COLLECTION)\n const matchingIds = allIds.filter(id => matchesPrefix(id, collection, recordId))\n\n for (const id of matchingIds) {\n const env = await adapter.get(vault, HISTORY_COLLECTION, id)\n if (!env) continue\n // Already a tombstone (forgotten/shredded version)? Nothing to rewrap.\n if (isTombstoneShape(env)) continue\n // #712/whole-branch-fix-3: toDek-first idempotency skip — already at the\n // target key (a same-target retry, or demote-after-crash landing back on\n // a key it already reached)? Nothing to do; put nothing.\n if (await isRewrappedUnder(env, toDek)) continue\n\n let next: EncryptedEnvelope\n try {\n next = await rewrapEnvelope(env, fromDek, toDek)\n } catch (err) {\n if (!tier0Dek) throw err\n // Legacy fallback: retry once under the tier-0 DEK. A failure here is\n // real corruption, not a tier mismatch — let it propagate.\n next = await rewrapEnvelope(env, tier0Dek, toDek)\n }\n\n await adapter.put(vault, HISTORY_COLLECTION, id, next)\n }\n}\n","/**\n * Time-machine queries — point-in-time reads reconstructed from the\n * existing history + ledger infrastructure.\n *\n * ## Usage\n *\n * ```ts\n * const vault = await db.openVault('acme', { secret })\n * const q1End = vault.at('2026-03-31T23:59:59Z')\n * const invoice = await q1End.collection<Invoice>('invoices').get('inv-001')\n * // → the record as it stood at the close of Q1 2026\n * ```\n *\n * ## How it works\n *\n * Every write path already fans out into two persistence lanes:\n *\n * 1. `saveHistory(...)` persists a **full encrypted envelope snapshot**\n * per version under the `_history` collection (one envelope per\n * version, keyed by `{collection}:{id}:{paddedVersion}`). Each\n * envelope carries its own `_ts` (the write timestamp).\n * 2. `ledger.append(...)` appends a hash-chained audit entry that\n * records the `op` (put / delete), `version`, and `ts`.\n *\n * Reconstruction at a target timestamp T is therefore:\n *\n * - Find the newest history envelope for `(collection, id)` whose\n * `_ts ≤ T` — that's the state the record was in at T.\n * - Check the ledger for any `op: 'delete'` entry for the same\n * `(collection, id)` with `entry.ts` in `(latestEnvelope._ts, T]` —\n * if present, the record was deleted before T, so return `null`.\n * - Decrypt the surviving envelope with the current collection DEK\n * (DEKs are per-collection but stable across versions — the same\n * key encrypts v1 and v15 of a record).\n *\n * No delta replay. The existing `history.ts` module already stores\n * complete snapshots; we just pick the right one.\n *\n * ## Read-only contract\n *\n * Every write method on `CollectionInstant` throws\n * {@link ReadOnlyAtInstantError}. A historical view is a *read*\n * surface — mutating the past would require either a branch/shadow\n * mechanism (tracked under shadow vaults) or a rewrite of\n * history, which breaks the ledger's tamper-evidence guarantee.\n *\n * @module\n */\nimport type { EncryptedEnvelope, NoydbStore } from '../../kernel/types.js'\nimport type { LedgerStore } from './ledger/store.js'\nimport { getHistory } from './history.js'\nimport { openEnvelopeJson, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { ReadOnlyAtInstantError } from '../../kernel/errors.js'\nimport { liveRecordIsElevated } from '../../kernel/tier-visibility.js'\n\n/**\n * Narrow view of a {@link Vault}'s internals that\n * {@link VaultInstant} needs. Passed in by `Vault.at()` rather than\n * constructed here so all crypto + adapter access stays inside the\n * Vault class.\n *\n * Not exported from the public barrel — consumers should get a\n * `VaultInstant` via `vault.at(ts)`, never by constructing one\n * directly.\n */\nexport interface VaultEngine {\n readonly adapter: NoydbStore\n /** Vault name (the compartment). */\n readonly name: string\n /**\n * `true` when the vault was opened with a secret (the normal\n * case). `false` in plaintext-mode vaults (`encrypt: false`) — in\n * that case `envelope._data` is raw JSON and we skip the DEK lookup.\n */\n readonly encrypted: boolean\n /**\n * Resolves the DEK used to decrypt a given collection's envelopes.\n * Not called when `encrypted` is false.\n */\n getDEK(collection: string): Promise<EnclaveKey>\n /**\n * Lazily-initialised ledger. We consult it to detect deletes that\n * happened between the latest history snapshot and the target\n * timestamp. `null` when history is disabled for this vault — in\n * that case time-machine reads fall back to history-only\n * reconstruction (which may miss deletes).\n */\n getLedger(): LedgerStore | null\n}\n\n/**\n * A vault at a fixed instant. Produced by `vault.at(timestamp)`.\n * Carries no session state of its own — every read is a fresh\n * lookup through the vault's adapter.\n *\n * Cheap to construct; safe to throw away. Create one per query.\n */\nexport class VaultInstant {\n constructor(\n private readonly engine: VaultEngine,\n /** Fully-resolved target timestamp (ISO-8601 UTC). */\n public readonly timestamp: string,\n ) {}\n\n /** Get a point-in-time view of a collection. */\n collection<T = unknown>(name: string): CollectionInstant<T> {\n return new CollectionInstant<T>(this.engine, this.timestamp, name)\n }\n}\n\n/**\n * A read-only collection view anchored to a past instant.\n *\n * Every write method throws {@link ReadOnlyAtInstantError} — see the\n * module docstring for why. The read surface is intentionally smaller\n * than the live {@link Collection}: `get` and `list` cover the\n * \"what did the books look like on date X\" use case without pulling\n * in the full query DSL / joins / aggregates at this stage. Follow-up\n * work tracked under.\n */\nexport class CollectionInstant<T = unknown> {\n constructor(\n private readonly engine: VaultEngine,\n private readonly targetTs: string,\n public readonly name: string,\n ) {}\n\n /**\n * Return the record as it existed at the target timestamp, or\n * `null` if the record had not been created yet or had already been\n * deleted by then.\n *\n * Gated on the LIVE record's current tier — #730, mirroring the #712\n * read-gate `history()`/`getVersion()` already apply: an elevated record\n * is invisible through the whole time-machine surface, not just a\n * decrypt failure. See {@link resolveVisibleEnvelope}.\n *\n * Decrypts through {@link openEnvelopeJson}, the `_cek`-aware envelope\n * body opener — a snapshot from a `perRecordKeys` collection is encrypted\n * under its own per-record CEK (wrapped in `_cek`), not the collection\n * DEK directly.\n */\n async get(id: string): Promise<T | null> {\n const envelope = await this.resolveVisibleEnvelope(id)\n if (!envelope) return null\n const plaintext = this.engine.encrypted\n ? await openEnvelopeJson(envelope, await this.engine.getDEK(this.name))\n : envelope._data\n return JSON.parse(plaintext) as T\n }\n\n /**\n * IDs of records that existed (had at least one `put` and were not\n * subsequently deleted) at the target timestamp.\n *\n * Implemented as a linear scan over history + ledger. Performance\n * is bounded by total history size (not live-vault size), so the\n * memory-first vault-scale cap (1K–50K records × average history\n * depth) still applies.\n */\n async list(): Promise<string[]> {\n const historyIds = await collectHistoryIds(this.engine.adapter, this.engine.name, this.name)\n const liveIds = await this.engine.adapter.list(this.engine.name, this.name)\n const candidateIds = new Set<string>([...historyIds, ...liveIds])\n const alive: string[] = []\n for (const id of candidateIds) {\n const env = await this.resolveVisibleEnvelope(id)\n if (env) alive.push(id)\n }\n return alive.sort()\n }\n\n // ── write guards ───────────────────────────────────────────────────\n\n async put(_id: string, _record: T): Promise<never> {\n throw new ReadOnlyAtInstantError('put', this.targetTs)\n }\n async delete(_id: string): Promise<never> {\n throw new ReadOnlyAtInstantError('delete', this.targetTs)\n }\n async update(_id: string, _patch: Partial<T>): Promise<never> {\n throw new ReadOnlyAtInstantError('update', this.targetTs)\n }\n\n // ── internals ─────────────────────────────────────────────────────\n\n /**\n * {@link resolveEnvelope}, additionally gated on the record's LIVE\n * (current, not historical) tier — #730. Mirrors the #712 read-gate\n * `history()`/`getVersion()` apply: history snapshots keep their\n * tier-0-wrapped CEKs and carry no `_tier` of their own, so an elevated\n * record's prior versions would otherwise stay tier-0-decryptable here.\n * `null` for a resolved envelope carrying its own `_tier > 0` (a\n * tier-aware snapshot reached some other way) or whose LIVE record is\n * currently elevated — both `get()` and `list()` route through this so\n * the invisibility law holds on the whole time-machine read surface, not\n * just a decrypt failure.\n */\n private async resolveVisibleEnvelope(id: string): Promise<EncryptedEnvelope | null> {\n const envelope = await this.resolveEnvelope(id)\n if (!envelope || (envelope._tier ?? 0) > 0) return null\n if (await liveRecordIsElevated(this.engine.adapter, this.engine.name, this.name, id)) return null\n return envelope\n }\n\n /**\n * Return the envelope that represents the record's state at\n * `targetTs`, accounting for deletes. `null` if the record didn't\n * exist at that instant.\n *\n * ## Why we use the ledger as the authoritative timeline\n *\n * The per-version history snapshots saved by `saveHistory()` do\n * carry a `_ts` field, but that timestamp is the moment the\n * snapshot was *captured* (i.e. the instant right before the\n * subsequent overwrite), not the original write time. The ledger,\n * by contrast, records `ts` at the moment of each `put` / `delete`\n * — it's the only source that tracks the real timeline. So:\n *\n * 1. Walk the ledger; find the latest entry for `(collection, id)`\n * with `ts ≤ targetTs`.\n * 2. If that entry is a `delete`, the record was gone at the\n * target instant — return null.\n * 3. Otherwise it's a `put` with a specific `version`. Load the\n * envelope for that version from history, falling back to the\n * live collection for the most recent version.\n *\n * ## Fallback when the ledger is disabled\n *\n * If the vault has history disabled, `getLedger()` returns null and\n * we fall back to comparing envelope `_ts` fields. This is\n * approximate and gets the *last write* right but may confuse the\n * intermediate versions; adopters needing accurate time-machine\n * reads should leave history enabled.\n */\n private async resolveEnvelope(id: string): Promise<EncryptedEnvelope | null> {\n const ledger = this.engine.getLedger()\n if (ledger) {\n return this.resolveViaLedger(id, ledger)\n }\n return this.resolveViaEnvelopeTs(id)\n }\n\n private async resolveViaLedger(id: string, ledger: LedgerStore): Promise<EncryptedEnvelope | null> {\n const entries = await ledger.entries()\n // Entries are already ordered by index which is the mutation order.\n let latest: { op: 'put' | 'delete'; version: number } | null = null\n for (const e of entries) {\n if (e.collection !== this.name || e.id !== id) continue\n if (e.ts > this.targetTs) break // entries are time-ordered by index\n // `amendment` + `lifecycle` entries are audit-only summaries — they\n // carry no (collection, id) tuple of their own and would never match\n // the filter above. The narrow here is a type guard, not a runtime\n // skip.\n // `forget` is a subject-erasure summary with empty (collection, id) —\n // never matches the filter above; the narrow is a type guard.\n if (e.op === 'amendment' || e.op === 'lifecycle' || e.op === 'forget') continue\n // `migration` is a record rewrite (cutover) — resolve it like a put.\n latest = { op: e.op === 'migration' ? 'put' : e.op, version: e.version }\n }\n if (!latest) return null\n if (latest.op === 'delete') return null\n return this.loadVersion(id, latest.version)\n }\n\n private async resolveViaEnvelopeTs(id: string): Promise<EncryptedEnvelope | null> {\n const history = await getHistory(\n this.engine.adapter, this.engine.name, this.name, id,\n )\n const live = await this.engine.adapter.get(this.engine.name, this.name, id)\n const byVersion = new Map<number, EncryptedEnvelope>()\n for (const e of history) byVersion.set(e._v, e)\n if (live) byVersion.set(live._v, live)\n const sorted = [...byVersion.values()].sort((a, b) =>\n a._ts < b._ts ? 1 : a._ts > b._ts ? -1 : 0,\n )\n return sorted.find((e) => e._ts <= this.targetTs) ?? null\n }\n\n /**\n * Fetch the envelope for a specific version. The live record (most\n * recent put) lives in the main collection; prior versions live in\n * `_history`. We check live first because the common case after a\n * delete is that we're trying to load the last-live version from\n * history, and skipping live for the current-version case avoids a\n * redundant lookup.\n */\n private async loadVersion(id: string, version: number): Promise<EncryptedEnvelope | null> {\n const live = await this.engine.adapter.get(this.engine.name, this.name, id)\n if (live && live._v === version) return live\n\n // Direct lookup by (collection, id, version) — avoids scanning all history.\n const historyId = `${this.name}:${id}:${String(version).padStart(10, '0')}`\n return await this.engine.adapter.get(this.engine.name, '_history', historyId)\n }\n}\n\n/**\n * Scan the `_history` collection once and collect every distinct\n * `recordId` for the given collection. History keys follow the\n * shape `<collection>:<recordId>:<paddedVersion>`; we split on the\n * last two colons (delimiter-safe because `paddedVersion` is\n * exactly 10 digits).\n */\nasync function collectHistoryIds(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n): Promise<string[]> {\n const all = await adapter.list(vault, '_history')\n const prefix = `${collection}:`\n const seen = new Set<string>()\n for (const key of all) {\n if (!key.startsWith(prefix)) continue\n const lastColon = key.lastIndexOf(':')\n if (lastColon <= prefix.length) continue\n const middle = key.slice(prefix.length, lastColon)\n seen.add(middle)\n }\n return [...seen]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAWA,IAAM,qBAAqB;AAC3B,IAAM,cAAc;AAEpB,SAAS,UAAU,YAAoB,UAAkB,SAAyB;AAChF,SAAO,GAAG,UAAU,IAAI,QAAQ,IAAI,OAAO,OAAO,EAAE,SAAS,aAAa,GAAG,CAAC;AAChF;AAkBA,SAAS,cAAc,IAAY,YAAoB,UAA4B;AACjF,MAAI,UAAU;AACZ,WAAO,GAAG,WAAW,GAAG,UAAU,IAAI,QAAQ,GAAG;AAAA,EACnD;AACA,SAAO,GAAG,WAAW,GAAG,UAAU,GAAG;AACvC;AAGA,eAAsB,YACpB,SACA,OACA,YACA,UACA,UACe;AACf,QAAM,KAAK,UAAU,YAAY,UAAU,SAAS,EAAE;AACtD,QAAM,QAAQ,IAAI,OAAO,oBAAoB,IAAI,QAAQ;AAC3D;AAGA,eAAsB,WACpB,SACA,OACA,YACA,UACA,SAC8B;AAC9B,QAAM,SAAS,MAAM,QAAQ,KAAK,OAAO,kBAAkB;AAC3D,QAAM,cAAc,OACjB,OAAO,QAAM,cAAc,IAAI,YAAY,QAAQ,CAAC,EACpD,KAAK,EACL,QAAQ;AAEX,QAAM,UAA+B,CAAC;AAEtC,aAAW,MAAM,aAAa;AAC5B,UAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,oBAAoB,EAAE;AAChE,QAAI,CAAC,SAAU;AAGf,QAAI,SAAS,QAAQ,SAAS,MAAM,QAAQ,KAAM;AAClD,QAAI,SAAS,MAAM,SAAS,MAAM,QAAQ,GAAI;AAE9C,YAAQ,KAAK,QAAQ;AAErB,QAAI,SAAS,SAAS,QAAQ,UAAU,QAAQ,MAAO;AAAA,EACzD;AAEA,SAAO;AACT;AAGA,eAAsB,mBACpB,SACA,OACA,YACA,UACA,SACmC;AACnC,QAAM,KAAK,UAAU,YAAY,UAAU,OAAO;AAClD,SAAO,QAAQ,IAAI,OAAO,oBAAoB,EAAE;AAClD;AAGA,eAAsB,aACpB,SACA,OACA,YACA,UACA,SACiB;AACjB,QAAM,SAAS,MAAM,QAAQ,KAAK,OAAO,kBAAkB;AAC3D,QAAM,cAAc,OACjB,OAAO,QAAM,WAAW,cAAc,IAAI,YAAY,QAAQ,IAAI,cAAc,IAAI,UAAU,CAAC,EAC/F,KAAK;AAER,MAAI,WAAqB,CAAC;AAE1B,MAAI,QAAQ,iBAAiB,QAAW;AAEtC,UAAM,OAAO,QAAQ;AACrB,QAAI,YAAY,SAAS,MAAM;AAC7B,iBAAW,YAAY,MAAM,GAAG,YAAY,SAAS,IAAI;AAAA,IAC3D;AAAA,EACF;AAEA,MAAI,QAAQ,YAAY;AAEtB,eAAW,MAAM,aAAa;AAC5B,UAAI,SAAS,SAAS,EAAE,EAAG;AAC3B,YAAM,WAAW,MAAM,QAAQ,IAAI,OAAO,oBAAoB,EAAE;AAChE,UAAI,YAAY,SAAS,MAAM,QAAQ,YAAY;AACjD,iBAAS,KAAK,EAAE;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,gBAAgB,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC;AAE3C,aAAW,MAAM,eAAe;AAC9B,UAAM,QAAQ,OAAO,OAAO,oBAAoB,EAAE;AAAA,EACpD;AAEA,SAAO,cAAc;AACvB;AAGA,eAAsB,aACpB,SACA,OACA,YACA,UACiB;AACjB,QAAM,SAAS,MAAM,QAAQ,KAAK,OAAO,kBAAkB;AAC3D,MAAI;AAEJ,MAAI,cAAc,UAAU;AAC1B,eAAW,OAAO,OAAO,QAAM,cAAc,IAAI,YAAY,QAAQ,CAAC;AAAA,EACxE,WAAW,YAAY;AACrB,eAAW,OAAO,OAAO,QAAM,cAAc,IAAI,UAAU,CAAC;AAAA,EAC9D,OAAO;AACL,eAAW;AAAA,EACb;AAEA,aAAW,MAAM,UAAU;AACzB,UAAM,QAAQ,OAAO,OAAO,oBAAoB,EAAE;AAAA,EACpD;AAEA,SAAO,SAAS;AAClB;AAiBA,eAAsB,iBACpB,SACA,OACA,YACA,UACA,OACA,WACiB;AACjB,QAAM,SAAS,MAAM,QAAQ,KAAK,OAAO,kBAAkB;AAC3D,QAAM,cAAc,OAAO,OAAO,QAAM,cAAc,IAAI,YAAY,QAAQ,CAAC;AAE/E,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,MAAI,QAAQ;AACZ,aAAW,MAAM,aAAa;AAC5B,UAAM,MAAM,MAAM,QAAQ,IAAI,OAAO,oBAAoB,EAAE;AAC3D,QAAI,CAAC,IAAK;AAEV,QAAI,YAAY,KAAK,SAAS,EAAG;AACjC,UAAM,YAA+B;AAAA,MACnC,QAAQ;AAAA,MACR,IAAI,IAAI;AAAA,MACR,KAAK;AAAA,MACL,KAAK;AAAA,MACL,OAAO;AAAA,MACP,GAAI,QAAQ,EAAE,KAAK,MAAM,IAAI,CAAC;AAAA,IAChC;AACA,UAAM,QAAQ,IAAI,OAAO,oBAAoB,IAAI,SAAS;AAC1D;AAAA,EACF;AACA,SAAO;AACT;AA8CA,eAAsB,cACpB,SACA,OACA,YACA,UACA,SACA,OACA,UACe;AACf,QAAM,SAAS,MAAM,QAAQ,KAAK,OAAO,kBAAkB;AAC3D,QAAM,cAAc,OAAO,OAAO,QAAM,cAAc,IAAI,YAAY,QAAQ,CAAC;AAE/E,aAAW,MAAM,aAAa;AAC5B,UAAM,MAAM,MAAM,QAAQ,IAAI,OAAO,oBAAoB,EAAE;AAC3D,QAAI,CAAC,IAAK;AAEV,QAAI,iBAAiB,GAAG,EAAG;AAI3B,QAAI,MAAM,iBAAiB,KAAK,KAAK,EAAG;AAExC,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,eAAe,KAAK,SAAS,KAAK;AAAA,IACjD,SAAS,KAAK;AACZ,UAAI,CAAC,SAAU,OAAM;AAGrB,aAAO,MAAM,eAAe,KAAK,UAAU,KAAK;AAAA,IAClD;AAEA,UAAM,QAAQ,IAAI,OAAO,oBAAoB,IAAI,IAAI;AAAA,EACvD;AACF;;;AClMO,IAAM,eAAN,MAAmB;AAAA,EACxB,YACmB,QAED,WAChB;AAHiB;AAED;AAAA,EACf;AAAA,EAHgB;AAAA,EAED;AAAA;AAAA,EAIlB,WAAwB,MAAoC;AAC1D,WAAO,IAAI,kBAAqB,KAAK,QAAQ,KAAK,WAAW,IAAI;AAAA,EACnE;AACF;AAYO,IAAM,oBAAN,MAAqC;AAAA,EAC1C,YACmB,QACA,UACD,MAChB;AAHiB;AACA;AACD;AAAA,EACf;AAAA,EAHgB;AAAA,EACA;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBlB,MAAM,IAAI,IAA+B;AACvC,UAAM,WAAW,MAAM,KAAK,uBAAuB,EAAE;AACrD,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,YAAY,KAAK,OAAO,YAC1B,MAAM,iBAAiB,UAAU,MAAM,KAAK,OAAO,OAAO,KAAK,IAAI,CAAC,IACpE,SAAS;AACb,WAAO,KAAK,MAAM,SAAS;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,OAA0B;AAC9B,UAAM,aAAa,MAAM,kBAAkB,KAAK,OAAO,SAAS,KAAK,OAAO,MAAM,KAAK,IAAI;AAC3F,UAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,KAAK,KAAK,OAAO,MAAM,KAAK,IAAI;AAC1E,UAAM,eAAe,oBAAI,IAAY,CAAC,GAAG,YAAY,GAAG,OAAO,CAAC;AAChE,UAAM,QAAkB,CAAC;AACzB,eAAW,MAAM,cAAc;AAC7B,YAAM,MAAM,MAAM,KAAK,uBAAuB,EAAE;AAChD,UAAI,IAAK,OAAM,KAAK,EAAE;AAAA,IACxB;AACA,WAAO,MAAM,KAAK;AAAA,EACpB;AAAA;AAAA,EAIA,MAAM,IAAI,KAAa,SAA4B;AACjD,UAAM,IAAI,uBAAuB,OAAO,KAAK,QAAQ;AAAA,EACvD;AAAA,EACA,MAAM,OAAO,KAA6B;AACxC,UAAM,IAAI,uBAAuB,UAAU,KAAK,QAAQ;AAAA,EAC1D;AAAA,EACA,MAAM,OAAO,KAAa,QAAoC;AAC5D,UAAM,IAAI,uBAAuB,UAAU,KAAK,QAAQ;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAc,uBAAuB,IAA+C;AAClF,UAAM,WAAW,MAAM,KAAK,gBAAgB,EAAE;AAC9C,QAAI,CAAC,aAAa,SAAS,SAAS,KAAK,EAAG,QAAO;AACnD,QAAI,MAAM,qBAAqB,KAAK,OAAO,SAAS,KAAK,OAAO,MAAM,KAAK,MAAM,EAAE,EAAG,QAAO;AAC7F,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgCA,MAAc,gBAAgB,IAA+C;AAC3E,UAAM,SAAS,KAAK,OAAO,UAAU;AACrC,QAAI,QAAQ;AACV,aAAO,KAAK,iBAAiB,IAAI,MAAM;AAAA,IACzC;AACA,WAAO,KAAK,qBAAqB,EAAE;AAAA,EACrC;AAAA,EAEA,MAAc,iBAAiB,IAAY,QAAwD;AACjG,UAAM,UAAU,MAAM,OAAO,QAAQ;AAErC,QAAI,SAA2D;AAC/D,eAAW,KAAK,SAAS;AACvB,UAAI,EAAE,eAAe,KAAK,QAAQ,EAAE,OAAO,GAAI;AAC/C,UAAI,EAAE,KAAK,KAAK,SAAU;AAO1B,UAAI,EAAE,OAAO,eAAe,EAAE,OAAO,eAAe,EAAE,OAAO,SAAU;AAEvE,eAAS,EAAE,IAAI,EAAE,OAAO,cAAc,QAAQ,EAAE,IAAI,SAAS,EAAE,QAAQ;AAAA,IACzE;AACA,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI,OAAO,OAAO,SAAU,QAAO;AACnC,WAAO,KAAK,YAAY,IAAI,OAAO,OAAO;AAAA,EAC5C;AAAA,EAEA,MAAc,qBAAqB,IAA+C;AAChF,UAAM,UAAU,MAAM;AAAA,MACpB,KAAK,OAAO;AAAA,MAAS,KAAK,OAAO;AAAA,MAAM,KAAK;AAAA,MAAM;AAAA,IACpD;AACA,UAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,IAAI,KAAK,OAAO,MAAM,KAAK,MAAM,EAAE;AAC1E,UAAM,YAAY,oBAAI,IAA+B;AACrD,eAAW,KAAK,QAAS,WAAU,IAAI,EAAE,IAAI,CAAC;AAC9C,QAAI,KAAM,WAAU,IAAI,KAAK,IAAI,IAAI;AACrC,UAAM,SAAS,CAAC,GAAG,UAAU,OAAO,CAAC,EAAE;AAAA,MAAK,CAAC,GAAG,MAC9C,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,MAAM,KAAK;AAAA,IAC3C;AACA,WAAO,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,QAAQ,KAAK;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,YAAY,IAAY,SAAoD;AACxF,UAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,IAAI,KAAK,OAAO,MAAM,KAAK,MAAM,EAAE;AAC1E,QAAI,QAAQ,KAAK,OAAO,QAAS,QAAO;AAGxC,UAAMA,aAAY,GAAG,KAAK,IAAI,IAAI,EAAE,IAAI,OAAO,OAAO,EAAE,SAAS,IAAI,GAAG,CAAC;AACzE,WAAO,MAAM,KAAK,OAAO,QAAQ,IAAI,KAAK,OAAO,MAAM,YAAYA,UAAS;AAAA,EAC9E;AACF;AASA,eAAe,kBACb,SACA,OACA,YACmB;AACnB,QAAM,MAAM,MAAM,QAAQ,KAAK,OAAO,UAAU;AAChD,QAAM,SAAS,GAAG,UAAU;AAC5B,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,OAAO,KAAK;AACrB,QAAI,CAAC,IAAI,WAAW,MAAM,EAAG;AAC7B,UAAM,YAAY,IAAI,YAAY,GAAG;AACrC,QAAI,aAAa,OAAO,OAAQ;AAChC,UAAM,SAAS,IAAI,MAAM,OAAO,QAAQ,SAAS;AACjD,SAAK,IAAI,MAAM;AAAA,EACjB;AACA,SAAO,CAAC,GAAG,IAAI;AACjB;","names":["historyId"]}